1 /*
   2  * Copyright (c) 2008, 2013, Oracle and/or its affiliates.
   3  * All rights reserved. Use is subject to license terms.
   4  *
   5  * This file is available and licensed under the following license:
   6  *
   7  * Redistribution and use in source and binary forms, with or without
   8  * modification, are permitted provided that the following conditions
   9  * are met:
  10  *
  11  *  - Redistributions of source code must retain the above copyright
  12  *    notice, this list of conditions and the following disclaimer.
  13  *  - Redistributions in binary form must reproduce the above copyright
  14  *    notice, this list of conditions and the following disclaimer in
  15  *    the documentation and/or other materials provided with the distribution.
  16  *  - Neither the name of Oracle Corporation nor the names of its
  17  *    contributors may be used to endorse or promote products derived
  18  *    from this software without specific prior written permission.
  19  *
  20  * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
  21  * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
  22  * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
  23  * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
  24  * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
  25  * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
  26  * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
  27  * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
  28  * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
  29  * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
  30  * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
  31  */
  32 package com.javafx.experiments.scheduleapp;
  33 
  34 import static com.javafx.experiments.scheduleapp.Theme.*;
  35 import com.javafx.experiments.scheduleapp.control.Popover;
  36 import java.util.HashMap;
  37 import java.util.Map;
  38 import javafx.animation.Interpolator;
  39 import javafx.animation.KeyFrame;
  40 import javafx.animation.KeyValue;
  41 import javafx.animation.Timeline;
  42 import javafx.application.Platform;
  43 import javafx.collections.FXCollections;
  44 import javafx.collections.ListChangeListener;
  45 import javafx.collections.ObservableList;
  46 import javafx.event.EventHandler;
  47 import javafx.geometry.Bounds;
  48 import javafx.scene.Scene;
  49 import javafx.scene.control.Label;
  50 import javafx.scene.image.Image;
  51 import javafx.scene.image.ImageView;
  52 import javafx.scene.input.MouseEvent;
  53 import javafx.scene.layout.HBox;
  54 import javafx.scene.layout.Pane;
  55 import javafx.scene.paint.Color;
  56 import javafx.scene.paint.ImagePattern;
  57 import javafx.scene.shape.Rectangle;
  58 import javafx.util.Duration;
  59 
  60 /**
  61  * A custom page container like a TabView but with special visuals and animation.
  62  */
  63 public class PageContainer extends Pane {
  64     private static final Color TEXT_SELECTED_COLOR = Color.WHITE;
  65     // model
  66     private final ObservableList<Page> pages = FXCollections.observableArrayList();
  67     private Page currentPage = null;
  68     // ui
  69     private HBox header = new HBox();
  70     private Rectangle selectionBox = new Rectangle();
  71     private Map<Page,Label> titlesMap = new HashMap<Page, Label>();
  72     private ImageView headerShadow = new ImageView(
  73                 new Image(getClass().getResource("images/header-shadow.png").toExternalForm()));
  74     private ImageView headerArrow = new ImageView(
  75                 new Image(getClass().getResource("images/header-arrow.png").toExternalForm()));
  76     private final Popover popover;
  77     private final AutoLogoutLightBox lightBox;
  78 
  79     public PageContainer(Popover popover, AutoLogoutLightBox lightBox, final Page ... pages) {
  80         this.pages.addAll(pages);
  81         this.popover = popover;
  82         this.lightBox = lightBox;
  83         // build ui
  84         header.setId("page-container-header");
  85         header.setFillHeight(true);
  86         headerShadow.setMouseTransparent(true);
  87         headerArrow.setMouseTransparent(true);
  88 //        selectionBox.setFill(new ImagePattern(
  89 //                new Image(getClass().getResource("images/rough_diagonal_blue.jpg").toExternalForm()),
  90 //                0,0,255,255,false));
  91         selectionBox.setFill( new ImagePattern(
  92                 new Image(getClass().getResource("images/rough_diagonal_blue.jpg").toExternalForm()),
  93                 0,0,255,255,false));
  94         selectionBox.setManaged(false);
  95         header.getChildren().add(selectionBox);
  96         getChildren().addAll(header,headerShadow, headerArrow);
  97         // add all pages
  98         getChildren().addAll(pages);
  99         for (Page page: pages) {
 100             page.setVisible(false);
 101         }
 102         // do first rebuild and listen to changes in available pages
 103         rebuild();
 104         this.pages.addListener(new ListChangeListener<Page>() {
 105             @Override public void onChanged(Change<? extends Page> change) {
 106                 rebuild();
 107             }
 108         });
 109         // goto first page, runLater because we want this to happen after first layout
 110         Platform.runLater(new Runnable() {
 111             @Override public void run() {
 112                 gotoPage(pages[0], false);
 113             }
 114         });
 115     }
 116     
 117     private void rebuild() {
 118         if (header.getChildren().size() > 1) {
 119             header.getChildren().remove(1, header.getChildren().size());
 120             titlesMap.clear();
 121         }
 122         for(final Page page: pages) {
 123             Label title = new Label(page.getName());
 124             titlesMap.put(page,title);
 125             title.setMaxHeight(Double.MAX_VALUE);
 126             title.getStyleClass().add("page-container-header-title");
 127             title.setPickOnBounds(true);
 128             title.setOnMouseClicked(new EventHandler<MouseEvent>() {
 129                 @Override public void handle(MouseEvent t) {
 130                     System.out.println("CLICKED ON PAGE BUTTON FOR "+page.getName());
 131                     if (currentPage != page) {
 132                         gotoPage(page, true);
 133                     }
 134                     page.pageTabClicked();
 135                 }
 136             });
 137             header.getChildren().add(title);
 138         }
 139     }
 140     
 141     public void gotoPage(Page page, boolean animate) {
 142         System.out.println("CHANGING TO PAGE --> "+page.getName());
 143         final Label newTitleLabel = titlesMap.get(page);
 144         final Bounds newPageTitleBounds = newTitleLabel.getBoundsInParent();
 145         if (currentPage != null && animate) {
 146             final Label currentTitlelabel = titlesMap.get(currentPage);
 147             final Bounds currentPageTitleBounds = currentTitlelabel.getBoundsInParent();
 148             TimeLine timeL = new Timeline(
 149                     new KeyFrame(Duration.ZERO,
 150                         new KeyValue(selectionBox.xProperty(), currentPageTitleBounds.getMinX()),
 151                         new KeyValue(selectionBox.yProperty(), currentPageTitleBounds.getMinY()),
 152                         new KeyValue(selectionBox.widthProperty(), currentPageTitleBounds.getWidth()),
 153                         new KeyValue(selectionBox.heightProperty(), currentPageTitleBounds.getHeight()),
 154                         new KeyValue(newTitleLabel.textFillProperty(), DARK_GREY),
 155                         new KeyValue(currentTitlelabel.textFillProperty(), TEXT_SELECTED_COLOR),
 156                         new KeyValue(headerArrow.layoutXProperty(), headerArrow.getLayoutX())
 157                     ),
 158                     new KeyFrame(Duration.seconds(.3), 
 159                         new KeyValue(selectionBox.xProperty(), newPageTitleBounds.getMinX(), Interpolator.EASE_BOTH),
 160                         new KeyValue(selectionBox.yProperty(), newPageTitleBounds.getMinY(), Interpolator.EASE_BOTH),
 161                         new KeyValue(selectionBox.widthProperty(), newPageTitleBounds.getWidth(), Interpolator.EASE_BOTH),
 162                         new KeyValue(selectionBox.heightProperty(), newPageTitleBounds.getHeight(), Interpolator.EASE_BOTH),
 163                         new KeyValue(newTitleLabel.textFillProperty(), TEXT_SELECTED_COLOR, Interpolator.EASE_BOTH),
 164                         new KeyValue(currentTitlelabel.textFillProperty(), DARK_GREY, Interpolator.EASE_BOTH),
 165                         new KeyValue(headerArrow.layoutXProperty(), newPageTitleBounds.getMinX() + (newPageTitleBounds.getWidth()/2) - 6, Interpolator.EASE_BOTH)
 166                     )
 167                 );
 168             timeL.play();
 169             // hide current page
 170             currentPage.setVisible(false);
 171         } else {
 172             selectionBox.setX(newPageTitleBounds.getMinX());
 173             selectionBox.setY(newPageTitleBounds.getMinY());
 174             selectionBox.setWidth(newPageTitleBounds.getWidth());
 175             selectionBox.setHeight(newPageTitleBounds.getHeight());
 176             headerArrow.setLayoutX(newPageTitleBounds.getMinX() + (newPageTitleBounds.getWidth()/2) - 6);
 177             if (currentPage != null) {
 178                 // hide current page
 179                 currentPage.setVisible(false);
 180                 // change current pages title back to dark grey
 181                 titlesMap.get(currentPage).setTextFill(DARK_GREY);
 182             }
 183             newTitleLabel.setTextFill(TEXT_SELECTED_COLOR);
 184         }
 185 //        if(getChildren().size() == 3) {
 186 //            getChildren().add(page);
 187 //        } else {
 188 //            getChildren().set(3,page);
 189 //        }
 190         page.setVisible(true);
 191         currentPage = page;
 192     }
 193 
 194     @Override protected void layoutChildren() {
 195         final double w = getWidth();
 196         final double h = getHeight();
 197         header.resize(w, 40);
 198         headerShadow.setFitWidth(w);
 199         for(Page page: pages) {
 200             page.resizeRelocate(0, 40, w, h-40);
 201         }
 202 
 203         Scene scene = getScene();
 204         double width = popover.prefWidth(-1);
 205         popover.setLayoutX((int) ((scene.getWidth() - width) / 2));
 206         popover.setLayoutY(50);
 207         if (popover.isVisible()) {
 208             popover.autosize();
 209         }
 210         lightBox.resizeRelocate(0, 0, w, h);
 211     }
 212     
 213     public ObservableList<Page> getPages() {
 214         return pages;
 215     }
 216 
 217     public void reset() {
 218         for (Page page : pages) {
 219             page.reset();
 220         }
 221     }
 222 }