Basic Usage
1 |
SwingCustomizer.getDefault().openObjectFrame(myObject); |
Disable the design mode by setting this system property:
-Dxy.reflect.ui.infoCustomizationsToolsHidden=true
The GUI builder Online Documenation
Since the version 4.6.0 the GUI builder has an online documentation:
Demonstration Videos
Video | Covered Subjects |
---|---|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
Architecture
This UI is generated in 2 steps:
- Each object class informations are read and interpreted by the ReflectionUI object. The result is an abstract GUI model element: ITypeInfo.
- Then this abstract GUI model is passed to the SwingRenderer that will create the various GUI controls displaying the object values
(ReflectionUI) | (SwingRenderer) | |||
Class | ——————–> | ITypeInfo | ——————–> | UI |
The ReflectionUI interpretation algorithm is based on the Java language coding standards (ex: getter/setter interpreted as a unique property, …).
Unfortunately, even when these standards are respected, there might not be enough information in every class definition to provide a UI that looks and behaves exactly as expected. This is why the ReflectionUI interpretation process will often need to be customized. This is where the InfoProxyFactory comes into play. It is not essential to the ReflectionUI customization, but it makes it easier by allowing to specify conveniently a proxy for each ITypeInfo object.
CustomUI provides the customizations editor by using the following classes:
- InfoCustomizations: declarative customizations persisted in XML.
- InfoCustomizationsFactory: subclass of InfoProxyFactory generating proxies according to an InfoCustomizations instance.
- CustomizedUI: ReflectionUI subclass compatible with InfoCustomizationsFactory
- SwingCustomizer: subclass of SwingRenderer compatible with CustomizedUI
(CustomizedUI) | (InfoCustomizationsFactory) | (SwingCustomizer) | ||||
| | | | | | ||||
∇ | ∇ | ∇ | ||||
(ReflectionUI) | (InfoProxyFactory) | (SwingRenderer) | ||||
Class | ——————–> | ITypeInfo | ——————–> | ITypeInfo (proxy) | ——————–> | UI |
^ | ||||||
|_______ | ____| |
HelloWorld example
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 |
package xy.reflect.ui.example; import java.io.Serializable; import java.util.ArrayList; import java.util.List; import xy.reflect.ui.control.swing.customizer.SwingCustomizer; import xy.reflect.ui.util.MoreSystemProperties; /** * Example showing a basic usage of the ReflectionUI library. * * @author olitank * */ public class HelloWorld implements Serializable { public static void main(String[] args) { System.out.println("Set the following system property to disable the design mode:\n-D" + MoreSystemProperties.HIDE_INFO_CUSTOMIZATIONS_TOOLS + "=true"); SwingCustomizer.getDefault().openObjectFrame(new HelloWorld()); } private static final long serialVersionUID = 1L; private String name = "world"; private Language language = Language.English; private boolean upperCase = false; private List<String> history = new ArrayList<String>(); public String getName() { return name; } public void setName(String name) { this.name = name; } public Language getLanguage() { return language; } public void setLanguage(Language language) { this.language = language; } public boolean isUpperCase() { return upperCase; } public void setUpperCase(boolean upperCase) { this.upperCase = upperCase; } public List<String> getHistory() { return history; } public void setHistory(List<String> history) { this.history = history; } public String sayHello() { String result = ""; if (language == Language.English) { result += "Hello"; } else if (language == Language.French) { result += "Bonjour"; } else if (language == Language.Spanish) { result += "Hola"; } else { throw new AssertionError(); } result += " " + name + "!"; if (upperCase) { result = result.toUpperCase(); } history.add(result); return result; } public enum Language { English, French, Spanish } } |
Tutorials
Tutorials have been written as class methods in the examples sub-project. You should read the comments first:
- ReflectionUI tutorial: <DISTRIBUTION_DIRECTORY>/examples/src/main/java/xy/reflect/ui/example/ReflectionUITutorial.java
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195 196 197 198 199 200 201 202 203 204 205 206 207 208 209 210 211 212 213 214 215 216 217 218 219 220 221 222 223 224 225 226 227 228 229 230 231 232 233 234 235 236 237 238 239 240 241 242 243 244 245 246 247 248 249 250 251 252 253 254 255 256 257 258 259 260 261 262 263 264 265 266 267 268 269 270 271 272 273 274 275 276 277 278 279 280 281 282 283 284 285 286 287 288 289 290 291 292 293 294 295 296 297 298 299 300 301 302 303 304 305 306 307 308 309 310 311 312 313 314 315 316 317 318 319 320 321 322 323 324 325 326 327 328 329 330 331 332 333 334 335 336 337 338 339 340 341 342 343 344 345 346 347 348 349 350 351 352 353 354 355 356 357 358 359 360 361 362 363 364 365 366 367 368 369 370 371 372 373 374 375 376 377 378 379 380 381 382 383 384 385 386 387 388 389 390 391 392 393 394 395 396 397 398 399 400 401 402 403 404 405 406 407 408 409 410 411 412 413 414 415 416 417 418 419 420 421 422 423 424 425 426 427 428 429 430 431 432 433 434 435 436 437 438 439 440 441 442 443 444 445 446 447 448 449 450 451 452 453 454 455 456 457 458 459 460 461 462 463 464 465 466 467 468 469 470 471 472 473 474 475 476 477 478 479 480 481 482 483 484 485 486 487 488 489 490 491 492 493 494 495 496 497 498 499 500 501 502 503 504 505 506 507 508 509 510 511 512 513 514 515 516 517 518 519 520 521 522 523 524 525 526 527 528 529 530 531 532 533 534 535 536 537 538 539 540 541 542 543 544 545 546 547 548 549 550 551 552 553 554 555 556 557 558 559 560 561 562 563 564 565 566 567 568 569 570 571 572 573 574 575 576 577 578 579 580 581 582 583 584 585 586 587 588 589 590 591 592 593 594 595 596 597 598 599 600 601 602 603 604 605 606 607 608 609 610 611 612 613 614 615 616 617 618 619 620 621 622 623 624 625 626 627 628 629 630 631 632 633 634 635 636 637 638 639 640 641 642 643 644 645 646 647 648 649 650 651 652 653 654 655 656 657 658 659 660 661 662 663 664 665 666 667 668 669 670 671 672 673 674 675 676 677 678 679 680 681 682 683 684 685 686 687 688 689 690 691 692 693 694 695 696 697 698 699 700 701 702 703 704 705 706 707 708 709 710 711 712 713 714 715 716 717 718 719 720 721 722 723 724 725 726 727 728 729 730 731 732 733 734 735 736 737 738 739 |
/** * ReflectionUI library demonstration: Each of the following methods * demonstrates a feature of the library. * * @author olitank * */ package xy.reflect.ui.example; import java.awt.Color; import java.awt.Component; import java.awt.event.ActionEvent; import java.awt.event.ActionListener; import java.io.ByteArrayInputStream; import java.io.ByteArrayOutputStream; import java.io.IOException; import java.util.ArrayList; import java.util.Arrays; import java.util.List; import javax.swing.JOptionPane; import javax.swing.JToggleButton; import xy.reflect.ui.CustomizedUI; import xy.reflect.ui.ReflectionUI; import xy.reflect.ui.control.IFieldControlData; import xy.reflect.ui.control.plugin.IFieldControlPlugin; import xy.reflect.ui.control.swing.plugin.OptionButtonsPlugin; import xy.reflect.ui.control.swing.plugin.OptionButtonsPlugin.OptionButtonsConfiguration; import xy.reflect.ui.control.swing.renderer.CustomizedSwingRenderer; import xy.reflect.ui.control.swing.renderer.FieldControlPlaceHolder; import xy.reflect.ui.control.swing.renderer.Form; import xy.reflect.ui.control.swing.renderer.MethodControlPlaceHolder; import xy.reflect.ui.control.swing.renderer.SwingRenderer; import xy.reflect.ui.info.ColorSpecification; import xy.reflect.ui.info.InfoCategory; import xy.reflect.ui.info.app.ApplicationInfoProxy; import xy.reflect.ui.info.app.IApplicationInfo; import xy.reflect.ui.info.custom.InfoCustomizations; import xy.reflect.ui.info.custom.InfoCustomizations.FieldCustomization; import xy.reflect.ui.info.custom.InfoCustomizations.TypeCustomization; import xy.reflect.ui.info.field.FieldInfoProxy; import xy.reflect.ui.info.field.IFieldInfo; import xy.reflect.ui.info.filter.IInfoFilter; import xy.reflect.ui.info.menu.Menu; import xy.reflect.ui.info.menu.MenuItemCategory; import xy.reflect.ui.info.menu.MenuModel; import xy.reflect.ui.info.menu.builtin.swing.CloseWindowMenuItem; import xy.reflect.ui.info.menu.builtin.swing.OpenMenuItem; import xy.reflect.ui.info.menu.builtin.swing.SaveAsMenuItem; import xy.reflect.ui.info.method.IMethodInfo; import xy.reflect.ui.info.method.InvocationData; import xy.reflect.ui.info.method.MethodInfoProxy; import xy.reflect.ui.info.parameter.IParameterInfo; import xy.reflect.ui.info.type.ITypeInfo; import xy.reflect.ui.info.type.ITypeInfo.CategoriesStyle; import xy.reflect.ui.info.type.factory.ControlPluginActivationFactory; import xy.reflect.ui.info.type.factory.InfoProxyFactory; import xy.reflect.ui.info.type.iterable.IListTypeInfo; import xy.reflect.ui.info.type.source.ITypeInfoSource; import xy.reflect.ui.info.type.source.JavaTypeInfoSource; import xy.reflect.ui.util.ReflectionUIUtils; import xy.reflect.ui.util.SwingRendererUtils; public class ReflectionUITutorial { public static void main(String[] args) { openObjectDialog(); createObjectForm(); changeFieldsAndMethodOrder(); allowToSetNull(); hideSomeFieldsAndMethods(); addVirtualFieldsAndMethods(); categorizeFieldsAndMethods(); customizeCopyCutPasteFeature(); customizeColors(); addMenus(); useFieldControlPlugins(); createCustomControls(); useInfoCustomizationsClass(); useXmlCustomizations(); openObjectFrame(); } private static void openObjectDialog() { /* * Most basic use case: opening an object dialog: */ Object myObject = new HelloWorld(); SwingRenderer.getDefault().openObjectDialog(null, myObject, myObject + " Dialog"); } private static void createObjectForm() { /* * create JPanel-based form in order to include it in a GUI as a sub-component. */ Object myObject = new HelloWorld(); JOptionPane.showMessageDialog(null, SwingRenderer.getDefault().createForm(myObject), "As a form", JOptionPane.INFORMATION_MESSAGE); } private static void changeFieldsAndMethodOrder() { Object myObject = new HelloWorld(); ReflectionUI reflectionUI = new ReflectionUI() { @Override public ITypeInfo getTypeInfo(ITypeInfoSource typeInfoSource) { return new InfoProxyFactory() { /* * You can change the order of the fields of your class by overloading the * following method: */ @Override protected List<IFieldInfo> getFields(ITypeInfo containingType) { if (containingType.getName().equals(HelloWorld.class.getName())) { List<IFieldInfo> result = new ArrayList<IFieldInfo>(super.getFields(containingType)); IFieldInfo nameField = ReflectionUIUtils.findInfoByName(result, "name"); result.remove(nameField); result.add(0, nameField); return result; } else { return super.getFields(containingType); } } /* * You can change the order of the methods of your class by overloading the * following method: */ @Override protected List<IMethodInfo> getMethods(ITypeInfo type) { return super.getMethods(type); } }.wrapTypeInfo(super.getTypeInfo(typeInfoSource)); } }; SwingRenderer swingRenderer = new SwingRenderer(reflectionUI); swingRenderer.openObjectDialog(null, myObject, "Name field => first field"); } private static void allowToSetNull() { Object myObject = new HelloWorld(); ReflectionUI reflectionUI = new ReflectionUI() { @Override public ITypeInfo getTypeInfo(ITypeInfoSource typeInfoSource) { return new InfoProxyFactory() { /* * By default, the generated user interface does not allow to assign <null> * values. Indeed, the developers do not generally allow the assignment of * <null> values from their graphical interface despite the fact that it is * authorized by the language. The following methods allow to selectively enable * or disable the nullable facet of any value displayed in the generated GUI. */ @Override protected boolean isNullValueDistinct(IParameterInfo param, IMethodInfo method, ITypeInfo containingType) { if (containingType.getName().equals(HelloWorld.class.getName())) { return !param.getType().isPrimitive(); } else { return super.isNullValueDistinct(param, method, containingType); } } @Override protected boolean isNullValueDistinct(IFieldInfo field, ITypeInfo containingType) { if (containingType.getName().equals(HelloWorld.class.getName())) { return !field.getType().isPrimitive(); } else { return super.isNullValueDistinct(field, containingType); } } @Override protected boolean isNullReturnValueDistinct(IMethodInfo method, ITypeInfo containingType) { if (containingType.getName().equals(HelloWorld.class.getName())) { return !method.getReturnValueType().isPrimitive(); } else { return super.isNullReturnValueDistinct(method, containingType); } } }.wrapTypeInfo(super.getTypeInfo(typeInfoSource)); } }; SwingRenderer swingRenderer = new SwingRenderer(reflectionUI); swingRenderer.openObjectDialog(null, myObject, "Non-primitive fields => nullable"); } private static void hideSomeFieldsAndMethods() { Object myObject = new HelloWorld(); ReflectionUI reflectionUI = new ReflectionUI() { @Override public ITypeInfo getTypeInfo(ITypeInfoSource typeInfoSource) { return new InfoProxyFactory() { /* * This is how you can hide fields and methods: */ @Override protected boolean isHidden(IFieldInfo field, ITypeInfo containingType) { if (field.getType() instanceof IListTypeInfo) { return true; } else { return super.isHidden(field, containingType); } } @Override protected boolean isHidden(IMethodInfo method, ITypeInfo containingType) { return super.isHidden(method, containingType); } @Override protected boolean isHidden(IParameterInfo param, IMethodInfo method, ITypeInfo containingType) { return super.isHidden(param, method, containingType); } }.wrapTypeInfo(super.getTypeInfo(typeInfoSource)); } }; SwingRenderer swingRenderer = new SwingRenderer(reflectionUI); swingRenderer.openObjectDialog(null, myObject, "List fields => hidden"); } private static void addVirtualFieldsAndMethods() { Object myObject = new HelloWorld(); ReflectionUI reflectionUI = new ReflectionUI() { @Override public ITypeInfo getTypeInfo(ITypeInfoSource typeInfoSource) { return new InfoProxyFactory() { /* * This is how you can add virtual fields and methods that would generally be * calculated from the existing ones: */ @Override protected List<IFieldInfo> getFields(ITypeInfo type) { if (type.getName().equals(HelloWorld.class.getName())) { final List<IFieldInfo> result = new ArrayList<IFieldInfo>(super.getFields(type)); result.add(new FieldInfoProxy(IFieldInfo.NULL_FIELD_INFO) { @Override public String getName() { return "numberOfFields"; } @Override public String getCaption() { return "(Virtual) Number Of Fields"; } @Override public Object getValue(Object object) { return result.size(); } }); return result; } else { return super.getFields(type); } } @Override protected List<IMethodInfo> getMethods(ITypeInfo type) { if (type.getName().equals(HelloWorld.class.getName())) { List<IMethodInfo> result = new ArrayList<IMethodInfo>(super.getMethods(type)); result.add(new MethodInfoProxy(IMethodInfo.NULL_METHOD_INFO) { @Override public String getName() { return "resetFields"; } @Override public String getCaption() { return "(Virtual) Reset Fields"; } @Override public Object invoke(Object object, InvocationData invocationData) { HelloWorld newObject = new HelloWorld(); for (IFieldInfo field : ReflectionUI.getDefault() .getTypeInfo(new JavaTypeInfoSource(HelloWorld.class, null)).getFields()) { if (field.isGetOnly()) { continue; } field.setValue(object, field.getValue(newObject)); } return null; } }); return result; } else { return super.getMethods(type); } } }.wrapTypeInfo(super.getTypeInfo(typeInfoSource)); } }; SwingRenderer swingRenderer = new SwingRenderer(reflectionUI); swingRenderer.openObjectDialog(null, myObject, "Added virtual members (numberOfFields + resetFields())"); } private static void categorizeFieldsAndMethods() { Object myObject = new HelloWorld(); ReflectionUI reflectionUI = new ReflectionUI() { @Override public ITypeInfo getTypeInfo(ITypeInfoSource typeInfoSource) { return new InfoProxyFactory() { /* * you have the ability to categorize the fields and methods of a class like * this: */ @Override protected InfoCategory getCategory(IFieldInfo field, ITypeInfo containingType) { if (containingType.getName().equals(HelloWorld.class.getName())) { if (!field.getName().equals("name")) { return new InfoCategory("Advanced", 1, null); } } return super.getCategory(field, containingType); } @Override protected InfoCategory getCategory(IMethodInfo method, ITypeInfo containingType) { return super.getCategory(method, containingType); } /* * This method allows you to modify the display of categories (classic, modern, * ...): */ @Override protected CategoriesStyle getCategoriesStyle(ITypeInfo type) { return CategoriesStyle.MODERN; } }.wrapTypeInfo(super.getTypeInfo(typeInfoSource)); } }; SwingRenderer swingRenderer = new SwingRenderer(reflectionUI); swingRenderer.openObjectDialog(null, myObject, "'Advanced properties' category created"); } private static void customizeCopyCutPasteFeature() { Object myObject = new HelloWorld(); ReflectionUI reflectionUI = new ReflectionUI() { @Override public ITypeInfo getTypeInfo(ITypeInfoSource typeInfoSource) { return new InfoProxyFactory() { /* * copy/cut/paste: By default this feature is enabled on collections/arrays but * only for Serializable items. If your item class does not implement the * Serializable interface yet you want to allow to copy its instances (or if you * want to customize the copy process) then override the following methods. Here * actually we will simply disable the feature on all lists: */ @Override public boolean canCopy(ITypeInfo type, Object object) { return false; } @Override public Object copy(ITypeInfo type, Object object) { throw new AssertionError(); } }.wrapTypeInfo(super.getTypeInfo(typeInfoSource)); } }; SwingRenderer swingRenderer = new SwingRenderer(reflectionUI); swingRenderer.openObjectDialog(null, myObject, "Copy/Cut/Paste from lists => disabled"); } private static void customizeColors() { Object myObject = new HelloWorld(); ReflectionUI reflectionUI = new ReflectionUI() { @Override public IApplicationInfo getApplicationInfo() { return new ApplicationInfoProxy(super.getApplicationInfo()) { @Override public boolean isSystemIntegrationCrossPlatform() { return true; } @Override public ColorSpecification getTitleBackgroundColor() { return SwingRendererUtils.getColorSpecification(Color.RED.darker()); } @Override public ColorSpecification getTitleForegroundColor() { return SwingRendererUtils.getColorSpecification(Color.WHITE); } @Override public ColorSpecification getMainBackgroundColor() { return SwingRendererUtils.getColorSpecification(Color.BLACK); } @Override public ColorSpecification getMainForegroundColor() { return SwingRendererUtils.getColorSpecification(Color.WHITE); } @Override public ColorSpecification getMainBorderColor() { return SwingRendererUtils.getColorSpecification(Color.GRAY); } @Override public ColorSpecification getMainButtonBackgroundColor() { return SwingRendererUtils.getColorSpecification(Color.CYAN); } @Override public ColorSpecification getMainButtonForegroundColor() { return SwingRendererUtils.getColorSpecification(Color.BLACK); } @Override public ColorSpecification getMainButtonBorderColor() { return SwingRendererUtils.getColorSpecification(Color.RED); } }; } }; SwingRenderer swingRenderer = new SwingRenderer(reflectionUI); swingRenderer.openObjectDialog(null, myObject, "Custom colors"); } private static void addMenus() { Object myObject = new HelloWorld(); ReflectionUI reflectionUI = new ReflectionUI() { @Override public ITypeInfo getTypeInfo(ITypeInfoSource typeInfoSource) { return new InfoProxyFactory() { /* * In order to add a menu bar to windows created with ReflectionUI, you must * override the following method. Note that each object can independently * contribute via its ITypeInfo to the menu bar. The contributions are then * merged by rendrerer. */ @Override protected MenuModel getMenuModel(ITypeInfo type) { if (type.getName().equals(HelloWorld.class.getName())) { MenuModel menuModel = new MenuModel(); { Menu fileMenu = new Menu("File"); { /* * Some standards menu items are provided by the framework (open file, save * file, quit, undo, redo, ...). Browse the package * "xy.reflect.ui.info.menu.builtin.swing" for more information. They use the * related features defined in the I*Info meta objects. The menu item categories * allow to distinctly separate groups of menu items. * */ MenuItemCategory persistenceCategory = new MenuItemCategory("Persistence"); { /* * In our case we only need the displayed object to implement the Serializable * interface for the following file menu items to works. */ OpenMenuItem openFileMenuItem = new OpenMenuItem(); { openFileMenuItem.setName("Open Hello World File"); persistenceCategory.addItem(openFileMenuItem); } SaveAsMenuItem saveAsFileMenuItem = new SaveAsMenuItem(); { saveAsFileMenuItem.setName("Save Hello World File As..."); persistenceCategory.addItem(saveAsFileMenuItem); } fileMenu.addItemCategory(persistenceCategory); } MenuItemCategory lifeCycleCategory = new MenuItemCategory("Life Cycle"); { CloseWindowMenuItem exitMenuItem = new CloseWindowMenuItem(); { exitMenuItem.setName("Quit"); lifeCycleCategory.addItem(exitMenuItem); } fileMenu.addItemCategory(lifeCycleCategory); } } menuModel.setMenus(Arrays.asList(fileMenu)); } return menuModel; } else { return super.getMenuModel(type); } } }.wrapTypeInfo(super.getTypeInfo(typeInfoSource)); } }; SwingRenderer swingRenderer = new SwingRenderer(reflectionUI); swingRenderer.openObjectDialog(null, myObject, "With File Menu"); } private static void useFieldControlPlugins() { Object myObject = new HelloWorld(); /* * Control plugins make it easy to customize the generated UIs by providing * easy-to-use alternative controls. Each plugin potentially offers * configuration options. Some built-in plugins are available. Browse the * package "xy.reflect.ui.control.swing.plugin" for more information. */ final OptionButtonsPlugin radioButtonsPlugin = new OptionButtonsPlugin(); final OptionButtonsConfiguration radioButtonsConfiguration = new OptionButtonsConfiguration(); ReflectionUI reflectionUI = new ReflectionUI() { /* * A plugin can be shared by several fields. It must therefore be associated * with the desired fields as follows: */ @Override public ITypeInfo getTypeInfo(ITypeInfoSource typeInfoSource) { return new InfoProxyFactory() { @Override protected ITypeInfo getType(IFieldInfo field, ITypeInfo containingType) { if (containingType.getName().equals(HelloWorld.class.getName()) && field.getName().equals("language")) { ITypeInfo result = super.getType(field, containingType); result = new ControlPluginActivationFactory(radioButtonsPlugin.getIdentifier(), radioButtonsConfiguration).wrapTypeInfo(result); return result; } else { return super.getType(field, containingType); } } }.wrapTypeInfo(super.getTypeInfo(typeInfoSource)); } }; SwingRenderer swingRenderer = new SwingRenderer(reflectionUI) { /* * The plugin must then be registered with the renderer for its activation to be * effective: */ @Override public List<IFieldControlPlugin> getFieldControlPlugins() { List<IFieldControlPlugin> result = new ArrayList<IFieldControlPlugin>(super.getFieldControlPlugins()); result.add(0, radioButtonsPlugin); return result; } }; swingRenderer.openObjectDialog(null, myObject, "Language field control => radio buttons"); } private static void useInfoCustomizationsClass() { Object myObject = new HelloWorld(); /* * Create a CustomizedUI: This is a subclass of ReflectionUI allowing the use of * declarative customization of type informations. */ InfoCustomizations customizations = new InfoCustomizations(); CustomizedUI customizedUI = new CustomizedUI(customizations); /* * Initilize the customization of the chosen type. */ TypeCustomization helloWorldTypeCustomization = InfoCustomizations.getTypeCustomization(customizations, HelloWorld.class.getName(), true); /* * Customize a field of this type. */ FieldCustomization nameFieldCustomization = InfoCustomizations .getFieldCustomization(helloWorldTypeCustomization, "name", true); nameFieldCustomization.setCustomFieldCaption( "(Caption modified through " + InfoCustomizations.class.getSimpleName() + " class) Name"); /* * Open the customized UI. */ SwingRenderer swingRenderer = new SwingRenderer(customizedUI); swingRenderer.openObjectDialog(null, myObject, "'Name' field caption modified using " + InfoCustomizations.class.getSimpleName() + " class"); } private static void createCustomControls() { Object myObject = new HelloWorld(); ReflectionUI reflectionUI = new ReflectionUI(); SwingRenderer swingRenderer = new SwingRenderer(reflectionUI) { @Override public Form createForm(Object object, IInfoFilter infoFilter) { return new Form(this, object, infoFilter) { private static final long serialVersionUID = 1L; /* * In order to create a custom field control: */ @Override public FieldControlPlaceHolder createFieldControlPlaceHolder(final IFieldInfo field) { return new FieldControlPlaceHolder(this.swingRenderer, this, field) { private static final long serialVersionUID = 1L; @Override public Component createFieldControl() { if (field.getName().equals("upperCase")) { final IFieldControlData data = getControlData(); final JToggleButton result = new JToggleButton(data.getCaption()); result.setSelected((Boolean) data.getValue()); result.addActionListener(new ActionListener() { @Override public void actionPerformed(ActionEvent e) { data.setValue(result.isSelected()); } }); return result; } else { return super.createFieldControl(); } } }; } /* * In order to create a custom method control: */ @Override public MethodControlPlaceHolder createMethodControlPlaceHolder(IMethodInfo method) { return new MethodControlPlaceHolder(this.swingRenderer, this, method) { private static final long serialVersionUID = 1L; @Override public Component createMethodControl() { // Create your custom method control here return super.createMethodControl(); } }; } }; } }; swingRenderer.openObjectDialog(null, myObject, "'Upper Case' field control => toggle button"); } private static void useXmlCustomizations() { try { /* * InfoCustomizations can be serialized/deserialized to XML. */ InfoCustomizations customizations = new InfoCustomizations(); CustomizedUI customizedUI = new CustomizedUI(customizations); /* * Customize a field. */ TypeCustomization helloWorldTypeCustomization = InfoCustomizations.getTypeCustomization(customizations, HelloWorld.class.getName(), true); FieldCustomization nameFieldCustomization = InfoCustomizations .getFieldCustomization(helloWorldTypeCustomization, "name", true); nameFieldCustomization.setCustomFieldCaption( "(Caption modified through " + InfoCustomizations.class.getSimpleName() + " class) Name"); /* * Saving customizations to an XML stream. */ ByteArrayOutputStream memoryOutput = new ByteArrayOutputStream(); customizations.saveToStream(memoryOutput, ReflectionUIUtils.getDebugLogListener(customizedUI)); /* * Loading customizations from an XML stream. */ ByteArrayInputStream memoryInput = new ByteArrayInputStream(memoryOutput.toByteArray()); customizations.loadFromStream(memoryInput, ReflectionUIUtils.getDebugLogListener(customizedUI)); /* * Displaying XML serialization text. */ String message = "<!-- Check " + FileExplorer.class.getName() + ".java and " + Calculator.class.getName() + ".java examples for more information -->"; message += "\n\n" + memoryOutput.toString(); CustomizedSwingRenderer.getDefault().openInformationDialog(null, message, "XML customizations example", null); } catch (IOException e) { throw new AssertionError(e); } } private static void openObjectFrame() { /* * Opening an object frame: */ Object myObject = new HelloWorld(); SwingRenderer.getDefault().openObjectDialog(null, myObject, myObject + " Frame"); } } |
- CustomUI tutorial: <DISTRIBUTION_DIRECTORY>/examples/src/main/java/xy/reflect/ui/example/CustomUITutorial.java
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 |
/** * CustomUI library demonstration: Each of the following methods demonstrates a * feature of the library. * * @author olitank * */ package xy.reflect.ui.example; import java.io.File; import xy.reflect.ui.CustomizedUI; import xy.reflect.ui.control.swing.customizer.SwingCustomizer; import xy.reflect.ui.info.field.IFieldInfo; import xy.reflect.ui.info.type.ITypeInfo; import xy.reflect.ui.info.type.factory.InfoProxyFactory; import xy.reflect.ui.util.FileUtils; import xy.reflect.ui.util.MoreSystemProperties; import xy.reflect.ui.util.SystemProperties; public class CustomUITutorial { public static void main(String[] args) { openObjectDialog(); disableDesignMode(); changeCustomizationsFilePath(); addHandCodedCustomizations(); } private static void openObjectDialog() { /* * Most basic use case: opening an object dialog. Note that by default the * dialog will be in design mode. The customizations file will be stored by * default in the current diectory. */ Object myObject = new HelloWorld(); SwingCustomizer.getDefault().openObjectDialog(null, myObject, myObject + " Dialog in design mode"); } private static void disableDesignMode() { /* * Disable the design mode before opening the object dialog: */ System.setProperty(MoreSystemProperties.HIDE_INFO_CUSTOMIZATIONS_TOOLS, "true"); Object myObject = new HelloWorld(); SwingCustomizer.getDefault().openObjectDialog(null, myObject, "Desing mode disabled"); } private static void changeCustomizationsFilePath() { File tmpDirectory; try { tmpDirectory = FileUtils.createTempDirectory(); } catch (Exception e) { throw new AssertionError(e); } File customizationsFile = new File(tmpDirectory, "myFile.icu"); /* * The following system property can be used to change the customizations file * path: */ System.setProperty(SystemProperties.getDefaultInfoCustomizationsFilePath(), customizationsFile.getPath()); Object myObject = new HelloWorld(); SwingCustomizer.getDefault().openObjectDialog(null, myObject, "Customizations file => " + customizationsFile.getPath()); } private static void addHandCodedCustomizations() { /* * Hand-coded customizations can be added before or after the declarative * customizations application. */ CustomizedUI customizedUI = new CustomizedUI() { @Override protected ITypeInfo getTypeInfoBeforeCustomizations(ITypeInfo type) { return new InfoProxyFactory() { @Override protected String getCaption(IFieldInfo field, ITypeInfo containingType) { return "(added before customizations) " + super.getCaption(field, containingType); } }.wrapTypeInfo(super.getTypeInfoBeforeCustomizations(type)); } @Override protected ITypeInfo getTypeInfoAfterCustomizations(ITypeInfo type) { return new InfoProxyFactory() { @Override protected String getCaption(IFieldInfo field, ITypeInfo containingType) { return super.getCaption(field, containingType) + " (added after customizations)"; } }.wrapTypeInfo(super.getTypeInfoAfterCustomizations(type)); } }; SwingCustomizer renderer = new SwingCustomizer(customizedUI, SystemProperties.getDefaultInfoCustomizationsFilePath()); Object myObject = new HelloWorld(); renderer.openObjectDialog(null, myObject, "Hand-Coded customizations added"); } } |
The GUI builder Documentation
Type Customization
- Dispaly Style: Specifies how categories are displayed.
- Background Color: Allows to specify the background color of the categories control of this type form.
- Foreground Color: Allows to specify the text color of the categories control of this type form.
- Hide Undo/Redo Options (windows reloading required): If checked, undo/redo options will
not be available for objects of this type. - Include Persistence Methods (save, load): If chcecked, the current type persistence
methods (save() and load()) will be available
for objects of this type. - Include Oject Class Members (getClass, toString, equals, …): If chcecked, default object fieds and
methods (getClass(), toString(), …) will
be available for objects of this type. - Custom Type Caption (windows reloading required): Custom display name of this type.
If not set, a default display name is provided. - Immutable: If checked, this type objects will
be considered as immutable
and then not editable. - Abstract: If checked, this type objects will
be considered as abstract
and then not instanciable. - Fields Layout: Allows to change this type fields layout.
- Methods Layout: Allows to change this type methods layout.
- Default Form Width: Allows to specify the default width of this type form.
- Default Form Height: Allows to specify the default height of this type form.
- Form Background Color: Allows to specify the background color of this type form.
- Form Foreground Color: Allows to specify the text color of this type form.
- Form Border Color: Allows to specify the border color of this type form.
- Form Background Image Path: Allows to specify a background image of this type form.
- Form Editors Color: Allows to specify the background color of the editable parts of this type form.
- Form Editors Text Color: Allows to specify the text color of the editable parts of this type form.
- Form Button Background Color (windows reloading required to update the bottom button bar): Allows to specify the background color of the buttons of this type form.
- Form Button Foreground Color (windows reloading required to update the bottom button bar): Allows to specify the text color of the buttons of this type form.
- Form Button Border Color (windows reloading required to update the bottom button bar): Allows to specify the border color of the buttons of this type form.
- Form Button Background Image Path (windows reloading required to update the bottom button bar): Allows to specify the background image of the buttons of this type form.
- Online Help: Help message displayed for the type.
- Icon Image Path (windows reloading required): Icon image displayed for this type.
- Allow to Classify Fields and Methods: List of categories in which
fields and methods of this type
can be put in order to make this
type window more intuitive and
usable. - Specify (Additional) Sub-Types: List of sub-types declared for this type.
These will be proposed during the
instanciation of this type. - Customize Fields Display: List of discovered fields of this type.
- Declare Virtual Fields: List of virtual fields of this type.
- Customize Methods Display: List of methods of this type.
Field Customization
- Allow To Set Null: Specifies that this field display should
allows to set the null value. - Null Value Label: Text to be displayed when
this field value is null. - Default Value (Replace Null By): If specified, this value will be
displayed when this field value
is null. - Export Null Status: Specifies that a new boolean field
should be generated to reflect the
null status of this field. - Import Null Status: Specifies a boolean field that
should be used to simulate a
null value (when false) for this
field whwther or not this field
value is actually null or not. - Generate Display Method: Generates a method which return
value will be the value of this field. - Generate Update Method: Generates a method that will
allow to set the value of this field. - Use Custom Setter: Specifies a method that will be
used to set this field value. - Custom Field Caption: Allows to specify the display name of this field.
If not set a default display name is provided. - Hide: Allows to hide this field.
- Get-Only: Allows to specify that this field value
(the reference or the primitive value)
cannot be set. Note that it does not
mean that the field is read-only unless
its value is immutable. - Online Help: Allows to specify a help message
to be displayed for this field. - Category: Allows to specify the category in
which this field will be put.
Note that the possible categories
are declared at the type level. - Category: Allows to specify the category in
which this field will be put.
Note that the possible categories
are declared at the type level. - Use Generic Control: Forces this field to be displayed
by a generic control (a form or
a button that will display a child
dialog containing the form). - Expand Generic Control: Forces this field form to be embedded
(no child dialog access button will be
displayed).
Note that this is significant only if this field
value is displayed as a form. - Display Area Weights: Allows to specify the display weights
of this field control relatively to the
sibling field controls. - Encapsulate (Move Into A Sibling Sub-Form): Specifies the name of the generated
field that will encapsulate this field. - Convert: Specifies that this field value should
be converted and how the conversion
will be managed. - Value As Singleton List: Specifies that this field value
must be converted to a list
containing this value. - Enumerate Value Options: Specifies that this field value
will be chosen among enumerated
values, - Value Access Mode: Specifies the way the value of this field
is obtained. It will affect how the field
is updated and refreshed.
Typically calculated values cannot really
be modified and then are systematically
refreshed. - Duplicate: Allows to duplicate this field.
- Auto-Update: Specifies a periodic delay after which
this field value will be automatically
refreshed.
Method Customization
- Value Return Mode: Specifies the way the return value of this method
is obtained. It will affect how this value
is updated and refreshed.
Typically calculated values cannot really
be modified and then are systematically
refreshed. - Confirmation Message: If provided, then a confirmation message
will be displayed before performimg the
method action. - Parameters Validation Custom Caption: Allows to change the ‘OK’ butoon
text of this method parameters
dialog. - Generate Return Value Field: Allows to generate a field that
will reflect the return value of
this method. - Menu Location: Allows to choose a menu location
for this method.
Note that menu locations are
declared at the type level. - Null Return Value Label: Allows to specify a text to be used
when displaying null return values
of this method. - Move Into Sub-Form: Specifies the name of the generated
field that will encapsulate this method. - Custom Method Caption: The display name of this method.
If not set, a default display name
is provided. - Hide: Allows to hide this method.
- Read-Only: Allows to specify if this method
is read-only or not. Read-only
methods will not affect the undo
management. - Presets: Allows to generate methods from
this one that will use predefined
sets of parameter values. - Fields Displayed As Parameters: Allows to specify a list of parameters
of this method that will be displayed
as sibling fields. - Online Help: Allows to display a help message
for this method. - Icon Image Path: Specifies a icon image to be used
for the display of this method. - Category: Allows to specify the category in
which this method will be put.
Note that the possible categories
are declared at the type level. - Category: Allows to specify the category in
which this method will be put.
Note that the possible categories
are declared at the type level. - Detached Return Value: If checked, then the return value
dialog will not be blocking. - Ignore Return Value: If checked, then the return value
dialog will not be displayed. - Validating: Specifies that this method should
be used to validate the owner
object. - Duplicate: Allows to duplicate this method.
- Run When Instance Shown: If chcecked, then this method
will be executed when the owner
object is shown. - Run When Instance Hidden: If chcecked, then this method
will be executed when the owner
object is hidden.
List Customization
- Allow To Select Item Constructor: Specifies that the item constructor
should be asked when the user
requests the instanciation of an item.
Note that known sub-classes
constructors will be proposed too. - Disable Details View: Allows to remove the ability
to view item details. - Item Field Shortcuts: Allows to specify item fields that will
be quickly editable from the toolbar
when the the corresponding item is
selected. - Item Method Shortcuts: Allows to specify item methods that will
be quickly accessible from the toolbar
when the the corresponding item is
selected. - Fields Excluded From Details View: Allows to specify item fields that will
be excluded from the details view.
IMPORTANT: the field name is
expected for each field to exclude. - Methods Excluded From Details View: Allows to specify item methods that will
be excluded from the details view.
IMPORTANT: the method signature is
expected for each method to exclude. - Details View Mode: Allows to select the layout of
the item details view. - Add Field Columns: Allows to add columns from
the item type fields. - Add Item Type Column: Allows to add a column containing
the item type name. - Add Item position Column: Allows to add a column containing
the item position. - Add toString() Column: Allows to add a column containing
the item toString() return value. - Customize Columns Display: Lists discovered customizable columns.
- Auto-Discover Tree Structure: Allows to display the tree structure
(sub-lists) of the list. Ideally the
caption of the fields holding the
sub-lists should be the same as
the caption of the root field. - Editable: Specifies if this list is editable
and at what extent. - Custom Length: Specifies a custom length (vertical)
of this list. - Sort Items: SPecifies that items of this list
should be sorted.
Enumeration Customization
- Dynamic Enumeration: If chcecked, then the enumerated
value list will be regularly refreshed. - Customize Items: List discovered customizable enumeration items.