Package org.ugate.gui

Source Code of org.ugate.gui.GuiUtil$Dialog

package org.ugate.gui;

import java.awt.Desktop;
import java.io.IOException;
import java.net.URI;

import javafx.animation.KeyFrame;
import javafx.animation.Timeline;
import javafx.application.Platform;
import javafx.beans.value.ChangeListener;
import javafx.beans.value.ObservableValue;
import javafx.concurrent.Service;
import javafx.concurrent.Task;
import javafx.concurrent.Worker.State;
import javafx.event.ActionEvent;
import javafx.event.EventHandler;
import javafx.geometry.Insets;
import javafx.geometry.Pos;
import javafx.scene.Node;
import javafx.scene.Scene;
import javafx.scene.control.Button;
import javafx.scene.control.ButtonBuilder;
import javafx.scene.control.Hyperlink;
import javafx.scene.control.Label;
import javafx.scene.control.LabelBuilder;
import javafx.scene.control.ProgressBarBuilder;
import javafx.scene.control.ProgressIndicator;
import javafx.scene.effect.ColorAdjustBuilder;
import javafx.scene.effect.DropShadow;
import javafx.scene.effect.Effect;
import javafx.scene.effect.InnerShadow;
import javafx.scene.effect.Light;
import javafx.scene.effect.Lighting;
import javafx.scene.input.MouseEvent;
import javafx.scene.layout.FlowPane;
import javafx.scene.layout.GridPane;
import javafx.scene.layout.Region;
import javafx.scene.layout.StackPane;
import javafx.scene.layout.VBox;
import javafx.scene.layout.VBoxBuilder;
import javafx.scene.paint.Color;
import javafx.scene.text.Text;
import javafx.scene.text.TextBuilder;
import javafx.stage.Modality;
import javafx.stage.Popup;
import javafx.stage.Stage;
import javafx.stage.StageStyle;
import javafx.util.Duration;

import org.ugate.resources.RS;
import org.ugate.resources.RS.KEY;

/**
* General GUI utility
*/
public class GuiUtil {

  public static final Color COLOR_ON = Color.web("#00FF00", 1);
  public static final Color COLOR_OPEN = Color.YELLOW;
  public static final Color COLOR_OFF = Color.RED;
  public static final Color COLOR_CLOSED = Color.SLATEGRAY;
  public static final Color COLOR_SELECTED = Color.DEEPSKYBLUE;
  public static final Color COLOR_SELECTED_BLAND = Color.DEEPSKYBLUE.deriveColor(1d, 1d, 0.7d, 1d);
  public static final Color COLOR_SELECTING = Color.YELLOW;
  public static final Color COLOR_SELECTING_BLAND = COLOR_SELECTING.deriveColor(1d, 1d, 0.7d, 1d);
  public static final Color COLOR_UNSELECTED = Color.DARKSLATEGRAY;

  public static final Color COLOR_SONAR = Color.CYAN;
  public static final Color COLOR_PIR = Color.ORANGE.brighter();
  public static final Color COLOR_MW = Color.CHARTREUSE;
  public static final Color COLOR_LASER = Color.RED;
  public static final Color COLOR_READ = Color.DARKBLUE.darker();
  public static final Color[] COLORS_CHART = new Color[] {
      GuiUtil.COLOR_SONAR, GuiUtil.COLOR_PIR, GuiUtil.COLOR_MW,
      GuiUtil.COLOR_LASER, GuiUtil.COLOR_READ };

  public static final Color COLOR_CAM = Color.YELLOW;
  public static final Color COLOR_MULTI = Color.ORANGE;
  public static final Color COLOR_DATE = Color.WHITESMOKE;

  /**
   * Private utility constructor
   */
  private GuiUtil() {
  }

  /**
   * Creates an external {@link Hyperlink} that will open in the default
   * browser (if supported)
   *
   * @param text
   *            the {@link Hyperlink#getText()} (when null the {@link URI}
   *            will be used)
   * @param uri
   *            the {@link URI} of the {@link Hyperlink}
   * @return the external {@link Hyperlink}
   */
  public static Hyperlink externalHyperlink(final String text, final URI uri) {
    final Hyperlink a = new Hyperlink();
    a.setText(text == null ? uri.toString() : text);
    a.setOnAction(new EventHandler<ActionEvent>() {
      @Override
      public void handle(final ActionEvent event) {
        if (Desktop.isDesktopSupported()) {
          try {
            Desktop.getDesktop().browse(uri);
          } catch (IOException e) {
            throw new RuntimeException(String.format(
                "Unable to open %1$s/%2$s", text, uri), e);
          }
        }
      }
    });
    return a;
  }

  /**
   * Creates a modal alert that shows a {@linkplain ProgressIndicator} display
   *
   * @param parent
   *            the parent {@linkplain Stage} of the alert (should not be
   *            null)
   * @return the {@linkplain ProgressIndicator} alert
   */
  public static Stage alertProgress(final Stage parent) {
    final Stage alert = alert(parent, 200, 150,
        Modality.APPLICATION_MODAL, new ProgressIndicator());
    return alert;
  }
 
  /**
   * Creates a {@linkplain Service} that will show a modal
   * {@linkplain ProgressIndicator} {@linkplain Stage} that will automatically
   * be shown when started and hidden when complete/failed/canceled
   *
   * @param <T>
   *            the {@linkplain Service} task type
   * @param parent
   *            the parent {@linkplain Stage} of the
   *            {@linkplain ProgressIndicator} alert
   * @param progressTask
   *            the {@linkplain ProgressIndicator} {@linkplain Task} ran in
   *            the {@linkplain Service}
   * @return the {@linkplain ProgressIndicator} alert {@linkplain Service}
   */
  public static <T> Service<T> alertProgress(final Stage parent, final Task<T> progressTask) {
    final Light light = new Light.Distant();
    final Lighting lighting = new Lighting();
    lighting.setSurfaceScale(1d);
    lighting.setLight(light);
    //final ProgressIndicator pi = ProgressIndicatorBuilder.create().maxWidth(200d).effect(lighting).build();
    final ProgressIndicator pi = ProgressBarBuilder.create().maxWidth(parent.getWidth() / 2d).maxHeight(25d).effect(lighting).build();
    final Stage alert = alert(parent, parent.getWidth(), parent.getHeight(), Modality.APPLICATION_MODAL, pi,
        LabelBuilder.create().text(RS.rbLabel(KEY.SENDING)).build());
    final Service<T> service = new Service<T>() {
      @Override
      protected Task<T> createTask() {
        return progressTask;
      }
    };
    final Effect origEffect = parent.getScene().getRoot().getEffect();
    progressTask.stateProperty().addListener(new ChangeListener<State>() {
      @Override
      public void changed(ObservableValue<? extends State> observable, State oldValue, State newValue) {
        if (newValue == State.RUNNING && !alert.isShowing()) {
          Platform.runLater(new Runnable() {
            public void run() {
              parent.getScene().getRoot().setEffect(ColorAdjustBuilder.create().brightness(-0.5d).build());
              alert.show();
            }
          });
        } else if (newValue == State.SUCCEEDED || newValue == State.FAILED || newValue == State.CANCELLED) {
          Platform.runLater(new Runnable() {
            public void run() {
              alert.hide();
              parent.getScene().getRoot().setEffect(origEffect);
            }
          });
        }
      }
    });
    return service;
  }

  /**
   * Creates an alert {@linkplain Stage}
   *
   * @param parent
   *            the parent of the alert (when present the alert will be
   *            centered over the parent)
   * @param width
   *            the width of the alert
   * @param height
   *            the height of the alert
   * @param modality
   *            the {@linkplain Modality} of the alert
   * @param children
   *            the children of the alert
   * @return the alert
   */
  public static Stage alert(final Stage parent, final double width, final double height,
      final Modality modality, final Node... children) {
    final Stage stage = new Stage();
    stage.initModality(modality);
    stage.initStyle(StageStyle.TRANSPARENT);
    final StackPane root = new StackPane();
    root.setPrefSize(width, height);
    if (children != null) {
      root.getChildren().addAll(children);
    }
    if (parent != null) {
      stage.setScene(new Scene(root, parent.getWidth(), parent.getHeight(), Color.TRANSPARENT));
      stage.initOwner(parent);
      stage.setX(parent.getX() + parent.getScene().getWidth() / 2d - width / 2d);
      stage.setY(parent.getY() + parent.getScene().getHeight() / 2d - height / 2d);
      parent.xProperty().addListener(new ChangeListener<Number>() {
        @Override
        public void changed(ObservableValue<? extends Number> observable, Number oldValue, Number newValue) {
          stage.setX(newValue.doubleValue() + parent.getScene().getWidth() / 2d - width / 2d);
        }
      });
      parent.yProperty().addListener(new ChangeListener<Number>() {
        @Override
        public void changed(ObservableValue<? extends Number> observable, Number oldValue, Number newValue) {
          stage.setY(newValue.doubleValue() + parent.getScene().getHeight() / 2d - height / 2d);
        }
      });
    } else {
      stage.setScene(new Scene(root, width, height, Color.TRANSPARENT));
    }
    return stage;
  }

  /**
   * Creates a dialog window {@linkplain Stage} that is shown when the
   * {@linkplain DialogService#start()} is called and hidden when the submit
   * {@linkplain Service#restart()} returns {@linkplain State#SUCCEEDED}. When
   * a {@linkplain Task} throws an {@linkplain Exception} the
   * {@linkplain Exception#getMessage()} will be used to update the messageHeader of
   * the dialog.
   *
   * @param parent
   *            the parent {@linkplain Stage}
   * @param titleKey
   *            the resource key for the {@linkplain Stage#setTitle(String)}
   * @param headerText
   *            the text displayed in the header of the {@linkplain Dialog}
   * @param submitLabelKey
   *            the resource key for the {@linkplain Button#setText(String)}
   * @param width
   *            the width of the {@linkplain Stage}
   * @param height
   *            the height of the {@linkplain Stage}
   * @param submitService
   *            the {@linkplain Service} ran whenever the submit
   *            {@linkplain Button} is clicked
   * @param children
   *            the child {@linkplain Node}s that will be added between the
   *            messageHeader and submit {@linkplain Button} (if any). If any of the
   *            {@linkplain Node}s are {@linkplain Button}s they will be added
   *            to the internal {@linkplain Button} {@linkplain FlowPane}
   *            added to the bottom of the dialog.
   * @return the {@linkplain DialogService}
   */
  public static DialogService dialogService(final Stage parent, final KEY titleKey, final String headerText,
      final KEY submitLabelKey, final double width, final double height, final Service<Void> submitService,
      final Modality modality, final Node... children) {
    final Stage window = new Stage();
    window.initModality(modality);
    final Button submitBtn = ButtonBuilder.create().text(RS.rbLabel(submitLabelKey == null ? KEY.SUBMIT :
      submitLabelKey)).defaultButton(true).onAction(new EventHandler<ActionEvent>() {
      @Override
      public void handle(final ActionEvent actionEvent) {
        submitService.restart();
      }
    }).build();
    final Dialog dialog = new Dialog(parent, window, titleKey, headerText, submitBtn, width, height, children);
    return new DialogService(dialog, submitService);
  }

  /**
   * Dialog {@linkplain Stage}
   */
  public static class Dialog {

    private final Stage parent;
    private final Stage stage;
    private final Text header;
    private final Text messageHeader;
    private final VBox content;

    /**
     * Constructor
     *
     * @param parent
     *            the parent {@linkplain Stage}
     * @param stage
     *            the dialog {@linkplain Stage}
     * @param titleKey
     *            the {@linkplain KEY} used for the title
     * @param headerText
     *            the text used for the header
     * @param submitButton
     *            the submit {@linkplain Button}
     * @param width
     *            the width
     * @param height
     *            the height
     * @param children
     *            the child {@linkplain Node}s
     */
    public Dialog(final Stage parent, final Stage stage, final KEY titleKey,
        final String headerText, final Button submitButton,
        final double width, final double height, final Node... children) {
      header = TextBuilder.create().text(headerText)
          .styleClass("dialog-title").wrappingWidth(width / 1.2d)
          .build();
      messageHeader = TextBuilder.create()
          .styleClass("dialog-message").wrappingWidth(width / 1.2d)
          .build();
      this.parent = parent;
      this.stage = stage;
      if (parent != null) {
        stage.initModality(Modality.APPLICATION_MODAL);
      }
      stage.initStyle(StageStyle.TRANSPARENT);
      RS.img(stage);
      if (titleKey != null) {
        stage.setTitle(RS.rbLabel(titleKey));
      }
      content = VBoxBuilder.create().styleClass("dialog")
          .build();
      content.setMaxSize(width, height);
      stage.setScene(new Scene(content, width, height, Color.TRANSPARENT));
      stage.getScene().getStylesheets().add(RS.path(RS.CSS_MAIN));
      final FlowPane flowPane = new FlowPane();
      flowPane.setAlignment(Pos.CENTER);
      flowPane.setVgap(20d);
      flowPane.setHgap(10d);
      flowPane.setPrefWrapLength(width);
      if (submitButton != null) {
        flowPane.getChildren().add(submitButton);
      }
      content.getChildren().addAll(header, messageHeader);
      if (children != null && children.length > 0) {
        for (final Node node : children) {
          if (node == null) {
            continue;
          }
          if (node instanceof Button) {
            flowPane.getChildren().add(node);
          } else {
            content.getChildren().add(node);
          }
        }
      }
      content.getChildren().addAll(flowPane);
    }

    /**
     * @return the parent {@linkplain Stage}
     */
    public Stage getParent() {
      return parent;
    }

    /**
     * @return the {@linkplain Dialog} {@linkplain Stage}
     */
    public Stage getStage() {
      return stage;
    }

    /**
     * @return the header text
     */
    public Text getHeader() {
      return header;
    }

    /**
     * @return the message header
     */
    public Text getMessageHeader() {
      return messageHeader;
    }

    /**
     * @return the content {@linkplain VBox}
     */
    public VBox getContent() {
      return content;
    }
  }
 
  /**
   * Creates a {@linkplain Popup} alert
   *
   * @param width
   *            the width of the {@linkplain Popup}
   * @param height
   *            the height of the {@linkplain Popup}
   * @param headerText
   *            the text to display in the header {@linkplain Region}
   * @param closeButtonKey
   *            the {@linkplain KEY} used for the text of the alternative
   *            close {@linkplain Button} when no children are specified
   * @param children
   *            the children of the {@linkplain Popup} (when null a close
   *            button will be added)
   * @return the {@linkplain Popup}
   */
  public static Popup alert(final double width, final double height,
      final String headerText, final KEY closeButtonKey, final Node... children) {
    final Popup alert = new Popup();
    alert.setAutoFix(true);
    alert.setAutoHide(false);
    alert.setHideOnEscape(false);
    if (headerText != null && !headerText.isEmpty()) {
      alert.getContent().add(new Label(headerText));
    }
    if (children != null && children.length > 0) {
      alert.getContent().addAll(children);
    } else {
      final KEY cbk = closeButtonKey != null ? closeButtonKey : KEY.CLOSE;
      final Button cb = ButtonBuilder.create().text(RS.rbLabel(cbk)).build();
      cb.setOnMouseClicked(new EventHandler<MouseEvent>() {
        @Override
        public void handle(final MouseEvent event) {
          alert.hide();
        }
      });
    }
    alert.sizeToScene();
    return alert;
  }
 
  /**
   * Creates a {@linkplain Timeline} that will alternate the
   * {@linkplain Color}s of the {@linkplain InnerShadow} or {@link DropShadow}
   * when played
   *
   * @param effect
   *            the {@linkplain InnerShadow} or {@link DropShadow}
   * @param onColor
   *            the on {@linkplain Color}
   * @param offColor
   *            the off {@linkplain Color}
   * @param cycleCount
   *            the {@linkplain Timeline#getCycleCount()}
   * @return the {@linkplain Timeline}
   */
  public static Timeline createShadowColorIndicatorTimeline(final Effect effect,
      final Color onColor, final Color offColor, final int cycleCount) {
    if (effect instanceof DropShadow) {
      ((DropShadow) effect).setColor(offColor);
    } else {
      ((InnerShadow) effect).setColor(offColor);
    }
    final Timeline timeline = new Timeline();
    timeline.setCycleCount(cycleCount <=0 ? Timeline.INDEFINITE : cycleCount);
    timeline.setAutoReverse(true);
    final KeyFrame kf = new KeyFrame(Duration.millis(500), new EventHandler<ActionEvent>() {
      @Override
      public void handle(final ActionEvent event) {
        if (effect instanceof DropShadow) {
          ((DropShadow) effect).setColor(
              ((DropShadow) effect).getColor().equals(offColor) ? onColor : offColor);
        } else {
          ((InnerShadow) effect).setColor(
              ((InnerShadow) effect).getColor().equals(offColor) ? onColor : offColor);
        }
      }
    });
    timeline.setOnFinished(new EventHandler<ActionEvent>() {
      @Override
      public void handle(final ActionEvent event) {
        if (effect instanceof DropShadow) {
          ((DropShadow) effect).setColor(offColor);
        } else {
          ((InnerShadow) effect).setColor(offColor);
        }
      }
    });
    timeline.getKeyFrames().add(kf);
    return timeline;
  }

  /**
   * Creates a background display {@linkplain Region}
   *
   * @param padding
   *            the padding in the {@linkplain GridPane} group
   * @param gapBetweenChildren
   *            the vertical and/or horizontal gap between cells
   * @param numItemsPerRow
   *            the number of items per row
   * @param gaugeStyle
   *            true to style the children as gauges
   * @param startCol
   *            the column to start adding the {@linkplain Node}(s) to
   * @param startRow
   *            the row to start adding the {@linkplain Node}(s) to
   * @param nodes
   *            the {@linkplain Node}s to add to the display
   * @return the background display {@linkplain Region}
   */
  public static final GridPane createBackgroundDisplay(final Insets padding, final double gapBetweenChildren,
      final int numItemsPerRow, final boolean gaugeStyle, final int startCol, final int startRow,
      final Node... nodes) {
    final GridPane grid = new GridPane();
    if (padding != null) {
      grid.setPadding(padding);
    }
    grid.setHgap(gapBetweenChildren);
    grid.setVgap(gapBetweenChildren);
    int col = (startCol >= 0 ? startCol : 0)-1, row = startRow >= 0 ? startRow : 0;
    for (final Node node : nodes) {
      if (gaugeStyle) {
        node.getStyleClass().add("gauge");
      }
      grid.add(node, ++col, row);
      row = col == (numItemsPerRow - 1) ? row + 1 : row;
      col = col == (numItemsPerRow - 1) ? -1 : col;
    }
//    grid.setAlignment(Pos.CENTER_LEFT);
    grid.getStyleClass().add("background-display");
    return grid;
  }
 
  /**
   * Checks the {@linkplain MouseEvent} to see if it's a normal primary press
   * of the mouse without any other keys held down
   *
   * @param event
   *            the pressed {@linkplain MouseEvent}
   * @return true when just the primary is pressed
   */
  public static boolean isPrimaryPress(final MouseEvent event) {
    return !(event.isMetaDown() || event.isControlDown() || event.isAltDown() ||
        event.isShiftDown() || event.isShortcutDown() || !event.isPrimaryButtonDown());
  }
 
  /**
   * A {@linkplain Service} for showing and hiding a {@linkplain Stage}
   */
  public static class DialogService extends Service<Void> {

    private final Dialog dialog;
    private final Effect origEffect;
    private final Service<Void> submitService;
   
    /**
     * Creates a dialog service for showing and hiding a {@linkplain Stage}
     *
     * @param parent
     *            the parent {@linkplain Stage}
     * @param window
     *            the window {@linkplain Stage} that will be shown/hidden
     * @param messageHeader
     *            the messageHeader {@linkplain Text} used for the service
     *            that will be updated with exception information as the
     *            submitService informs the {@linkplain DialogService} of
     * @param submitService
     *            the {@linkplain Service} that will be listened to for
     *            {@linkplain State#SUCCEEDED} at which point the
     *            {@linkplain DialogService} window {@linkplain Stage} will
     *            be hidden
     */
    protected DialogService(final Dialog dialog, final Service<Void> submitService) {
      this.dialog = dialog;
      this.origEffect = hasParentSceneRoot() ? dialog.getParent().getScene().getRoot().getEffect() : null;
      this.submitService = submitService;
      this.submitService.stateProperty().addListener(new ChangeListener<State>() {
        @Override
        public void changed(ObservableValue<? extends State> observable, State oldValue, State newValue) {
          if (submitService.getException() != null) {
            // service indicated that an error occurred
            dialog.getMessageHeader().setText(submitService.getException().getMessage());
          } else if (newValue == State.SUCCEEDED) {
            dialog.getStage().getScene().getRoot().setEffect(ColorAdjustBuilder.create().brightness(-0.5d).build());
            Platform.runLater(createHideTask());
          }
        }
      });
    }
   
    /**
     * {@inheritDoc}
     */
    @Override
    protected Task<Void> createTask() {
      return dialog.getStage().isShowing() ? createHideTask() : createShowTask();
    }
   
    /**
     * @return a task that will show the service {@linkplain Stage}
     */
    protected Task<Void> createShowTask() {
      final Task<Void> showTask = new Task<Void>() {
        @Override
        protected Void call() throws Exception {
          Platform.runLater(new Runnable() {
            public void run() {
              if (hasParentSceneRoot()) {
                dialog.getParent().getScene().getRoot().setEffect(
                    ColorAdjustBuilder.create().brightness(-0.5d).build());
              }
              dialog.getStage().show();
              dialog.getStage().centerOnScreen();
            }
          });
          return null;
        }
      };
      showTask.stateProperty().addListener(new ChangeListener<State>() {
        @Override
        public void changed(ObservableValue<? extends State> observable, State oldValue, State newValue) {
          if (newValue == State.FAILED || newValue == State.CANCELLED) {
            Platform.runLater(createHideTask());
          }
        }
      });
      return showTask;
    }
   
    /**
     * @return a task that will hide the service {@linkplain Stage}
     */
    protected Task<Void> createHideTask() {
      final Task<Void> closeTask = new Task<Void>() {
        @Override
        protected Void call() throws Exception {
          dialog.getStage().hide();
          if (hasParentSceneRoot()) {
            dialog.getParent().getScene().getRoot().setEffect(origEffect);
          }
          dialog.getStage().getScene().getRoot().setDisable(false);
          return null;
        }
      };
      return closeTask;
    }
   
    /**
     * @return true when the parent {@linkplain Stage#getScene()} has a valid {@linkplain Scene#getRoot()}
     */
    private boolean hasParentSceneRoot() {
      return dialog.getParent() != null && dialog.getParent().getScene() != null &&
          dialog.getParent().getScene().getRoot() != null;
    }

    /**
     * Hides the dialog used in the {@linkplain Service}
     */
    public void hide() {
      Platform.runLater(createHideTask());
    }

    /**
     * @return the {@link Stage} used by the {@link DialogService}
     */
    public Stage getStage() {
      return dialog.getStage();
    }
  }
}
TOP

Related Classes of org.ugate.gui.GuiUtil$Dialog

TOP
Copyright © 2018 www.massapi.com. All rights reserved.
All source code are property of their respective owners. Java is a trademark of Sun Microsystems, Inc and owned by ORACLE Inc. Contact coftware#gmail.com.