Basic Usage
1 |
SwingCustomizer.getDefault().openObjectFrame(myObject); |
Disable the design mode by setting this system property:
-Dxy.reflect.ui.infoCustomizationsToolsHidden=true
Default Operation
- Only public fields and methods are considered.
- A public getter (public <Type> (get|is|has)Something()) and its eventual associated public setter (public void setSomething(<Type> object)) are considered as a unique public field (public <Type> something).
- A final field or a getter without an associated setter are considered as a “get-only” field that may not be editable (depends on the field type).
- A field control (text component, list view, combo-box, …) is generated for each field.
- A button is generated for each method.
- Field changes can be undone.
- Method calls invalidate the modification stack and cannot be undone. Note that IMethodInfo.getNextInvocationUndoJob(…) or IMethodInfo.isReadOnly() can be overridden in order to make a method call undoable or harmless for the modification stack.
Architecture
The GUI is generated in 2 steps:
- The class information of each object is read and interpreted by the ReflectionUI object. The result is an abstract GUI model: ITypeInfo.
- Then this abstract GUI model is passed to the engine that will create the various GUI controls displaying the object values: the SwingRenderer.
(ReflectionUI) | (SwingRenderer) | |||
Class | ——————–> | ITypeInfo | ——————–> | GUI |
Note that the SwingRenderer creates Swing-based GUIs. This is the default renderer but other renderers (JavaFX-based, SWT-based, …) could be developed and plugged.
IMPORTANT: Swing is not (and will not be) obsolete even if JavaFX seems to be its replacement. Since 2020 we know that Swing might even survive longer than JavaFX. From the official Oracle Java Client Roadmap:
|
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 GUI 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 its 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) | ——————–> | GUI |
^ | ||||||
|_______ | ____| |
The GUI builder Online Documenation
Since the version 4.6.0 the GUI builder has an online documentation:
Demonstration Videos
Video | Covered Subjects |
---|---|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
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 88 89 90 |
package xy.reflect.ui.example; import java.io.Serializable; import java.util.ArrayList; import java.util.List; import javax.swing.SwingUtilities; import xy.reflect.ui.control.swing.customizer.SwingCustomizer; /** * Example showing a basic usage of the ReflectionUI library. * * @author olitank * */ public class HelloWorld implements Serializable { public static void main(String[] args) { SwingUtilities.invokeLater(new Runnable() { @Override public void run() { 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 |
/** * 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.util.ArrayList; import java.util.Arrays; import java.util.List; import javax.swing.JOptionPane; import javax.swing.JToggleButton; import javax.swing.SwingUtilities; 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.FileBrowserPlugin.FileBrowserConfiguration; import xy.reflect.ui.control.swing.plugin.FileBrowserPlugin.FileNameFilterConfiguration; import xy.reflect.ui.control.swing.plugin.FileBrowserPlugin.SelectionModeConfiguration; import xy.reflect.ui.control.swing.plugin.OptionButtonsPlugin; import xy.reflect.ui.control.swing.plugin.OptionButtonsPlugin.OptionButtonsConfiguration; 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.control.swing.util.SwingRendererUtils; 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.field.FieldInfoProxy; import xy.reflect.ui.info.field.IFieldInfo; import xy.reflect.ui.info.filter.IInfoFilter; import xy.reflect.ui.info.menu.MenuInfo; import xy.reflect.ui.info.menu.MenuItemCategory; import xy.reflect.ui.info.menu.MenuModel; import xy.reflect.ui.info.menu.StandradActionMenuItemInfo; 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; public class ReflectionUITutorial { public static void main(String[] args) { SwingUtilities.invokeLater(new Runnable() { @Override public void run() { openObjectDialog(); createObjectForm(); changeFieldsAndMethodOrder(); allowToSetNull(); hideSomeFieldsAndMethods(); addVirtualFieldsAndMethods(); categorizeFieldsAndMethods(); customizeCopyCutPasteFeature(); customizeColors(); addMenus(); useFieldControlPlugins(); createCustomControls(); 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 buildTypeInfo(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.buildTypeInfo(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 buildTypeInfo(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.buildTypeInfo(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 buildTypeInfo(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); } }.wrapTypeInfo(super.buildTypeInfo(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 buildTypeInfo(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 getSignature() { return ReflectionUIUtils.buildMethodSignature(this); } @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().buildTypeInfo( new JavaTypeInfoSource(ReflectionUI.getDefault(), 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.buildTypeInfo(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 buildTypeInfo(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 (horizontal, * vertical, ...): */ @Override protected CategoriesStyle getCategoriesStyle(ITypeInfo type) { return CategoriesStyle.MODERN; } }.wrapTypeInfo(super.buildTypeInfo(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 buildTypeInfo(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.buildTypeInfo(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 buildTypeInfo(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(); { MenuInfo fileMenu = new MenuInfo("File"); { /* * Some standards menu items are provided by the framework (open file, save * file, quit, undo, redo, ...). See the * 'xy.reflect.ui.info.menu.StandradActionMenuItemInfo.Type' enum for more * information. 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. */ FileBrowserConfiguration fileBrowserConfiguration = new FileBrowserConfiguration(); { fileBrowserConfiguration.selectionMode = SelectionModeConfiguration.FILES_ONLY; FileNameFilterConfiguration fileNameFilterConfiguration = new FileNameFilterConfiguration(); { fileNameFilterConfiguration.description = "Hello World Files"; fileNameFilterConfiguration.extensions.add("hello"); } fileBrowserConfiguration.fileNameFilters.add(fileNameFilterConfiguration); } persistenceCategory.addItem(new StandradActionMenuItemInfo( "Open Hello World File...", null, StandradActionMenuItemInfo.Type.OPEN, fileBrowserConfiguration)); persistenceCategory.addItem(new StandradActionMenuItemInfo( "Save Hello World File As...", null, StandradActionMenuItemInfo.Type.SAVE_AS, fileBrowserConfiguration)); fileMenu.addItemCategory(persistenceCategory); } MenuItemCategory lifeCycleCategory = new MenuItemCategory("Life Cycle"); { lifeCycleCategory.addItem(new StandradActionMenuItemInfo("Quit", null, StandradActionMenuItemInfo.Type.EXIT)); fileMenu.addItemCategory(lifeCycleCategory); } } menuModel.setMenus(Arrays.asList(fileMenu)); } return menuModel; } else { return super.getMenuModel(type); } } }.wrapTypeInfo(super.buildTypeInfo(typeInfoSource)); } }; SwingRenderer swingRenderer = new SwingRenderer(reflectionUI); swingRenderer.openObjectDialog(null, myObject, "With File Menu"); } private static void useFieldControlPlugins() { Object myObject = new HelloWorld(); /* * Control plugins provide 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 buildTypeInfo(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.buildTypeInfo(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 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, 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, 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 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 106 107 108 109 110 111 112 |
/** * 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 javax.swing.SwingUtilities; 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.IOUtils; import xy.reflect.ui.util.MoreSystemProperties; import xy.reflect.ui.util.SystemProperties; public class CustomUITutorial { public static void main(String[] args) { SwingUtilities.invokeLater(new Runnable() { @Override public void run() { 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 = IOUtils.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"); } } |