Skip to content

Instantly share code, notes, and snippets.

@TatuLund
Created November 28, 2024 09:05
Show Gist options
  • Select an option

  • Save TatuLund/68789017e80a00762917a5a8c0674fbf to your computer and use it in GitHub Desktop.

Select an option

Save TatuLund/68789017e80a00762917a5a8c0674fbf to your computer and use it in GitHub Desktop.
One very common challenge that occurs when migrating from Swing to Vaadin is that blocking dialog is very common pattern in Swing. That is possible there, because Swing application is single threaded and non-distributed. Vaadin application has UI running in browser making it distributed system. Hence blocking dialog is not a natural concept. The…
package com.example.application.views;
import com.vaadin.flow.component.AttachEvent;
import com.vaadin.flow.component.DetachEvent;
import com.vaadin.flow.component.Text;
import com.vaadin.flow.component.UI;
import com.vaadin.flow.component.UIDetachedException;
import com.vaadin.flow.component.button.Button;
import com.vaadin.flow.component.dialog.Dialog;
import com.vaadin.flow.component.notification.Notification;
import com.vaadin.flow.component.notification.Notification.Position;
import com.vaadin.flow.component.orderedlayout.VerticalLayout;
import com.vaadin.flow.component.progressbar.ProgressBar;
import com.vaadin.flow.router.PageTitle;
import com.vaadin.flow.router.Route;
import com.vaadin.flow.server.Command;
import java.util.Optional;
import java.util.concurrent.*;
@PageTitle("Blocking Dialog")
@Route(value = "blockingdialog", layout = MainLayout.class)
public class BlockingDialogView extends VerticalLayout {
private ExecutorService executor = Executors.newSingleThreadExecutor();
private CompletableFuture<Optional<String>> mainFuture;
private CompletableFuture<String> dialogFuture;
private Button button;
private UI ui;
public BlockingDialogView() {
button = new Button("Process", buttonClickEvent -> startTheTask());
button.setDisableOnClick(true);
add(button);
}
private void accessUI(Command command) {
try {
ui.access(() -> {
if (isAttached()) {
command.execute();
}
});
} catch (UIDetachedException e) {
}
}
private void startTheTask() {
var progress = new ProgressBar();
progress.setIndeterminate(true);
progress.setWidth("50px");
button.setText("");
button.setIcon(progress);
mainFuture = CompletableFuture.supplyAsync(this::longRunningTask,
executor);
mainFuture.thenAccept(optResult -> optResult.ifPresentOrElse(result -> {
accessUI(() -> {
Notification.show("Final: <" + result + ">", 2000,
Position.MIDDLE);
reEnableButton();
});
}, () -> {
accessUI(() -> {
Notification.show("Cancelled", 2000, Position.MIDDLE);
reEnableButton();
});
}));
}
private void reEnableButton() {
button.setEnabled(true);
button.setIcon(null);
button.setText("Process");
}
private Optional<String> longRunningTask() {
dialogFuture = new CompletableFuture<>();
String result = null;
accessUI(() -> {
Notification.show("Processing ...", 2000, Position.MIDDLE);
});
try {
Thread.sleep(2000);
} catch (InterruptedException e) {
}
// need user input for further processing
launchDialog(dialogFuture);
var answer = dialogFuture.join();
accessUI(() -> {
Notification.show("Got input [" + answer + "]", 2000,
Position.MIDDLE);
});
if (answer.equals("Closed")) {
return Optional.ofNullable(result);
}
try {
Thread.sleep(2000);
} catch (InterruptedException e) {
}
if (answer.equals("Yes")) {
result = "Complete Result";
} else if (answer.equals("No")) {
result = "Partial Result";
}
return Optional.ofNullable(result);
}
private void launchDialog(CompletableFuture<String> future) {
accessUI(() -> {
Dialog dialog = new Dialog();
dialog.setHeaderTitle("Choose");
dialog.add(new Text("Esc to cancel."));
dialog.getFooter().add(new Button("Yes", buttonClickEvent -> {
future.complete("Yes");
dialog.close();
}));
dialog.getFooter().add(new Button("No", buttonClickEvent -> {
future.complete("No");
dialog.close();
}));
dialog.open();
dialog.setCloseOnOutsideClick(true);
dialog.addOpenedChangeListener(e -> {
if (!e.isOpened()) {
future.complete("Closed");
}
});
});
}
@Override
public void onAttach(AttachEvent event) {
super.onAttach(event);
ui = event.getUI();
}
@Override
public void onDetach(DetachEvent event) {
ui = null;
if (dialogFuture != null) {
dialogFuture.cancel(true);
dialogFuture = null;
}
if (mainFuture != null) {
mainFuture.cancel(true);
mainFuture = null;
}
super.onDetach(event);
}
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment