Skip to content

diffplug/durian-swt

Folders and files

NameName
Last commit message
Last commit date

Latest commit

 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 

Repository files navigation

DurianSwt: Reactive utilities and fluent builders for SWT

Maven central Apache 2.0

Changelog Javadoc Live chat Travis CI

Infrastructure

  • ControlWrapper - create custom widgets which properly encapsulate their base control.
  • Coat - a functional interface for populating an empty Composite.
  • CoatMux - a mechanism for layering and swapping Coats.
  • SwtExec - an ExecutorService which executes on the SWT thread.
  • SwtExec.Guarded - an ExecutorService which is tied to the lifetime of an SWT widget. Say goodbye to SWTException: Widget is disposed forever! It can also subscribe to any kind of observable (Guava's ListenableFuture or RxJava's Observable), see DurianRx for more info.
SwtExec.async().guardOn(textBox).subscribe(serverResponse, txt -> {
  textBox.setText(txt);
});

Fluent builders

  • Layouts - all the layouts you'll need in SWT
void textOkCanel(Composite cmp) {
  Layouts.setGrid(cmp).numColumns(3);

  // instructions fill the full width
  Text text = new Text(cmp, SWT.WRAP);
  Layouts.setGridData(text).horizontalSpan(3).grabAll();

  // right-justified ok / cancel buttons
  Layouts.newGridPlaceholder(cmp).grabHorizontal();
  Button btnOk = new Button(cmp, SWT.PUSH);
  Layouts.setGridData(btn).widthHint(SwtMisc.defaultButtonWidth());
  Button btnCancel = new Button(cmp, SWT.PUSH);
  Layouts.setGridData(btn).widthHint(SwtMisc.defaultButtonWidth());
}
  • Shells - dialogs without boilerplate
Shells.builder(SWT.DIALOG_TRIM, this::textOkCanel)
  .setTitle("Confirm operation")
  .setSize(SwtMisc.defaultDialogWidth(), 0) // set the width, pack height to fit contents
  .openOnDisplayBlocking();
ColumnViewerFormat<Person> format = ColumnViewerFormat.builder();
format.setStyle(SWT.SINGLE | SWT.FULL_SELECTION);
format.addColumn().setText("First").setLabelProviderText(Person::getFirstName);
format.addColumn().setText("Last").setLabelProviderText(Person::getLastName);
format.addColumn().setText("Age").setLabelProviderText(p -> Integer.toString(p.getAge())).setLayoutPixel(3 * SwtMisc.systemFontWidth());
TableViewer table = format.buildTable(parent);
TreeViewer tree = format.buildTree(parent);

Resource management

  • OnePerWidget - a cache tied to the lifetime of an SWT Widget.
  • ColorPool - a pool of colors tied to the lifetime of a widget. ColorPool.forWidget(widget).getColor(rgbValue)
  • ImageDescriptors - use ImageDescriptors with proper resource sharing. ImageDescriptors.set(btn, imageDescriptor)

Interactive testing

Ideally, all UI code would have fully automated UI testing, but such tests are time-consuming to write, so they often just don't get written at all. InteractiveTest bridges the gap by making it easy to write user-in-the-loop guided tests. Furthermore, these tests can even be run in a headless enviroment on a CI server, where the test UI will be opened, then automatically closed after a timeout. This ensures that the tests are all in working order and ready for a human tester to do final validation.

InteractiveTest

From ViewerMiscTest.java:

String message = StringPrinter.buildStringFromLines(
  "- The table and the tree should keep their selection in sync.",
  "- The table and the tree should not allow multi-selection.",
  "- The categories in the tree should not be selectable.");
InteractiveTest.testCoat(message, cmp -> {
  TableAndTree tableAndTree = new TableAndTree(cmp, SWT.SINGLE);

  // get the selection of the tree
  RxBox<Optional<TreeNode<String>>> treeSelection = ViewerMisc.<TreeNode<String>> singleSelection(tableAndTree.tree)
      // only names can be selected - not categories
      .enforce(opt -> opt.map(val -> isName(val) ? val : null));

  // sync the tree and the table
  RxOptional<TreeNode<String>> tableSelection = ViewerMisc.singleSelection(tableAndTree.table);
  Rx.subscribe(treeSelection, tableSelection::set);
  Rx.subscribe(tableSelection, treeSelection::set);
});

Miscellaneous stuff

  • SwtMisc - useful static methods.
    • blockForError, blockForSuccess, blockForQuestion, etc. - opens a dialog and blocks for the user's response, can be called from any thread.
    • loopUntil, loopUntilDisposed, loopUntilGet - spins the SWT display loop until some condition is satisfied.
    • systemFontHeight/Width, scaleByFont, scaleByFontHeight - resolution-independent sizes.
    • treeDefControl, treeDefComposite - a TreeDef for traversing UI elements.
    • setEnabledDeep - sets the enabled status of every child, grandchild, etc. of the given composite.
  • SwtRx - methods for converting SWT events and models to RxJava Observables.
  • SwtDebug - utilities for debugging SWT events.
  • OS, Arch, and SwtPlatform - detect things about the running system, and manipulate the SWT jars for build tools.
    • These do not require SWT or JFace, so you can add DurianSwt to your gradle or maven dependencies without needing to also figure out the SWT messiness.
    • You can also just copy-paste these straight into your own code - they have no external dependencies.
String installerExtension = OS.getNative().winMacLinux("exe","dmg","sh");
String helperBinary = "driver_" + Arch.getNative().x86x64("", "_64") + ".dll";
String swtJarName = "org.eclipse.swt." + SwtPlatform.getRunning();
  • ViewerMisc - useful static methods for JFace viewers.
    • singleSelection, multiSelection - returns an RxBox for listening to and setting the selection of a viewer.
    • setTreeContentProvider, setLazyTreeContentProvider - uses a TreeDef to provide the content of a TreeViewer.

Requirements

Durian requires:

  • Java 8
  • Durian and DurianRx
  • Guava and RxJava
  • SWT and JFace from Eclipse 4.4+
    • SWT and JFace are not included in the Maven POM, but everything else is.

Acknowledgements