Java tutorial
/* * Wizard.java * * Copyright (C) 2009-12 by RStudio, Inc. * * Unless you have received this program directly from RStudio pursuant * to the terms of a commercial license agreement with RStudio, then * this program is licensed to you under the terms of version 3 of the * GNU Affero General Public License. This program is distributed WITHOUT * ANY EXPRESS OR IMPLIED WARRANTY, INCLUDING THOSE OF NON-INFRINGEMENT, * MERCHANTABILITY OR FITNESS FOR A PARTICULAR PURPOSE. Please refer to the * AGPL (http://www.gnu.org/licenses/agpl-3.0.txt) for more details. * */ package org.rstudio.core.client.widget; import java.util.ArrayList; import org.rstudio.core.client.CommandWithArg; import org.rstudio.core.client.theme.res.ThemeResources; import com.google.gwt.dom.client.Style.Unit; import com.google.gwt.event.dom.client.ClickEvent; import com.google.gwt.event.dom.client.ClickHandler; import com.google.gwt.event.logical.shared.CloseEvent; import com.google.gwt.event.logical.shared.CloseHandler; import com.google.gwt.layout.client.Layout.AnimationCallback; import com.google.gwt.layout.client.Layout.Layer; import com.google.gwt.resources.client.ImageResource; import com.google.gwt.user.client.Command; import com.google.gwt.user.client.ui.Label; import com.google.gwt.user.client.ui.LayoutPanel; import com.google.gwt.user.client.ui.PopupPanel; import com.google.gwt.user.client.ui.VerticalPanel; import com.google.gwt.user.client.ui.Widget; public class Wizard<I, T> extends ModalDialog<T> { public Wizard(String caption, String okCaption, I initialData, WizardPage<I, T> firstPage, final ProgressOperationWithInput<T> operation) { super(caption, operation); initialData_ = initialData; okCaption_ = okCaption; firstPage_ = firstPage; activePage_ = firstPage; resetOkButtonCaption(); setOkButtonVisible(false); addCloseHandler(new CloseHandler<PopupPanel>() { @Override public void onClose(CloseEvent<PopupPanel> arg0) { cleanupPage(firstPage_); } }); // add next button nextButton_ = new ThemedButton("Next", new ClickHandler() { @Override public void onClick(ClickEvent arg0) { if (activePage_ instanceof WizardIntermediatePage<?, ?>) { final WizardIntermediatePage<I, T> page = (WizardIntermediatePage<I, T>) activePage_; // collect input from this page asynchronously and advance when // we have input page.collectIntermediateInput(getProgressIndicator(), new OperationWithInput<T>() { @Override public void execute(T input) { if (page.validate(input)) { intermediateResult_ = input; page.advance(); } } }); } } }); nextButton_.setVisible(false); addActionButton(nextButton_); } @Override protected Widget createMainWidget() { WizardResources res = WizardResources.INSTANCE; WizardResources.Styles styles = res.styles(); VerticalPanel mainWidget = new VerticalPanel(); mainWidget.addStyleName(styles.mainWidget()); headerPanel_ = new LayoutPanel(); headerPanel_.addStyleName(styles.headerPanel()); // layout constants final int kTopMargin = 5; final int kLeftMargin = 8; final int kCaptionWidth = 400; final int kCaptionHeight = 30; final int kPageUILeftMargin = 123; // first page caption subCaptionLabel_ = new Label(firstPage_.getPageCaption()); subCaptionLabel_.addStyleName(styles.headerLabel()); headerPanel_.add(subCaptionLabel_); headerPanel_.setWidgetLeftWidth(subCaptionLabel_, kTopMargin, Unit.PX, kCaptionWidth, Unit.PX); headerPanel_.setWidgetTopHeight(subCaptionLabel_, kLeftMargin, Unit.PX, kCaptionHeight, Unit.PX); // second page back button ImageResource bkImg = res.wizardBackButton(); backButton_ = new Label("Back"); backButton_.addStyleName(styles.wizardBackButton()); backButton_.addStyleName(ThemeResources.INSTANCE.themeStyles().handCursor()); headerPanel_.add(backButton_); headerPanel_.setWidgetLeftWidth(backButton_, kTopMargin - 2, Unit.PX, bkImg.getWidth(), Unit.PX); headerPanel_.setWidgetTopHeight(backButton_, kTopMargin - 2, Unit.PX, bkImg.getHeight(), Unit.PX); backButton_.setVisible(false); backButton_.addClickHandler(new ClickHandler() { public void onClick(ClickEvent event) { goBack(); } }); // second page caption label pageCaptionLabel_ = new Label(); pageCaptionLabel_.addStyleName(styles.headerLabel()); headerPanel_.add(pageCaptionLabel_); headerPanel_.setWidgetLeftWidth(pageCaptionLabel_, kPageUILeftMargin, Unit.PX, kCaptionWidth, Unit.PX); headerPanel_.setWidgetTopHeight(pageCaptionLabel_, kLeftMargin, Unit.PX, kCaptionHeight, Unit.PX); pageCaptionLabel_.setVisible(false); mainWidget.add(headerPanel_); // main body panel for transitions bodyPanel_ = new LayoutPanel(); bodyPanel_.addStyleName(styles.wizardBodyPanel()); bodyPanel_.getElement().getStyle().setProperty("overflowX", "hidden"); mainWidget.add(bodyPanel_); // add first page (and all sub-pages recursively) addAndInitializePage(firstPage_, true); setNextButtonState(firstPage_); return mainWidget; } private void addAndInitializePage(WizardPage<I, T> page, boolean visible) { page.setSize("100%", "100%"); bodyPanel_.add(page); bodyPanel_.setWidgetTopBottom(page, 0, Unit.PX, 0, Unit.PX); bodyPanel_.setWidgetLeftRight(page, 0, Unit.PX, 0, Unit.PX); bodyPanel_.setWidgetVisible(page, visible); page.initialize(initialData_); CommandWithArg<WizardPage<I, T>> showPageCmd = new CommandWithArg<WizardPage<I, T>>() { @Override public void execute(WizardPage<I, T> page) { showPage(page); }; }; if (page instanceof WizardNavigationPage<?, ?>) { ((WizardNavigationPage<I, T>) page).setSelectionHandler(showPageCmd); } else if (page instanceof WizardIntermediatePage<?, ?>) { ((WizardIntermediatePage<I, T>) page).setNextHandler(showPageCmd); } // recursively initialize child pages ArrayList<WizardPage<I, T>> subPages = page.getSubPages(); if (subPages != null) { for (int i = 0; i < subPages.size(); i++) { addAndInitializePage(subPages.get(i), false); } } } @Override protected T collectInput() { WizardPage<I, T> inputPage = activeInputPage(); if (inputPage != null) { T input = ammendInput(inputPage.collectInput()); return input; } else return null; } @Override protected boolean validate(T input) { WizardPage<I, T> inputPage = activeInputPage(); if (inputPage != null) return inputPage.validate(input); else return false; } private WizardPage<I, T> activeInputPage() { if (activePage_ != null && !(activePage_ instanceof WizardNavigationPage<?, ?>)) { return activePage_; } else { return null; } } private void animate(final Widget from, final Widget to, boolean rightToLeft, final Command onCompleted) { // protect against multiple calls if (isAnimating_) return; bodyPanel_.setWidgetVisible(to, true); int width = getOffsetWidth(); bodyPanel_.setWidgetLeftWidth(from, 0, Unit.PX, width, Unit.PX); bodyPanel_.setWidgetLeftWidth(to, rightToLeft ? width : -width, Unit.PX, width, Unit.PX); bodyPanel_.forceLayout(); bodyPanel_.setWidgetLeftWidth(from, rightToLeft ? -width : width, Unit.PX, width, Unit.PX); bodyPanel_.setWidgetLeftWidth(to, 0, Unit.PX, width, Unit.PX); isAnimating_ = true; bodyPanel_.animate(300, new AnimationCallback() { @Override public void onAnimationComplete() { bodyPanel_.setWidgetVisible(from, false); bodyPanel_.setWidgetLeftRight(to, 0, Unit.PX, 0, Unit.PX); bodyPanel_.forceLayout(); isAnimating_ = false; onCompleted.execute(); } @Override public void onLayout(Layer layer, double progress) { } }); } private void showPage(final WizardPage<I, T> page) { // ask whether the page will accept the navigation if (!page.acceptNavigation()) return; // give the page the currently accumulated result, if any page.setIntermediateResult(intermediateResult_); // determine behavior based on whether this is standard page or // a navigation page final boolean okButtonVisible = pageIsFinal(page); activeParentNavigationPage_ = activePage_; animate(activePage_, page, true, new Command() { @Override public void execute() { // set active page activePage_ = page; // update header subCaptionLabel_.setVisible(false); backButton_.setVisible(true); pageCaptionLabel_.setText(page.getPageCaption()); pageCaptionLabel_.setVisible(true); // make ok button visible setOkButtonVisible(okButtonVisible); // if this is an intermediate page, make Next visible setNextButtonState(page); // let wizard and page know that the new page is active onPageActivated(page, okButtonVisible); page.onActivate(); // set focus FocusHelper.setFocusDeferred(page); } }); } private void goBack() { final boolean isNavigationPage = activeParentNavigationPage_ != null; // determine behavior based on whether we are going back to a // navigation page or a selector page final Widget toWidget = isNavigationPage ? activeParentNavigationPage_ : firstPage_; final String pageCaptionLabel = isNavigationPage ? activeParentNavigationPage_.getPageCaption() : ""; final WizardPage<I, T> newActivePage = isNavigationPage ? activeParentNavigationPage_ : firstPage_; final CanFocus focusWidget = (CanFocus) toWidget; activeParentNavigationPage_ = null; animate(activePage_, toWidget, false, new Command() { @Override public void execute() { // update active page activePage_ = newActivePage; // update header subCaptionLabel_.setVisible(newActivePage == firstPage_); pageCaptionLabel_.setVisible(newActivePage != firstPage_ && isNavigationPage); pageCaptionLabel_.setText(pageCaptionLabel); setNextButtonState(newActivePage); backButton_.setVisible(newActivePage != firstPage_); // make ok button invisible setOkButtonVisible(false); // call hook onSelectorActivated(); // set focus focusWidget.focus(); } }); } protected void onPageActivated(WizardPage<I, T> page, boolean okButtonVisible) { } protected void onSelectorActivated() { } protected T ammendInput(T input) { return input; } private void resetOkButtonCaption() { setOkButtonCaption(okCaption_); } private boolean pageIsFinal(WizardPage<I, T> page) { return page.getSubPages() == null || page.getSubPages().size() == 0; } private void setNextButtonState(WizardPage<I, T> page) { boolean isIntermediate = page instanceof WizardIntermediatePage<?, ?>; nextButton_.setVisible(isIntermediate); setDefaultOverrideButton(isIntermediate ? nextButton_ : null); } private void cleanupPage(WizardPage<I, T> page) { if (page == null) return; // notify child pages first (do cleanup in reverse order of construction) ArrayList<WizardPage<I, T>> subPages = page.getSubPages(); if (subPages != null) { for (int i = 0; i < subPages.size(); i++) { cleanupPage(subPages.get(i)); } } // clean this page page.onWizardClosing(); } private final I initialData_; private T intermediateResult_; private final String okCaption_; private LayoutPanel headerPanel_; private Label subCaptionLabel_; private Label backButton_; private Label pageCaptionLabel_; private ThemedButton nextButton_; private LayoutPanel bodyPanel_; private WizardPage<I, T> firstPage_ = null; private WizardPage<I, T> activePage_ = null; private WizardPage<I, T> activeParentNavigationPage_ = null; private boolean isAnimating_ = false; }