░▒▓██████▓▒░░▒▓███████▓▒░ ░▒▓█▓▒░▒▓███████▓▒░ ░▒▓██████▓▒░░▒▓█▓▒░░▒▓█▓▒░▒▓█▓▒░▒▓███████▓▒░
░▒▓█▓▒░░▒▓█▓▒░▒▓█▓▒░░▒▓█▓▒░ ░▒▓█▓▒░ ░▒▓█▓▒░▒▓█▓▒░░▒▓█▓▒░▒▓█▓▒░░▒▓█▓▒░▒▓█▓▒░ ░▒▓█▓▒░
░▒▓█▓▒░░▒▓█▓▒░▒▓█▓▒░░▒▓█▓▒░ ░▒▓█▓▒░ ░▒▓█▓▒░▒▓█▓▒░ ░▒▓█▓▒░░▒▓█▓▒░▒▓█▓▒░ ░▒▓█▓▒░
░▒▓█▓▒░░▒▓█▓▒░▒▓███████▓▒░ ░▒▓█▓▒░░▒▓██████▓▒░░▒▓█▓▒▒▓███▓▒░▒▓█▓▒░░▒▓█▓▒░▒▓█▓▒░░▒▓██████▓▒░
░▒▓█▓▒░░▒▓█▓▒░▒▓█▓▒░░▒▓█▓▒░▒▓█▓▒░░▒▓█▓▒░▒▓█▓▒░ ░▒▓█▓▒░░▒▓█▓▒░▒▓█▓▒░░▒▓█▓▒░▒▓█▓▒░▒▓█▓▒░
░▒▓█▓▒░░▒▓█▓▒░▒▓█▓▒░░▒▓█▓▒░▒▓█▓▒░░▒▓█▓▒░▒▓█▓▒░ ░▒▓█▓▒░░▒▓█▓▒░▒▓█▓▒░░▒▓█▓▒░▒▓█▓▒░▒▓█▓▒░
░▒▓██████▓▒░░▒▓███████▓▒░ ░▒▓██████▓▒░░▒▓████████▓▒░░▒▓██████▓▒░ ░▒▓██████▓▒░░▒▓█▓▒░▒▓████████▓▒░
Generate Swing GUIs automatically from annotated Java objects
Features • Installation • Quick Start • Documentation • Examples
- Overview
- Features
- Installation
- Quick Start
- Core Concepts
- Annotations
- Advanced Features
- Examples
- API Reference
- Contributing
- License
OBJ2GUI2 is a powerful Java library that automatically generates Swing GUI interfaces from annotated Java objects. It uses introspection and a rich annotation system to create dynamic, editable forms with minimal code. Perfect for creating property editors, configuration interfaces, and data entry forms.
- Reduce boilerplate: Generate complete GUIs with just annotations
- Type-safe: Automatic input validation based on field types
- Multi-selection: Edit multiple objects simultaneously
- Extensible: Plugin system for custom types
- Internationalization: Built-in label management with XML
- 🔄 Automatic GUI Generation - Create complex forms from simple annotations
- 📝 Multiple Input Types - TextFields, Sliders, ComboBoxes, DatePickers, and more
- 🎯 Multi-Object Editing - Edit multiple objects at once with difference highlighting
- 🌳 Nested Object Support - Recursive editing of complex object hierarchies
- 📋 Collection Support - Built-in editors for Lists and Arrays
- 🔌 Plugin System - Extend with custom type handlers
- 🌍 I18n Support - Automatic label management with locale support
- ✅ Validation - Built-in and custom validators
- 🎨 Layout Control - Flexible layout management with MigLayout
- 🌲 Tree View - Hierarchical object navigation
- 🔗 Binding System - Map IDs to objects for ComboBoxes
<dependency>
<groupId>io.github.warnotte</groupId>
<artifactId>obj2gui2</artifactId>
<version>1.4.4</version>
</dependency>
implementation 'io.github.warnotte:obj2gui2:1.4.4'
import io.github.warnotte.obj2gui2.*;
import io.github.warnotte.waxlib3.core.TemplatePropertyMerger.Annotations.*;
public class Person {
@PROPERTY_interface(gui_type = gui_type.TEXTFIELD)
private String name;
@PROPERTY_interface(gui_type = gui_type.SLIDER, min = 0, max = 120)
private int age;
@PROPERTY_interface(gui_type = gui_type.COMBO)
private Gender gender;
@PROPERTY_interface(gui_type = gui_type.CHECKBOX)
private boolean active;
@PROPERTY_interface(gui_type = gui_type.DATEPICKER)
private Date birthDate;
// Getters and setters...
}
// Create objects to edit
List<Person> persons = Arrays.asList(new Person(), new Person());
// Generate the magic panel
JPanelMagique panel = JPanelMagique.GenerateJPanelFromSelectionAndBindings(
persons, null);
// Add to your frame
JFrame frame = new JFrame("Person Editor");
frame.add(new JScrollPane(panel));
frame.setSize(400, 300);
frame.setVisible(true);
The central class that generates GUI panels from annotated objects.
// Basic usage
JPanelMagique panel = JPanelMagique.GenerateJPanelFromSelectionAndBindings(
selection, bindings);
// With all options
JPanelMagique panel = JPanelMagique.GenerateJPanelFromSelectionAndBindings(
parent, // Parent panel (for event propagation)
selection, // List of objects to edit
recursive, // Edit nested objects
bindings, // ID-to-object bindings
bindingsEnum, // Enum bindings
onlyAnnotated, // Only show annotated fields
showTreeView // Show tree view buttons
);
When editing multiple objects:
- Same values: Display normally
- Different values: Highlight in orange
- Last object rule: Display value from last selected object
// Enable new system (shows last object value instead of "Different values")
JPanelMagique.newSystem = true;
OBJ2GUI2 uses a custom event system to notify changes:
panel.addMyEventListener(new MyEventListener() {
@Override
public void myEventOccurred(MyChangedEvent evt) {
System.out.println("Object changed!");
}
});
Main annotation for configuring property display:
@PROPERTY_interface(
gui_type = gui_type.TEXTFIELD, // Component type
readOnly = false, // Make field read-only
isDisplayLabel = true, // Show label
min = 0, // Minimum value (for sliders)
max = 100, // Maximum value
divider = 1.0, // Divider for sliders
extension = ".txt", // File extension (for file choosers)
isDirectoryOnly = false, // Directory selection only
displayFormat = "#0.00", // Number format
editFormat = "#0.00" // Edit format
)
public enum gui_type {
TEXTFIELD, // Simple text input
TEXTAREA, // Multi-line text
TEXTPANE, // Rich text
CHECKBOX, // Boolean checkbox
COMBO, // ComboBox
SLIDER, // JSlider
FLATSLIDER, // Custom flat slider
DATEPICKER, // Date selection
COLORCHOOSER, // Color picker
FILECHOOSER, // File selection
DECIMALFORMATTEDTEXTFIELD, // Formatted numbers
MASKFORMATTEDTEXTFIELD, // Masked input
REGEXPFORMATTEDFIELD // Regex-validated input
}
Mark fields containing editable objects:
public class Order {
@PROPERTY_FIELD_XXXABLE
private Customer customer; // Creates nested editor
}
Mark list fields for collection editing:
public class Invoice {
@PROPERTY_FIELD_LISTABLE
private List<InvoiceItem> items; // Creates list editor with +/- buttons
}
Add action buttons to the panel:
public class Report {
@PROPERTY_button(
method_name = "generateReport",
text = "Generate",
toolTipText = "Generate the report",
threadedMethod = true // Run in separate thread
)
private String dummyField;
public void generateReport() {
// Method called when button clicked
}
}
@PROPERTY_MIGLAYOUT(
LayoutConstraints = "fill, gap 5",
ColumnConstraints = "[right]rel[grow,fill]",
RowConstraints = "[]5[]",
labelPosition = lblposition.RIGHT
)
public class MyClass {
// Fields...
}
Map integer IDs to objects for ComboBoxes:
// Create materials list
List<Material> materials = getMaterials();
// Create binding
Binding binding = new Binding(
materials, // List of Identifiable objects
Product.class, // Target class
"materialId" // Field name
);
// Use in panel generation
List<Binding> bindings = Arrays.asList(binding);
JPanelMagique panel = JPanelMagique.GenerateJPanelFromSelectionAndBindings(
products, bindings);
Create custom handlers for specific types:
// Simple plugin
public class MyTypePlugin implements OBJ2GUIPlug<MyType, JTextField> {
@Override
public Class<MyType> getType() { return MyType.class; }
@Override
public JTextField build(Object value, List<?> selection,
Method getter, Method setter,
JPanelMagique panel) {
JTextField field = new JTextField();
if (value != null) {
field.setText(value.toString());
}
return field;
}
@Override
public Object getValue(Object component) {
return new MyType(((JTextField)component).getText());
}
@Override
public void refresh(Object value, JComponent component) {
((JTextField)component).setText(value.toString());
}
}
// Register plugin
JPanelMagique.registerPlugin(new MyTypePlugin());
Add custom validators:
// Create validator
Validator<Person, Integer> ageValidator = new Validator<Person, Integer>() {
@Override
public Integer valideValue(Person object, Integer oldValue, Integer newValue)
throws ValidationException {
if (newValue < 0 || newValue > 150) {
throw new ValidationException("Age must be between 0 and 150");
}
return newValue;
}
};
// Register validator
Method setAgeMethod = Person.class.getMethod("setAge", int.class);
JPanelMagique.registerValidator(setAgeMethod, ageValidator);
Automatic label persistence with XML:
// Set label directory
JPanelMagique.LABEL_XML_DIRECTORY = "config/labels";
// Labels are automatically saved/loaded
// Files: config/labels/obj2gui_Labels/en/ClassName_labels.xml
// Manual label management
GUI2XMLLabel.SaveLabel(panel, "labels.xml");
GUI2XMLLabel.LoadLabel(panel, "labels.xml");
Navigate complex object hierarchies:
JTreeMagique tree = JTreeMagique.GenerateJTreeFromSelectionAndBindings(
selection, bindings, bindingsEnum,
"labels.xml", // Label file
true, // Use tabbed pane
true // Show tree view buttons
);
@PROPERTY_MIGLAYOUT(
LayoutConstraints = "fill, gap 5",
ColumnConstraints = "[right]rel[grow,fill]"
)
public class Task {
@PROPERTY_interface(gui_type = gui_type.TEXTFIELD)
private String title;
@PROPERTY_interface(gui_type = gui_type.TEXTAREA)
private String description;
@PROPERTY_interface(gui_type = gui_type.COMBO)
private Priority priority;
@PROPERTY_interface(gui_type = gui_type.DATEPICKER)
private Date dueDate;
@PROPERTY_interface(gui_type = gui_type.SLIDER, min = 0, max = 100)
private int progress;
@PROPERTY_interface(gui_type = gui_type.CHECKBOX)
private boolean completed;
@PROPERTY_FIELD_XXXABLE
private TaskDetails details;
@PROPERTY_FIELD_LISTABLE
private List<SubTask> subTasks;
@PROPERTY_button(
method_name = "save",
text = "Save Task",
toolTipText = "Save changes to database"
)
private String saveButton;
public enum Priority {
LOW, MEDIUM, HIGH, URGENT
}
// Methods
public void save() {
// Save to database
}
// Getters and setters...
}
public class DataSet {
@PROPERTY_interface
private double[] values; // Automatically creates table editor
@PROPERTY_interface
private String[][] matrix; // 2D table editor
}
public class Financial {
@PROPERTY_interface(
gui_type = gui_type.DECIMALFORMATTEDTEXTFIELD,
displayFormat = "$#,##0.00",
editFormat = "0.00"
)
private BigDecimal amount;
@PROPERTY_interface(
gui_type = gui_type.MASKFORMATTEDTEXTFIELD,
displayFormat = "###-##-####" // SSN format
)
private String ssn;
}
JPanel_MultiPropsEditor
is a ready-to-use property inspector that can display and edit a heterogeneous selection of domain objects inside a single, scrollable Swing panel.
Think of it as the “Inspector” you find in tools like Unity, Blender or IntelliJ designer — generated for you, in one line of code.
- Drop-in inspector for mixed selections (different runtime classes).
- Graceful empty state – shows a “Nothing selected” card until items are provided.
- Automatic grouping – partitions the current selection by class and stacks one
JPanelMagique
per group. - Bidirectional event relay – implements
MyEventListener
and forwards everyMyChangedEvent
to its own listeners, so you only have to register once. - Programmatic refresh – call
refresh()
to redraw all embedded panels after model changes. - Highly tunable constructor – toggle recursion, “only-annotated” mode, custom
Binding
/BindingEnum
lists and tree-view buttons directly in the constructor. - Localisable UI strings – edit
JPanel_MultiPropsEditor.text_nothing_selected
to translate the placeholder.
What happens | Internal mechanism |
---|---|
Selection is empty | A CardLayout switches to a placeholder card called NONE. |
Mixed classes in selection | setTaskProperties groups by Class<?> (Collectors.groupingBy ) and creates one JPanelMagique per bucket. |
Each sub-panel stretches horizontally | After creation, each panel’s max width is set to Integer.MAX_VALUE while preserving its preferred height. |
Scrollable inspector | All sub-panels live inside a JScrollPane with smoother scroll (unitIncrement = 16 ). |
Change propagation | Every sub-panel registers the inspector as a MyEventListener ; myEventOccurred relays to external listeners. |
Runtime updates | refresh() loops through child JPanelMagique instances and calls their own refresh() method. |
// 1. Build a mixed selection
List<Object> selection = List.of(new Sphere(), new Cube(), new PointLight());
// 2. Create the inspector (recursive editing enabled)
JPanel_MultiPropsEditor inspector = new JPanel_MultiPropsEditor(true);
inspector.setTaskProperties(selection);
// 3. Listen to change events just once
inspector.addMyEventListener(evt -> System.out.println("Something changed!"));
// 4. Plug into your UI
frame.add(inspector, BorderLayout.EAST);
List<Binding> binds = List.of(
new Binding(materials, Product.class, "materialId"));
List<BindingEnum> bindsEnum = List.of(/* … */);
JPanel_MultiPropsEditor inspector = new JPanel_MultiPropsEditor(
/* isRecursive */ true,
/* binds */ binds,
/* bindsEnum */ bindsEnum,
/* onlyAnnotatedMethods */ false,
/* showTreeViewButtons */ true);
inspector.setTaskProperties(selection);
// Change the placeholder text globally
JPanel_MultiPropsEditor.text_nothing_selected = "No objects selected";
Class | Description |
---|---|
JPanelMagique |
Main panel generator |
JTreeMagique |
Tree view for object navigation |
Binding |
ID-to-object binding |
BindingEnum |
Enum binding |
GUI2XMLLabel |
Label management |
// Register custom title for class
JPanelMagique.registerTitleBorder(Person.class, "Person Details");
// Register title for list fields
JPanelMagique.registerTitleBorderForList(
Order.class, "items", "Order Items");
// Configure buttons
JPanelMagique.setTEXT_BOUTON_ADD_ELEMENT("Add");
JPanelMagique.setIMG_BOUTON_ADD_ELEMENT(addIcon);
Contributions are welcome! Please feel free to submit a Pull Request.
- Clone the repository
- Import as Maven project
- Run tests:
mvn test
- Build:
mvn package
This project is licensed under the MIT License - see the LICENSE file for details.
Created by Warnotte Renaud (2011-2024)