Commit 6dea4a05 authored by Romain Bioteau's avatar Romain Bioteau

feat(ui-toolkit) create fluent api & builders to ease ui dev (#230)

parent 8985fa0f
......@@ -588,4 +588,11 @@ along with this program. If not, see <http://www.gnu.org/licenses/>.
version="0.0.0"
unpack="false"/>
<plugin
id="org.bonitasoft.studio.ui"
download-size="0"
install-size="0"
version="0.0.0"
unpack="false"/>
</feature>
......@@ -14,6 +14,7 @@
*/
package org.bonitasoft.studio.pics;
import java.io.InputStream;
import java.net.MalformedURLException;
import java.net.URL;
import java.util.Locale;
......@@ -25,6 +26,8 @@ import org.eclipse.swt.graphics.Cursor;
import org.eclipse.swt.graphics.GC;
import org.eclipse.swt.graphics.Image;
import org.eclipse.swt.graphics.ImageData;
import org.eclipse.swt.graphics.PaletteData;
import org.eclipse.swt.graphics.RGB;
import org.eclipse.swt.widgets.Display;
import org.eclipse.ui.ISharedImages;
import org.eclipse.ui.PlatformUI;
......@@ -62,7 +65,11 @@ public class Pics extends AbstractUIPlugin {
*/
@Override
public ImageData getImageData() {
return new ImageData(Pics.class.getResourceAsStream(file));
InputStream resourceAsStream = Pics.class.getResourceAsStream(file);
if (resourceAsStream != null) {
return new ImageData(resourceAsStream);
}
return new ImageData(1, 1, 16, new PaletteData(new RGB[] {}));
}
}
......
<?xml version="1.0" encoding="UTF-8"?>
<classpath>
<classpathentry kind="con" path="org.eclipse.jdt.launching.JRE_CONTAINER/org.eclipse.jdt.internal.debug.ui.launcher.StandardVMType/JavaSE-1.8"/>
<classpathentry kind="con" path="org.eclipse.pde.core.requiredPlugins"/>
<classpathentry kind="src" path="src"/>
<classpathentry kind="src" path="src-test/java"/>
<classpathentry kind="output" path="bin"/>
</classpath>
<?xml version="1.0" encoding="UTF-8"?>
<projectDescription>
<name>org.bonitasoft.studio.ui</name>
<comment></comment>
<projects>
</projects>
<buildSpec>
<buildCommand>
<name>org.eclipse.jdt.core.javabuilder</name>
<arguments>
</arguments>
</buildCommand>
<buildCommand>
<name>org.eclipse.pde.ManifestBuilder</name>
<arguments>
</arguments>
</buildCommand>
<buildCommand>
<name>org.eclipse.pde.SchemaBuilder</name>
<arguments>
</arguments>
</buildCommand>
</buildSpec>
<natures>
<nature>org.eclipse.pde.PluginNature</nature>
<nature>org.eclipse.jdt.core.javanature</nature>
</natures>
</projectDescription>
Manifest-Version: 1.0
Bundle-ManifestVersion: 2
Bundle-Name: UI Toolkit Plug In
Bundle-SymbolicName: org.bonitasoft.studio.ui
Bundle-Version: 7.4.0.qualifier
Bundle-Vendor: BonitaSoft S.A.
Bundle-RequiredExecutionEnvironment: JavaSE-1.8
Require-Bundle: org.eclipse.jface;bundle-version="3.10.1",
org.eclipse.core.runtime;bundle-version="3.10.0",
org.eclipse.core.databinding;bundle-version="1.4.2",
org.eclipse.jface.databinding,
org.eclipse.core.databinding.beans,
org.eclipse.core.databinding.property;bundle-version="1.4.200",
org.junit;bundle-version="4.11.0";resolution:=optional,
org.bonitasoft.studio.tests-utils;bundle-version="7.4.0";resolution:=optional,
assertj-core;bundle-version="1.5.0";resolution:=optional
Export-Package: org.bonitasoft.studio.ui.databinding,
org.bonitasoft.studio.ui.validator,
org.bonitasoft.studio.ui.widget,
org.bonitasoft.studio.ui.wizard
source.. = src/
output.. = bin/
bin.includes = META-INF/,\
.
src.excludes = src-test/java/
<?xml version="1.0" encoding="UTF-8"?>
<project
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd"
xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance">
<modelVersion>4.0.0</modelVersion>
<parent>
<groupId>org.bonitasoft.studio.bundles</groupId>
<artifactId>plugins</artifactId>
<version>7.4.0-SNAPSHOT</version>
</parent>
<groupId>org.bonitasoft.studio.bundles.plugins</groupId>
<artifactId>org.bonitasoft.studio.ui</artifactId>
<packaging>eclipse-plugin</packaging>
</project>
/**
* Copyright (C) 2016 Bonitasoft S.A.
* Bonitasoft, 32 rue Gustave Eiffel - 38000 Grenoble
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 2.0 of the License, or
* (at your option) any later version.
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
package org.bonitasoft.studio.ui.validator;
import org.bonitasoft.studio.assertions.StatusAssert;
import org.eclipse.core.runtime.IStatus;
import org.junit.Test;
public class EmptyInputValidatorTest {
@Test
public void should_fails_with_given_error_message_input_is_null() throws Exception {
final EmptyInputValidator validator = new EmptyInputValidator.Builder()
.withMessage("null value")
.create();
final IStatus status = validator.validate(null);
StatusAssert.assertThat(status).isNotOK().hasMessage("null value");
}
@Test
public void should_fails_with_given_error_message_input_is_empty() throws Exception {
final EmptyInputValidator validator = new EmptyInputValidator.Builder()
.withMessage("empty value")
.create();
final IStatus status = validator.validate("");
StatusAssert.assertThat(status).isNotOK().hasMessage("empty value");
}
@Test
public void should_fails_with_given_error_message_input_is_whitespace_only() throws Exception {
final EmptyInputValidator validator = new EmptyInputValidator.Builder()
.withMessage("empty value")
.create();
final IStatus status = validator.validate(" ");
StatusAssert.assertThat(status).isNotOK().hasMessage("empty value");
}
@Test
public void should_fails_with_given_severity() throws Exception {
final EmptyInputValidator validator = new EmptyInputValidator.Builder()
.withMessage("empty value")
.warningLevel()
.create();
final IStatus status = validator.validate(" ");
StatusAssert.assertThat(status).isNotOK().hasSeverity(IStatus.WARNING).hasMessage("empty value");
}
}
/**
* Copyright (C) 2016 Bonitasoft S.A.
* Bonitasoft, 32 rue Gustave Eiffel - 38000 Grenoble
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 2.0 of the License, or
* (at your option) any later version.
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
package org.bonitasoft.studio.ui.validator;
import org.bonitasoft.studio.assertions.StatusAssert;
import org.eclipse.core.runtime.IStatus;
import org.junit.Test;
public class LengthValidatorTest {
@Test
public void should_fails_with_given_error_message_input_is_too_long() throws Exception {
final LengthValidator validator = new LengthValidator.Builder()
.withMessage("'%v' is too long (max = %max)")
.maxLength(10)
.create();
final IStatus status = validator.validate("I'm too long man");
StatusAssert.assertThat(status).isNotOK().hasMessage("'I'm too long man' is too long (max = 10)");
}
@Test
public void should_fails_with_given_error_message_input_is_too_short() throws Exception {
final LengthValidator validator = new LengthValidator.Builder()
.withMessage("'%v' must have at least %min characters")
.minLength(8)
.create();
final IStatus status = validator.validate("short");
StatusAssert.assertThat(status).isNotOK().hasMessage("'short' must have at least 8 characters");
}
}
/**
* Copyright (C) 2016 Bonitasoft S.A.
* Bonitasoft, 32 rue Gustave Eiffel - 38000 Grenoble
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 2.0 of the License, or
* (at your option) any later version.
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
package org.bonitasoft.studio.ui.validator;
import org.bonitasoft.studio.assertions.StatusAssert;
import org.eclipse.core.databinding.validation.ValidationStatus;
import org.eclipse.core.runtime.IStatus;
import org.eclipse.core.runtime.MultiStatus;
import org.junit.Test;
public class MultiValidatorTest {
@Test
public void should_fails_validation_if_one_of_the_validator_fail() throws Exception {
final MultiValidator multiValidator = new MultiValidator.Builder()
.havingValidators(v -> ValidationStatus.ok(), v -> ValidationStatus.error("oups"))
.create();
final IStatus status = multiValidator.validate("Hello");
StatusAssert.assertThat(status).isNotOK();
}
@Test
public void should_succeed_validation_if_all_of_the_validator_are_in_success() throws Exception {
final MultiValidator multiValidator = new MultiValidator.Builder()
.havingValidators(v -> ValidationStatus.ok(), v -> ValidationStatus.ok())
.create();
final IStatus status = multiValidator.validate("Hello");
StatusAssert.assertThat(status).isOK();
}
@Test
public void should_collect_all_statuses_in_a_MultiStatus() throws Exception {
final MultiValidator multiValidator = new MultiValidator.Builder()
.havingValidators(v -> ValidationStatus.ok(),
v -> ValidationStatus.warning("careful"),
v -> ValidationStatus.error("error"))
.create();
final IStatus status = multiValidator.validate("Hello");
StatusAssert.assertThat(status).isInstanceOf(MultiStatus.class);
StatusAssert.assertThat(status).hasChildren(ValidationStatus.ok(),
ValidationStatus.warning("careful"), ValidationStatus.error("error"));
}
}
/**
* Copyright (C) 2016 Bonitasoft S.A.
* Bonitasoft, 32 rue Gustave Eiffel - 38000 Grenoble
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 2.0 of the License, or
* (at your option) any later version.
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
package org.bonitasoft.studio.ui.validator;
import org.bonitasoft.studio.assertions.StatusAssert;
import org.eclipse.core.runtime.IStatus;
import org.junit.Test;
public class RegExpValidatorTest {
@Test
public void should_fails_with_given_error_message_when_input_does_not_match_regExp() throws Exception {
final RegExpValidator validator = new RegExpValidator.Builder()
.withMessage("'%v' cannot contains whitespace")
.matches("^[^\\d\\s]*$")
.create();
final IStatus status = validator.validate("I've got white space");
StatusAssert.assertThat(status).isNotOK().hasMessage("'I've got white space' cannot contains whitespace");
}
@Test
public void should_not_fail_when_input_matches_regExp() throws Exception {
final RegExpValidator validator = new RegExpValidator.Builder()
.withMessage("'%v' cannot contains whitespace")
.matches("^[^\\d\\s]*$")
.create();
final IStatus status = validator.validate("I'veGotAnyNospace");
StatusAssert.assertThat(status).isOK();
}
}
/**
* Copyright (C) 2016 Bonitasoft S.A.
* Bonitasoft, 32 rue Gustave Eiffel - 38000 Grenoble
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 2.0 of the License, or
* (at your option) any later version.
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
package org.bonitasoft.studio.ui.wizard;
import static org.bonitasoft.studio.ui.databinding.UpdateStrategyFactory.updateValueStrategy;
import static org.bonitasoft.studio.ui.wizard.WizardBuilder.newWizard;
import static org.bonitasoft.studio.ui.wizard.WizardPageBuilder.newPage;
import org.bonitasoft.studio.swt.rules.RealmWithDisplay;
import org.bonitasoft.studio.ui.validator.EmptyInputValidator;
import org.bonitasoft.studio.ui.widget.ComboWidget;
import org.bonitasoft.studio.ui.widget.TextWidget;
import org.eclipse.core.databinding.DataBindingContext;
import org.eclipse.core.databinding.beans.PojoObservables;
import org.eclipse.core.databinding.validation.ValidationStatus;
import org.eclipse.jface.dialogs.MessageDialog;
import org.eclipse.jface.layout.GridDataFactory;
import org.eclipse.jface.layout.GridLayoutFactory;
import org.eclipse.swt.SWT;
import org.eclipse.swt.widgets.Composite;
import org.eclipse.swt.widgets.Display;
import org.junit.Rule;
import org.junit.Test;
public class WizardBuilderTest {
class Person {
private String name;
private String title;
private String manager;
public void setName(String name) {
this.name = name;
}
public String getName() {
return name;
}
public void setManager(String manager) {
this.manager = manager;
}
public String getManager() {
return manager;
}
public String getTitle() {
return title;
}
public void setTitle(String title) {
this.title = title;
}
}
@Rule
public RealmWithDisplay rule = new RealmWithDisplay();
@Test
public void should_create_a_new_wizard() throws Exception {
final Person person = new Person();
person.setTitle("Mr");
newWizard()
.withTitle("My Wizard Window")
.havingPage(newPage()
.withTitle("Page1")
.withDescription("Desc1")
.withDatabindingContext(() -> new DataBindingContext())
.withControl((parent, ctx) -> {
final Composite container = new Composite(parent, SWT.NONE);
container.setLayout(GridLayoutFactory.fillDefaults().numColumns(1).margins(15, 10).create());
new TextWidget.Builder()
.withLabel("Name")
.withLabelWidth(100)
.withLayoutData(GridDataFactory.fillDefaults().grab(true, false).create())
.bindTo(PojoObservables.observeValue(person, "name"))
.inContext(ctx)
.withTargetToModelStrategy(
updateValueStrategy().withValidator(new EmptyInputValidator.Builder().withMessage("Name is mandatory").create()))
.createIn(container);
new TextWidget.Builder()
.withPlaceholder("Mr,Miss")
.withLabel("Title")
.withLabelWidth(100)
.withMessage("Example: Mr, Ms..")
.fill()
.grabHorizontalSpace()
.readOnly()
.bindTo(PojoObservables.observeValue(person, "title"))
.inContext(ctx)
.withTargetToModelStrategy(
updateValueStrategy()
.withValidator(new EmptyInputValidator.Builder().withMessage("Title is mandatory").warningLevel().create()))
.createIn(container);
new ComboWidget.Builder()
.withLabel("Manager With Super long label")
.withLabelWidth(100)
.labelAbove()
.readOnly()
.withMessage("Choose the manager of this employee")
.withItems("", "Romain", "Toto")
.withLayoutData(GridDataFactory.fillDefaults().grab(true, false).create())
.bindTo(PojoObservables.observeValue(person, "manager"))
.inContext(ctx)
.withTargetToModelStrategy(updateValueStrategy().withValidator(value -> {
return value == null || ((String) value).isEmpty() ? ValidationStatus.warning("Manager is missing")
: ValidationStatus.ok();
}))
.createIn(container);
return container;
}))
.onFinish(() -> {
return MessageDialog.openConfirm(Display.getDefault().getActiveShell(), String.format("Create %s ?", person.getName()),
"A new person will be added into the contact list.");
})/* .open(rule.getShell()) */;
}
}
/**
* Copyright (C) 2016 Bonitasoft S.A.
* Bonitasoft, 32 rue Gustave Eiffel - 38000 Grenoble
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 2.0 of the License, or
* (at your option) any later version.
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
package org.bonitasoft.studio.ui.databinding;
import java.util.Objects;
import org.eclipse.core.databinding.ValidationStatusProvider;
import org.eclipse.core.databinding.observable.DisposeEvent;
import org.eclipse.core.databinding.observable.IDisposeListener;
import org.eclipse.core.databinding.observable.list.IObservableList;
import org.eclipse.core.databinding.observable.value.IObservableValue;
import org.eclipse.core.databinding.observable.value.IValueChangeListener;
import org.eclipse.core.databinding.observable.value.ValueChangeEvent;
import org.eclipse.core.runtime.Assert;
import org.eclipse.core.runtime.IStatus;
public abstract class ControlMessageSupport {
private IObservableValue validationStatus;
private IObservableList targets;
private IDisposeListener disposeListener = new IDisposeListener() {
@Override
public void handleDispose(DisposeEvent staleEvent) {
dispose();
}
};
private IValueChangeListener statusChangeListener = new IValueChangeListener() {
@Override
public void handleValueChange(ValueChangeEvent event) {
if (statusHasChanged(event)) {
statusChanged((IStatus) validationStatus.getValue());
}
}
};
public ControlMessageSupport(ValidationStatusProvider validationStatusProvider) {
this.validationStatus = validationStatusProvider.getValidationStatus();
Assert.isTrue(!this.validationStatus.isDisposed());
this.targets = validationStatusProvider.getTargets();
Assert.isTrue(!this.targets.isDisposed());
validationStatus.addDisposeListener(disposeListener);
validationStatus.addValueChangeListener(statusChangeListener);
}
protected boolean statusHasChanged(ValueChangeEvent event) {
final IStatus newStatus = (IStatus) event.diff.getNewValue();
final IStatus oldStatus = (IStatus) event.diff.getOldValue();
return !Objects.equals(oldStatus, newStatus);
}
protected abstract void statusChanged(IStatus status);
public void dispose() {
if (validationStatus != null) {
validationStatus.removeDisposeListener(disposeListener);
validationStatus.removeValueChangeListener(statusChangeListener);
validationStatus = null;
}
if (targets != null) {
targets.removeDisposeListener(disposeListener);
targets = null;
}
disposeListener = null;
statusChangeListener = null;
}
}
/**
* Copyright (C) 2016 Bonitasoft S.A.
* Bonitasoft, 32 rue Gustave Eiffel - 38000 Grenoble
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 2.0 of the License, or
* (at your option) any later version.
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
package org.bonitasoft.studio.ui.databinding;
import org.eclipse.core.databinding.DataBindingContext;
import org.eclipse.core.runtime.IStatus;
import org.eclipse.jface.databinding.dialog.DialogPageSupport;
import org.eclipse.jface.wizard.WizardPage;
public class NoMessageWizardPageSupport extends DialogPageSupport {
private NoMessageWizardPageSupport(WizardPage wizardPage, DataBindingContext dbc) {
super(wizardPage, dbc);
}
public static NoMessageWizardPageSupport create(WizardPage wizardPage,
DataBindingContext dbc) {
return new NoMessageWizardPageSupport(wizardPage, dbc);
}
@Override
protected void handleStatusChanged() {
boolean pageComplete = true;
if (currentStatusStale) {
pageComplete = false;
} else if (currentStatus != null) {
pageComplete = !currentStatus.matches(IStatus.ERROR
| IStatus.CANCEL);
}
((WizardPage) getDialogPage()).setPageComplete(pageComplete);
}
}
/**
* Copyright (C) 2014 BonitaSoft S.A.
* BonitaSoft, 32 rue Gustave Eiffel - 38000 Grenoble
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 2.0 of the License, or
* (at your option) any later version.
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
package org.bonitasoft.studio.ui.databinding;
import org.eclipse.core.databinding.UpdateValueStrategy;
public class UpdateStrategyFactory {