Skip to content

Instantly share code, notes, and snippets.

@ldelaprade
Last active July 27, 2017 08:28
Show Gist options
  • Save ldelaprade/75d2f28189c3941935feaf37ebaea1c9 to your computer and use it in GitHub Desktop.
Save ldelaprade/75d2f28189c3941935feaf37ebaea1c9 to your computer and use it in GitHub Desktop.
Add a Progress Window to your VAADIN web application
/**
* @author
*
* Laurent de Laprade
*
* _ _ _ ____ _
* | | __| | | / __ \| |__ _ __ ___ ___ ___ _ __ ___
* | |/ _` | |/ / _` | '_ \| '_ \ / _ \ / __/ _ \| '_ ` _ \
* | | (_| | | | (_| | | | | |_) | __/| (_| (_) | | | | | |
* |_|\__,_|_|\ \__,_|_| |_| .__/ \___(_)___\___/|_| |_| |_|
* \____/ |_|
*
* ASCII generation by: http://www.network-science.de/ascii/
*
* A vaadin WEB application showing a page for testing the ProgressWindow
*
*/
package com.mycompany.vaadin.addons;
import javax.servlet.annotation.WebServlet;
import com.vaadin.annotations.Push;
import com.vaadin.annotations.Theme;
import com.vaadin.annotations.VaadinServletConfiguration;
import com.vaadin.server.VaadinRequest;
import com.vaadin.server.VaadinServlet;
import com.vaadin.shared.communication.PushMode;
import com.vaadin.ui.Button;
import com.vaadin.ui.UI;
import com.vaadin.ui.VerticalLayout;
/**
* This UI is the application entry point. A UI may either represent a browser window
* (or tab) or some part of a html page where a Vaadin application is embedded.
* <p>
* The UI is initialized using {@link #init(VaadinRequest)}. This method is intended to be
* overridden to add component to the user interface and initialize non-component functionality.
*/
@Theme("mytheme")
@Push(PushMode.MANUAL)
public class MyUI extends UI
{
private static final long serialVersionUID = 2182917538601097229L;
void DummyLengthyOperation() throws Exception
{
try
{
int i=0;
while(i++<10 && !Thread.currentThread().isInterrupted()) Thread.sleep(500);
}
catch (InterruptedException e)
{
System.out.println("Operation interrupted by user");
}
}
@Override
protected void init(VaadinRequest vaadinRequest) {
final VerticalLayout layout = new VerticalLayout();
Button button1 = new Button("Test Progress Window");
button1.addClickListener
(
e ->
{
// Initialize our new UI component
new ProgressWindow("Task in progress...", "Please wait")
{
private static final long serialVersionUID = 1L;
@Override
public void lengthyOperation() throws Exception
{
DummyLengthyOperation();
}
};
}
);
Button button2 = new Button("Test Progress Window with cancel button");
button2.addClickListener
(
e ->
{
// Initialize our new UI component
new ProgressWindow("Task in progress...", "Please wait", "Can't wait")
{
private static final long serialVersionUID = 1L;
@Override
public void lengthyOperation() throws Exception
{
DummyLengthyOperation();
}
};
}
);
layout.addComponents(button1, button2);
setContent(layout);
}
@WebServlet(urlPatterns = "/*", name = "MyUIServlet", asyncSupported = true)
@VaadinServletConfiguration(ui = MyUI.class, productionMode = false)
public static class MyUIServlet extends VaadinServlet
{
private static final long serialVersionUID = 9104328224784918917L;
}
}
/**
* @author
*
* Laurent de Laprade
*
* _ _ _ ____ _
* | | __| | | / __ \| |__ _ __ ___ ___ ___ _ __ ___
* | |/ _` | |/ / _` | '_ \| '_ \ / _ \ / __/ _ \| '_ ` _ \
* | | (_| | | | (_| | | | | |_) | __/| (_| (_) | | | | | |
* |_|\__,_|_|\ \__,_|_| |_| .__/ \___(_)___\___/|_| |_| |_|
* \____/ |_|
*
* ASCII generation by: http://www.network-science.de/ascii/
*
*
* -------------------------------------------------------------
* Usage samples:
* -------------------------------------------------------------
* Usage 1 : a Progress windows which is not cancel-able
* -------------------------------------------------------------
* new ProgressWindow("Loading Table")
* {
* private static final long serialVersionUID = 1L;
* @Override
* public void lengthyOperation() throws Exception
* {
* ProcessLogin();
* }
* };
* -------------------------------------------------------------
* Usage 2 : a Progress windows with a Cancel Button
* -------------------------------------------------------------
* new ProgressWindow("Connecting you...", "Please wait", "Can't wait")
* {
* private static final long serialVersionUID = 1L;
* @Override
* public void lengthyOperation() throws Exception
* {
* ProcessLogin();
* }
* };
*
*/
package com.mycompany.vaadin.addons;
import com.vaadin.event.FieldEvents.FocusEvent;
import com.vaadin.event.FieldEvents.FocusListener;
import com.vaadin.server.Page;
import com.vaadin.server.VaadinRequest;
import com.vaadin.server.VaadinResponse;
import com.vaadin.server.VaadinService;
import com.vaadin.server.WebBrowser;
import com.vaadin.ui.Alignment;
import com.vaadin.ui.Button.ClickEvent;
import com.vaadin.ui.Button.ClickListener;
import com.vaadin.ui.Label;
import com.vaadin.ui.NativeButton;
import com.vaadin.ui.Notification;
import com.vaadin.ui.Notification.Type;
import com.vaadin.ui.ProgressBar;
import com.vaadin.ui.UI;
import com.vaadin.ui.VerticalLayout;
import com.vaadin.ui.Window;
//
// PROGRESS WINDOW DESIGN
// drawing using http://asciiflow.com/
//
// +---------------------------------------------------------------------------+
// | |
// | Progress window title | CAPTION ZONE
// | |
// +---------------------------------------------------------------------------+
// +---------------------------------------------------------------------------+
// | | DESCRIPTION LABEL
// | Progess infos ... |
// | |
// | |
// | |
// | |
// +-----------------------+---------------------------+-----------------------+
// | | | |
// | | progress Animation | | PROGRESS BAR
// | | | |
// | +---------------------------+ +------------------+ |
// | | | |
// | | Optional Cancel | | CANCEL BUTTON
// | | button | |
// | +------------------+ |
// +---------------------------------------------------------------------------+
//
//
public abstract class ProgressWindow extends Window implements FocusListener, ClickListener
{
private static final long serialVersionUID = 5253966202431615380L;
private VerticalLayout layout = new VerticalLayout();
private Label description = new Label();
private ProgressBar progressIndicator = new ProgressBar();
// The lengthy action you'll implement
public abstract void lengthyOperation() throws Exception;
private boolean processWasStarted = false;
// members for a 'Cancel' button mode
// We won't use semaphore and thread spawning for
// the lightweight non-cancel-able progress windows mode
private boolean hasCancelButton = false;
private NativeButton btnKill = null;
String btnKillCaption = null;
Thread threadedLenghtyOperation = null;
public ProgressWindow(String caption)
{
init(caption, "Please wait...");
}
public ProgressWindow()
{
init("Job in progress", "Please wait...");
}
public ProgressWindow(String caption, String progressInfo)
{
init(caption, progressInfo);
}
public ProgressWindow(String caption, String progressInfo, String killBtnText)
{
if( UI.getCurrent().getPushConfiguration().getPushMode().isEnabled() )
{
hasCancelButton = true;
btnKillCaption = killBtnText;
}
else
Notification.show("Vaadin Push not enabled. 'Cancel' button cannot be added", Type.HUMANIZED_MESSAGE);
init(caption, progressInfo);
}
public void init(String caption, String progressInfo)
{
setCaption(caption);
setClosable(false);
center();
layout.addComponent(description);
description.setSizeFull();
description.setHeight("40px");
layout.setComponentAlignment(description, Alignment.MIDDLE_CENTER);
description.setValue(progressInfo);
layout.addComponent(progressIndicator);
layout.setComponentAlignment(progressIndicator, Alignment.MIDDLE_CENTER);
progressIndicator.setIndeterminate(true);
setContent(layout);
layout.setMargin(true);
setWidth("450px");
setHeight("180px");
setResizable(false);
UI.getCurrent().addWindow(this);
if( hasCancelButton && btnKill==null )
{
btnKill = new NativeButton();
btnKill.setCaption(btnKillCaption);
layout.addComponent(btnKill);
layout.setComponentAlignment(btnKill, Alignment.BOTTOM_RIGHT);
this.setClosable(true);
btnKill.addClickListener(clicEvent -> buttonClick(clicEvent));
}
// SetModal call will force focus on us,
// then, focus will cause the lengthy operation to start
setModal(true);
addFocusListener(this);
}
@Override
public void buttonClick(ClickEvent event)
{
if( btnKill!=null && event.getButton() == btnKill )
{
btnKill.setEnabled(false);
btnKill.setCaption("Closing...");
if( threadedLenghtyOperation!=null )
threadedLenghtyOperation.interrupt();
setModal(false);
close();
}
}
@Override
public void focus(FocusEvent event)
{
// processWasStarted flag to protect against multiple 'OnFocus' events
if( !processWasStarted )
{
try
{
processWasStarted = true;
final VaadinRequest mainThreadRequest = VaadinService.getCurrentRequest();
final VaadinResponse mainThreadResponse = VaadinService.getCurrentResponse();
if( !hasCancelButton ) lengthyOperation();
else
{
UI currentUI = UI.getCurrent();
VaadinService currentService = VaadinService.getCurrent();
threadedLenghtyOperation = new Thread()
{
@Override
public void run()
{
if( currentService!=null ) VaadinService.setCurrent(currentService);
if( currentUI!=null ) UI.setCurrent(currentUI);
try
{
// Background thread is not aware of Request/Response
// and this can induce bugs if your 'lengthy Operation' need them
// For instance if it needs to read/write cookies !
if( currentService != null ) currentService.setCurrentInstances(mainThreadRequest, mainThreadResponse);
lengthyOperation();
}
catch (Exception e)
{
e.printStackTrace();
}
finally
{
setModal(false);
close();
// Need push call because we are in Background thread
// To enable Push, Note that UI class need Push annotation like this:
// @Push(PushMode.MANUAL)
// public class MyUI extends UI
if( currentUI != null ) currentUI.push();
}
}
};
threadedLenghtyOperation.start();
}
}
catch (Exception e)
{
e.printStackTrace();
}
finally
{
if( btnKill==null )
{
// Mono-thread mode: no btnKill was added.
// Lengthy task is complete
setModal(false);
close();
}
}
}
}
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment