001package ca.bc.webarts.javaFX;
002
003import javafx.application.Application;
004import javafx.beans.property.*;
005import javafx.beans.value.*;
006import javafx.collections.*;
007import javafx.event.*;
008import javafx.scene.*;
009import javafx.scene.control.*;
010import javafx.scene.layout.*;
011import javafx.stage.Stage;
012
013import java.util.Stack;
014
015/** This class displays a survey using a wizard */
016public class Survey extends Application
017{
018  public static void main(String[] args) throws Exception
019  { launch(args); }
020  
021  @Override
022  public void start(Stage stage) throws Exception
023  {
024    // configure and display the scene and stage.
025    stage.setScene(new Scene(new SurveyWizard(stage), 400, 250));
026    stage.show();
027  }
028}
029
030/** basic wizard infrastructure class */
031abstract class Wizard extends StackPane
032{
033  private static final int UNDEFINED = -1;
034  private ObservableList<WizardPage> pages = FXCollections.observableArrayList();
035  private Stack<Integer> history = new Stack<>();
036  private int curPageIdx = UNDEFINED;
037
038  Wizard(WizardPage... nodes)
039  {
040    pages.addAll(nodes);
041    navTo(0);
042    setStyle("-fx-padding: 10; -fx-background-color: cornsilk;");
043  }
044
045  void nextPage()
046  {
047    if (hasNextPage())
048    {
049      navTo(curPageIdx + 1);
050    }
051  }
052
053  void priorPage()
054  {
055    if (hasPriorPage())
056    {
057      navTo(history.pop(), false);
058    }
059  }
060
061  boolean hasNextPage()
062  {
063    return (curPageIdx < pages.size() - 1);
064  }
065
066  boolean hasPriorPage()
067  {
068    return !history.isEmpty();
069  }
070
071  void navTo(int nextPageIdx, boolean pushHistory)
072  {
073    if (nextPageIdx < 0 || nextPageIdx >= pages.size())
074    {
075      return;
076    }
077    if (curPageIdx != UNDEFINED)
078    {
079      if (pushHistory)
080      {
081        history.push(curPageIdx);
082      }
083    }
084
085    WizardPage nextPage = pages.get(nextPageIdx);
086    curPageIdx = nextPageIdx;
087    getChildren().clear();
088    getChildren().add(nextPage);
089    nextPage.manageButtons();
090  }
091
092  void navTo(int nextPageIdx)
093  {
094    navTo(nextPageIdx, true);
095  }
096
097  void navTo(String id)
098  {
099    Node page = lookup("#" + id);
100    if (page != null)
101    {
102      int nextPageIdx = pages.indexOf(page);
103      if (nextPageIdx != UNDEFINED)
104      {
105        navTo(nextPageIdx);
106      }
107    }
108  }
109
110  public void finish()
111  { }
112  public void cancel()
113  { }
114}
115
116/** This class shows a satisfaction survey */
117class SurveyWizard extends Wizard
118{
119  Stage owner;
120  public SurveyWizard(Stage owner)
121  {
122    super(new ComplaintsPage(), new MoreInformationPage(), new ThanksPage());
123    this.owner = owner;
124  }
125  public void finish()
126  {
127    System.out.println("Had complaint? " + SurveyData.instance.hasComplaints.get());
128    if (SurveyData.instance.hasComplaints.get())
129    {
130      System.out.println("Complaints: " + (SurveyData.instance.complaints.get().isEmpty() ? "No Details" : "\n" + SurveyData.instance.complaints.get()));
131    }
132    owner.close();
133  }
134  public void cancel()
135  {
136    System.out.println("Cancelled");
137    owner.close();
138  }
139}
140
141/** Simple placeholder class for the customer entered survey response. */
142class SurveyData
143{
144  BooleanProperty hasComplaints = new SimpleBooleanProperty();
145  StringProperty complaints = new SimpleStringProperty();
146  static SurveyData instance = new SurveyData();
147}
148
149
150/** basic wizard page class */
151abstract class WizardPage extends VBox
152{
153  Button priorButton = new Button("_Previous");
154  Button nextButton = new Button("N_ext");
155  Button cancelButton = new Button("Cancel");
156  Button finishButton = new Button("_Finish");
157
158  WizardPage(String title)
159  {
160    getChildren().add(LabelBuilder.create().text(title).style("-fx-font-weight: bold; -fx-padding: 0 0 5 0;").build());
161    setId(title);
162    setSpacing(5);
163    setStyle("-fx-padding:10; -fx-background-color: honeydew; -fx-border-color: derive(honeydew, -30%); -fx-border-width: 3;");
164
165    Region spring = new Region();
166    VBox.setVgrow(spring, Priority.ALWAYS);
167    getChildren().addAll(getContent(), spring, getButtons());
168
169    priorButton.setOnAction(new EventHandler<ActionEvent>()
170    {
171      @Override
172      public void handle(ActionEvent actionEvent)
173      {
174        priorPage();
175      }
176    } );
177    nextButton.setOnAction(new EventHandler<ActionEvent>()
178    {
179      @Override
180      public void handle(ActionEvent actionEvent)
181      {
182        nextPage();
183      }
184    } );
185    cancelButton.setOnAction(new EventHandler<ActionEvent>()
186    {
187      @Override
188      public void handle(ActionEvent actionEvent)
189      {
190        getWizard().cancel();
191      }
192    } );
193    finishButton.setOnAction(new EventHandler<ActionEvent>()
194    {
195      @Override
196      public void handle(ActionEvent actionEvent)
197      {
198        getWizard().finish();
199      }
200    } );
201  }
202
203  HBox getButtons()
204  {
205    Region spring = new Region();
206    HBox.setHgrow(spring, Priority.ALWAYS);
207    HBox buttonBar = new HBox(5);
208    cancelButton.setCancelButton(true);
209    finishButton.setDefaultButton(true);
210    buttonBar.getChildren().addAll(spring, priorButton, nextButton, cancelButton, finishButton);
211    return buttonBar;
212  }
213
214  abstract Parent getContent();
215
216  boolean hasNextPage()
217  {
218    return getWizard().hasNextPage();
219  }
220
221  boolean hasPriorPage()
222  {
223    return getWizard().hasPriorPage();
224  }
225
226  void nextPage()
227  {
228    getWizard().nextPage();
229  }
230
231  void priorPage()
232  {
233    getWizard().priorPage();
234  }
235
236  void navTo(String id)
237  {
238    getWizard().navTo(id);
239  }
240
241  Wizard getWizard()
242  {
243    return (Wizard) getParent();
244  }
245
246  public void manageButtons()
247  {
248    if (!hasPriorPage())
249    {
250      priorButton.setDisable(true);
251    }
252
253    if (!hasNextPage())
254    {
255      nextButton.setDisable(true);
256    }
257  }
258}
259
260/**
261 * This class determines if the user has complaints.
262 * If not, it jumps to the last page of the wizard.
263 */
264class ComplaintsPage extends WizardPage
265{
266  private RadioButton yes;
267  private RadioButton no;
268  private ToggleGroup options = new ToggleGroup();
269
270  public ComplaintsPage()
271  {
272    super("Complaints");
273
274    nextButton.setDisable(true);
275    finishButton.setDisable(true);
276    yes.setToggleGroup(options);
277    no.setToggleGroup(options);
278    options.selectedToggleProperty().addListener(new ChangeListener<Toggle>()
279    { @Override
280      public void changed(ObservableValue<? extends Toggle> observableValue, Toggle oldToggle, Toggle newToggle)
281      {
282        nextButton.setDisable(false);
283        finishButton.setDisable(false);
284      }
285    } );
286  }
287
288  Parent getContent()
289  {
290    yes = new RadioButton("Yes");
291    no = new RadioButton("No");
292    SurveyData.instance.hasComplaints.bind(yes.selectedProperty());
293    return VBoxBuilder.create().spacing(5).children (new Label("Do you have complaints?"), yes, no).build();
294  }
295
296  void nextPage()
297  {
298    // If they have complaints, go to the normal next page
299    if (options.getSelectedToggle().equals(yes))
300    {
301      super.nextPage();
302    }
303    else
304    {
305      // No complaints? Short-circuit the rest of the pages
306      navTo("Thanks");
307    }
308  }
309}
310
311/** This page gathers more information about the complaint */
312class MoreInformationPage extends WizardPage
313{
314  public MoreInformationPage()
315  {
316    super("More Info");
317  }
318
319  Parent getContent()
320  {
321    TextArea textArea = TextAreaBuilder.create()
322                            .wrapText(true)
323                            .text("Tell me what's wrong Dave...")
324                          .build();
325    nextButton.setDisable(true);
326    textArea.textProperty().addListener(new ChangeListener<String>()
327    { @Override
328      public void changed(ObservableValue<? extends String> observableValue, String oldValue, String newValue)
329      {
330        nextButton.setDisable(newValue.isEmpty());
331      }
332    } );
333    SurveyData.instance.complaints.bind(textArea.textProperty());
334    return VBoxBuilder.create()
335               .spacing(5)
336               .children 
337                 (
338                   new Label("Please enter your complaints."),
339                   textArea
340                 )
341             .build();
342  }
343}
344
345/** This page thanks the user for taking the survey */
346class ThanksPage extends WizardPage
347{
348  public ThanksPage()
349  {
350    super("Thanks");
351  }
352
353  Parent getContent()
354  {
355    StackPane stack = StackPaneBuilder.create().children (new Label("Thanks!") ).build();
356    VBox.setVgrow(stack, Priority.ALWAYS);
357    return stack;
358  }
359}