durian-swt
durian-swt copied to clipboard
Reactive utilities and fluent builders for SWT
DurianSwt: Reactive utilities and fluent builders for SWT
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.