Awesome
<img align="left" src="durian-swt.png"> DurianSwt: Reactive utilities and fluent builders for SWT
<!---freshmark shields output = [ link(shield('Maven central', 'mavencentral', 'com.diffplug.durian:durian-swt', 'blue'), 'https://search.maven.org/artifact/com.diffplug.durian/durian-swt'), link(shield('Apache 2.0', 'license', 'apache-2.0', 'blue'), 'https://tldrlegal.com/license/apache-license-2.0-(apache-2.0)'), '', link(shield('Changelog', 'changelog', versionLast, 'brightgreen'), 'CHANGES.md'), link(shield('Javadoc', 'javadoc', 'yes', 'brightgreen'), 'https://javadoc.io/static/com.diffplug.durian/durian-swt/{{versionLast}}/overview-summary.html'), link(shield('Live chat', 'gitter', 'chat', 'brightgreen'), 'https://gitter.im/diffplug/durian'), link(image('Travis CI', 'https://travis-ci.org/diffplug/durian-swt.svg?branch=master'), 'https://travis-ci.org/diffplug/durian-swt'), ].join('\n'); --> <!---freshmark /shields --> <!---freshmark javadoc output = prefixDelimiterReplace(input, 'https://javadoc.io/static/com.diffplug.durian/durian-swt/', '/', versionLast); -->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
- anExecutorService
which executes on the SWT thread.SwtExec.Guarded
- anExecutorService
which is tied to the lifetime of an SWT widget. Say goodbye toSWTException: 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();
-
Actions
- builder and one-liner:Actions.create("Redo", this::redo);
-
LabelProviders
- builder and one-liner:LabelProviders.createWithText(Person::getName)
-
ColumnFormat
andColumnViewerFormat
- tables and trees without boilerplate
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.
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
- aTreeDef
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
, andSwtPlatform
- 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
- Thanks to David Karnok for contributing an SwtScheduler that honors the Scheduler/Worker contracts.
- Thanks to Moritz Post for his fluent layout idea.
- Formatted by spotless, as such.
- Bugs found by findbugs, as such.
- OSGi metadata generated by JRuyi's [osgibnd-gradle-plugin] (https://github.com/jruyi/osgibnd-gradle-plugin), which leverages Peter Kriens' bnd.
- Scripts in the
.ci
folder are inspired by Ben Limmer's work. - Built by gradle.
- Tested by junit.
- Maintained by DiffPlug.