GWT backend for libGDX enabling Java game development for web browsers through JavaScript compilation
—
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.
public interface ResizableWidget {
void resize(int width, int height);
}public class ResizableWidgetCollection implements ResizeHandler, Iterable<ResizableWidget> {
// Widget management
public void add(ResizableWidget widget);
public boolean remove(ResizableWidget widget);
public void clear();
public int size();
// Resize handling
public void onResize(ResizeEvent event);
// Iteration support
public Iterator<ResizableWidget> iterator();
// Manual resize trigger
public void resize(int width, int height);
}public class ProgressBar extends Widget implements ResizableWidget {
// Progress control
public void setValue(float value); // 0.0 to 1.0
public float getValue();
// Appearance
public void setVisible(boolean visible);
public boolean isVisible();
public void setAnimated(boolean animated);
public boolean isAnimated();
// Styling
public void setProgressColor(String color);
public void setBackgroundColor(String color);
public void setBorderColor(String color);
public void setHeight(int height);
// Text display
public void setShowText(boolean showText);
public boolean isShowText();
public void setTextFormat(String format); // e.g., "{0}%" for percentage
// ResizableWidget implementation
public void resize(int width, int height);
// Widget positioning
public void setPosition(int x, int y);
public void setSize(int width, int height);
// Constructors
public ProgressBar();
public ProgressBar(int width, int height);
}public class TextInputDialogBox extends DialogBox {
// Dialog display
public void show(String title, String text, TextInputListener listener);
public void show(String title, String text, String hint, TextInputListener listener);
public void hide();
// Dialog configuration
public void setModal(boolean modal);
public boolean isModal();
public void setAnimationEnabled(boolean enabled);
public boolean isAnimationEnabled();
// Input validation
public void setValidator(InputValidator validator);
public InputValidator getValidator();
public void setMaxLength(int maxLength);
public int getMaxLength();
// Styling
public void setDialogStyle(String styleName);
public void setButtonText(String okText, String cancelText);
// Event handling
public interface TextInputListener {
void input(String text);
void canceled();
}
public interface InputValidator {
boolean isValid(String input);
String getErrorMessage();
}
// Constructors
public TextInputDialogBox();
public TextInputDialogBox(boolean autoHide);
}public class PlaceholderTextBox extends TextBox {
// Placeholder functionality
public void setPlaceholder(String placeholder);
public String getPlaceholder();
// Enhanced text input
public void selectAll();
public void setSelectionRange(int start, int length);
public String getSelectedText();
// Input filtering
public void setInputFilter(InputFilter filter);
public InputFilter getInputFilter();
// Event handling
public void addValueChangeHandler(ValueChangeHandler<String> handler);
public void addKeyUpHandler(KeyUpHandler handler);
public void addFocusHandler(FocusHandler handler);
public void addBlurHandler(BlurHandler handler);
// Styling enhancements
public void setErrorStyle(boolean error);
public boolean hasErrorStyle();
public interface InputFilter {
boolean accept(char character);
String filter(String input);
}
// Constructors
public PlaceholderTextBox();
public PlaceholderTextBox(String placeholder);
}public class LoadingScreen implements Screen {
private ProgressBar progressBar;
private ResizableWidgetCollection widgets;
private Stage stage;
private Label statusLabel;
public LoadingScreen() {
stage = new Stage();
widgets = new ResizableWidgetCollection();
// Create progress bar
progressBar = new ProgressBar(400, 30);
progressBar.setProgressColor("#00ff00");
progressBar.setBackgroundColor("#333333");
progressBar.setBorderColor("#666666");
progressBar.setShowText(true);
progressBar.setTextFormat("{0}%");
progressBar.setAnimated(true);
// Add to widget collection for automatic resizing
widgets.add(progressBar);
// Create status label
statusLabel = new Label("Loading...", new Label.LabelStyle());
// Position widgets
layoutWidgets();
// Handle window resize
Gdx.graphics.setResizeCallback(new Graphics.ResizeCallback() {
@Override
public void onResize(int width, int height) {
widgets.resize(width, height);
layoutWidgets();
}
});
}
@Override
public void show() {
Gdx.input.setInputProcessor(stage);
}
@Override
public void render(float delta) {
// Update progress (example: based on asset loading)
updateProgress();
// Clear screen
Gdx.gl.glClearColor(0.1f, 0.1f, 0.1f, 1);
Gdx.gl.glClear(GL20.GL_COLOR_BUFFER_BIT);
// Render UI
stage.act(delta);
stage.draw();
}
private void updateProgress() {
// Example: Update based on preloader state
if (MyGame.getPreloader() != null) {
Preloader.PreloaderState state = MyGame.getPreloader().update();
float progress = state.getProgress();
progressBar.setValue(progress);
// Update status text
if (progress < 0.3f) {
statusLabel.setText("Loading graphics...");
} else if (progress < 0.6f) {
statusLabel.setText("Loading audio...");
} else if (progress < 0.9f) {
statusLabel.setText("Loading data...");
} else if (progress < 1.0f) {
statusLabel.setText("Finalizing...");
} else {
statusLabel.setText("Complete!");
}
}
}
private void layoutWidgets() {
int screenWidth = Gdx.graphics.getWidth();
int screenHeight = Gdx.graphics.getHeight();
// Center progress bar
int barX = (screenWidth - 400) / 2;
int barY = (screenHeight - 30) / 2;
progressBar.setPosition(barX, barY);
// Position status label above progress bar
statusLabel.setPosition(barX, barY + 50);
}
// ... other Screen methods
}public class TextInputManager {
private TextInputDialogBox currentDialog;
public void showNameInput(TextInputListener listener) {
if (currentDialog != null) {
currentDialog.hide();
}
currentDialog = new TextInputDialogBox();
currentDialog.setModal(true);
currentDialog.setAnimationEnabled(true);
currentDialog.setButtonText("OK", "Cancel");
// Set up validation
currentDialog.setValidator(new TextInputDialogBox.InputValidator() {
@Override
public boolean isValid(String input) {
return input != null && input.trim().length() >= 2 && input.length() <= 20;
}
@Override
public String getErrorMessage() {
return "Name must be 2-20 characters long";
}
});
currentDialog.show("Enter Your Name", "", "Your name here", new TextInputDialogBox.TextInputListener() {
@Override
public void input(String text) {
currentDialog = null;
if (listener != null) {
listener.input(text.trim());
}
}
@Override
public void canceled() {
currentDialog = null;
if (listener != null) {
listener.canceled();
}
}
});
}
public void showScoreInput(int currentHighScore, TextInputListener listener) {
if (currentDialog != null) {
currentDialog.hide();
}
currentDialog = new TextInputDialogBox();
currentDialog.setMaxLength(10);
// Numeric validation
currentDialog.setValidator(new TextInputDialogBox.InputValidator() {
@Override
public boolean isValid(String input) {
try {
int score = Integer.parseInt(input);
return score > currentHighScore;
} catch (NumberFormatException e) {
return false;
}
}
@Override
public String getErrorMessage() {
return "Enter a score higher than " + currentHighScore;
}
});
currentDialog.show("New High Score!", "", "Enter score", listener);
}
public void showCustomDialog(String title, String defaultText, String hint,
TextInputDialogBox.InputValidator validator,
TextInputListener listener) {
if (currentDialog != null) {
currentDialog.hide();
}
currentDialog = new TextInputDialogBox();
if (validator != null) {
currentDialog.setValidator(validator);
}
currentDialog.show(title, defaultText, hint, listener);
}
public void hideCurrentDialog() {
if (currentDialog != null) {
currentDialog.hide();
currentDialog = null;
}
}
public boolean isDialogShowing() {
return currentDialog != null;
}
}public class GameTextInput extends PlaceholderTextBox {
private InputType inputType;
public enum InputType {
ALPHA_ONLY,
NUMERIC_ONLY,
ALPHANUMERIC,
EMAIL,
PASSWORD,
CUSTOM
}
public GameTextInput(InputType type) {
super();
this.inputType = type;
setupInputFilter();
setupStyling();
}
public GameTextInput(String placeholder, InputType type) {
super(placeholder);
this.inputType = type;
setupInputFilter();
setupStyling();
}
private void setupInputFilter() {
switch (inputType) {
case ALPHA_ONLY:
setInputFilter(new InputFilter() {
@Override
public boolean accept(char c) {
return Character.isLetter(c) || Character.isWhitespace(c);
}
@Override
public String filter(String input) {
return input.replaceAll("[^a-zA-Z\\s]", "");
}
});
break;
case NUMERIC_ONLY:
setInputFilter(new InputFilter() {
@Override
public boolean accept(char c) {
return Character.isDigit(c) || c == '.' || c == '-';
}
@Override
public String filter(String input) {
return input.replaceAll("[^0-9.-]", "");
}
});
break;
case ALPHANUMERIC:
setInputFilter(new InputFilter() {
@Override
public boolean accept(char c) {
return Character.isLetterOrDigit(c) || Character.isWhitespace(c);
}
@Override
public String filter(String input) {
return input.replaceAll("[^a-zA-Z0-9\\s]", "");
}
});
break;
case EMAIL:
setInputFilter(new InputFilter() {
@Override
public boolean accept(char c) {
return Character.isLetterOrDigit(c) || c == '@' || c == '.' || c == '_' || c == '-';
}
@Override
public String filter(String input) {
return input.replaceAll("[^a-zA-Z0-9@._-]", "").toLowerCase();
}
});
break;
}
}
private void setupStyling() {
// Add focus/blur styling
addFocusHandler(new FocusHandler() {
@Override
public void onFocus(FocusEvent event) {
getElement().getStyle().setBorderColor("#4CAF50");
getElement().getStyle().setProperty("boxShadow", "0 0 5px rgba(76, 175, 80, 0.5)");
}
});
addBlurHandler(new BlurHandler() {
@Override
public void onBlur(BlurEvent event) {
getElement().getStyle().setBorderColor("#ccc");
getElement().getStyle().setProperty("boxShadow", "none");
// Validate input on blur
validateInput();
}
});
// Real-time validation
addValueChangeHandler(new ValueChangeHandler<String>() {
@Override
public void onValueChange(ValueChangeEvent<String> event) {
validateInput();
}
});
}
private void validateInput() {
String value = getText();
boolean isValid = true;
switch (inputType) {
case EMAIL:
isValid = value.matches("^[a-zA-Z0-9._%+-]+@[a-zA-Z0-9.-]+\\.[a-zA-Z]{2,}$");
break;
case NUMERIC_ONLY:
try {
Double.parseDouble(value);
} catch (NumberFormatException e) {
isValid = !value.isEmpty();
}
break;
}
setErrorStyle(!isValid);
}
public boolean isValidInput() {
validateInput();
return !hasErrorStyle();
}
public String getValidatedText() {
if (isValidInput()) {
return getText();
}
return null;
}
}public class ResponsiveLayoutManager {
private ResizableWidgetCollection widgets;
private Map<ResizableWidget, LayoutConstraints> constraints;
public static class LayoutConstraints {
public float percentX, percentY; // Position as percentage of screen
public float percentWidth, percentHeight; // Size as percentage of screen
public int minWidth, minHeight; // Minimum pixel sizes
public int maxWidth, maxHeight; // Maximum pixel sizes
public Anchor anchor; // Anchor point for positioning
public enum Anchor {
TOP_LEFT, TOP_CENTER, TOP_RIGHT,
MIDDLE_LEFT, MIDDLE_CENTER, MIDDLE_RIGHT,
BOTTOM_LEFT, BOTTOM_CENTER, BOTTOM_RIGHT
}
}
public ResponsiveLayoutManager() {
widgets = new ResizableWidgetCollection();
constraints = new HashMap<>();
// Listen for window resize events
Window.addResizeHandler(new ResizeHandler() {
@Override
public void onResize(ResizeEvent event) {
layoutAllWidgets();
}
});
}
public void addWidget(ResizableWidget widget, LayoutConstraints layoutConstraints) {
widgets.add(widget);
constraints.put(widget, layoutConstraints);
layoutWidget(widget, layoutConstraints);
}
public void removeWidget(ResizableWidget widget) {
widgets.remove(widget);
constraints.remove(widget);
}
public void layoutAllWidgets() {
for (ResizableWidget widget : widgets) {
LayoutConstraints constraint = constraints.get(widget);
if (constraint != null) {
layoutWidget(widget, constraint);
}
}
}
private void layoutWidget(ResizableWidget widget, LayoutConstraints constraint) {
int screenWidth = Gdx.graphics.getWidth();
int screenHeight = Gdx.graphics.getHeight();
// Calculate size
int width = (int)(screenWidth * constraint.percentWidth);
int height = (int)(screenHeight * constraint.percentHeight);
// Apply size constraints
width = Math.max(constraint.minWidth, Math.min(constraint.maxWidth, width));
height = Math.max(constraint.minHeight, Math.min(constraint.maxHeight, height));
// Calculate position based on anchor
int x = calculateAnchoredX(screenWidth, width, constraint);
int y = calculateAnchoredY(screenHeight, height, constraint);
// Apply layout
widget.resize(width, height);
// Set position if widget supports it
if (widget instanceof Widget) {
((Widget) widget).setPixelSize(width, height);
// Position setting depends on widget implementation
}
}
private int calculateAnchoredX(int screenWidth, int widgetWidth, LayoutConstraints constraint) {
int baseX = (int)(screenWidth * constraint.percentX);
switch (constraint.anchor) {
case TOP_LEFT:
case MIDDLE_LEFT:
case BOTTOM_LEFT:
return baseX;
case TOP_CENTER:
case MIDDLE_CENTER:
case BOTTOM_CENTER:
return baseX - widgetWidth / 2;
case TOP_RIGHT:
case MIDDLE_RIGHT:
case BOTTOM_RIGHT:
return baseX - widgetWidth;
default:
return baseX;
}
}
private int calculateAnchoredY(int screenHeight, int widgetHeight, LayoutConstraints constraint) {
int baseY = (int)(screenHeight * constraint.percentY);
switch (constraint.anchor) {
case TOP_LEFT:
case TOP_CENTER:
case TOP_RIGHT:
return baseY;
case MIDDLE_LEFT:
case MIDDLE_CENTER:
case MIDDLE_RIGHT:
return baseY - widgetHeight / 2;
case BOTTOM_LEFT:
case BOTTOM_CENTER:
case BOTTOM_RIGHT:
return baseY - widgetHeight;
default:
return baseY;
}
}
// Predefined constraint builders
public static LayoutConstraints centerScreen(float percentWidth, float percentHeight) {
LayoutConstraints constraints = new LayoutConstraints();
constraints.percentX = 0.5f;
constraints.percentY = 0.5f;
constraints.percentWidth = percentWidth;
constraints.percentHeight = percentHeight;
constraints.anchor = LayoutConstraints.Anchor.MIDDLE_CENTER;
constraints.minWidth = 100;
constraints.minHeight = 50;
constraints.maxWidth = Integer.MAX_VALUE;
constraints.maxHeight = Integer.MAX_VALUE;
return constraints;
}
public static LayoutConstraints bottomCenter(float percentWidth, float height) {
LayoutConstraints constraints = new LayoutConstraints();
constraints.percentX = 0.5f;
constraints.percentY = 0.1f;
constraints.percentWidth = percentWidth;
constraints.percentHeight = 0; // Fixed height
constraints.anchor = LayoutConstraints.Anchor.BOTTOM_CENTER;
constraints.minWidth = 200;
constraints.minHeight = (int)height;
constraints.maxWidth = Integer.MAX_VALUE;
constraints.maxHeight = (int)height;
return constraints;
}
}// Widgets are designed for web browser environments
public class BrowserIntegrationHelper {
public static void configureForMobile() {
// Optimize widgets for mobile browsers
ProgressBar.setDefaultHeight(40); // Larger for touch
TextInputDialogBox.setDefaultAnimationDuration(200); // Faster for mobile
// Handle virtual keyboard
PlaceholderTextBox.addGlobalFocusHandler(new FocusHandler() {
@Override
public void onFocus(FocusEvent event) {
// Scroll input into view when virtual keyboard appears
Element element = event.getSource().getElement();
element.scrollIntoView();
}
});
}
public static void configureForDesktop() {
// Optimize for desktop browsers
ProgressBar.setDefaultHeight(24); // Standard height
TextInputDialogBox.setDefaultAnimationDuration(300); // Smooth animations
// Enable advanced keyboard shortcuts
configureKeyboardShortcuts();
}
private static void configureKeyboardShortcuts() {
// Add Ctrl+A, Ctrl+C, Ctrl+V support for text inputs
// This is handled automatically by PlaceholderTextBox
}
}Install with Tessl CLI
npx tessl i tessl/maven-com-badlogicgames-gdx--gdx-backend-gwt