org.datafx.controller.flow.Flow Maven / Gradle / Ivy
/**
* Copyright (c) 2011, 2014, Jonathan Giles, Johan Vos, Hendrik Ebbers
* All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions are met:
* * Redistributions of source code must retain the above copyright
* notice, this list of conditions and the following disclaimer.
* * Redistributions in binary form must reproduce the above copyright
* notice, this list of conditions and the following disclaimer in the
* documentation and/or other materials provided with the distribution.
* * Neither the name of DataFX, the website javafxdata.org, nor the
* names of its contributors may be used to endorse or promote products
* derived from this software without specific prior written permission.
*
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND
* ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
* WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
* DISCLAIMED. IN NO EVENT SHALL BE LIABLE FOR ANY
* DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
* (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
* LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
* ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
* (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
* SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*/
package org.datafx.controller.flow;
import javafx.scene.Node;
import javafx.scene.Scene;
import javafx.scene.control.Tab;
import javafx.scene.layout.StackPane;
import javafx.stage.Stage;
import org.datafx.controller.ViewConfiguration;
import org.datafx.controller.context.ViewMetadata;
import org.datafx.controller.flow.action.*;
import org.datafx.controller.flow.container.DefaultFlowContainer;
import org.datafx.controller.flow.context.ViewFlowContext;
import java.lang.reflect.Method;
import java.util.HashMap;
import java.util.Map;
/**
* This class defines a flow. A flow is a map of different views that are linked.
* A flow can define actions for each view or global actions for the complete flow.
* The class provides a fluent API to create a flow with views and actions.
*
* @author Hendrik Ebbers
*/
public class Flow {
private Class> startViewControllerClass;
private Map, Map> viewFlowMap;
private Map globalFlowMap;
private ViewConfiguration viewConfiguration;
/**
* Creates a new Flow with the given controller for the start view and a
* view configuration for all views.
* The start view must be a view controller as specified in the DataFX-Controller API.
* See FXMLController for more information
*
* @param startViewControllerClass Controller class of the start view
* @param viewConfiguration Configuration for all views of the flow
* @see org.datafx.controller.FXMLController
*/
public Flow(Class> startViewControllerClass, ViewConfiguration viewConfiguration) {
this.startViewControllerClass = startViewControllerClass;
globalFlowMap = new HashMap<>();
viewFlowMap = new HashMap<>();
this.viewConfiguration = viewConfiguration;
}
/**
* Creates a new Flow with the given controller for the start view.
* The start view must be a view controller as specified in the DataFX-Controller API.
* See FXMLController for more information
*
* @param startViewControllerClass Controller class of the start view
*/
public Flow(Class> startViewControllerClass) {
this(startViewControllerClass, new ViewConfiguration());
}
/**
* Returns the view configuration for all views of the flow
*
* @return the view configuration
*/
public ViewConfiguration getViewConfiguration() {
return viewConfiguration;
}
/**
* Creates a handler that can be used to run the flow. The Flow class provides only the definition of a flow. To run a flow in the JavaFX scene graph a FlowHandler is needed
*
* @param flowContext The context for the flow
* @return a flow handler to run the flow
*/
public FlowHandler createHandler(ViewFlowContext flowContext) {
return new FlowHandler(this, flowContext);
}
/**
* Creates a handler that can be used to run the flow. The Flow class provides only the definition of a flow. To run a flow in the JavaFX scene graph a FlowHandler is needed
*
* @return a flow handler to run the flow
*/
public FlowHandler createHandler() throws FlowException {
return createHandler(new ViewFlowContext());
}
/**
* Adds a global action to the flow. The action is registered by the given unique ID and can be called at runtime by using the id. A global action can be called from each view and from outside of the flow.
*
* @param actionId unique action id
* @param action the action
* @return returns this flow (for the fluent API)
*/
public Flow withGlobalAction(String actionId, FlowAction action) {
addGlobalAction(actionId, action);
return this;
}
/**
* Adds a run action as a global action to the flow. Internally a SimpleFlowAction will be created and added to the flow
*
* @param actionId unique action id
* @param actionClass a runnable that will be called whenever the action is called
* @return returns this flow (for the fluent API)
* @see FlowTaskAction
*/
public Flow withGlobalTaskAction(String actionId,
Class extends Runnable> actionClass) {
addGlobalAction(actionId, new FlowTaskAction(actionClass));
return this;
}
public Flow withGlobalTaskAction(String actionId,
Runnable action) {
addGlobalAction(actionId, new FlowTaskAction(action));
return this;
}
/**
* Adds a link action as a global action to the flow. Internally a FlowLink will be created and added to the flow
*
* @param actionId unique action id
* @param controllerClass the controller of the view that should be shown whenever the action will be called
* @return returns this flow (for the fluent API)
* @see FlowLink
*/
public Flow withGlobalLink(String actionId, Class> controllerClass) {
addGlobalAction(actionId, new FlowLink<>(controllerClass));
return this;
}
public Flow withGlobalBackAction(String actionId) {
addGlobalAction(actionId, new FlowBackAction());
return this;
}
/**
* Adds a action to the view of the given view controller. The action is registered by the given unique ID and can be called at runtime by using the id. the action can only be called when the view of the given controller is the active view of the flow
*
* @param controllerClass controller class for the view of the action
* @param actionId unique action id
* @param action the action
* @return returns this flow (for the fluent API)
*/
public Flow withAction(Class> controllerClass, String actionId,
FlowAction action) {
addActionToView(controllerClass, actionId, action);
return this;
}
public Flow withLink(Class> fromControllerClass, String actionId,
Class> toControllerClass) {
addActionToView(fromControllerClass, actionId, new FlowLink<>(
toControllerClass));
return this;
}
public Flow withTaskAction(Class> controllerClass, String actionId,
Class extends Runnable> actionClass) {
addActionToView(controllerClass, actionId, new FlowTaskAction(
actionClass));
return this;
}
public Flow withTaskAction(Class> controllerClass, String actionId,
Runnable action) {
addActionToView(controllerClass, actionId, new FlowTaskAction(
action));
return this;
}
public Flow withGlobalCallMethodAction(String actionId,
String actionMethodName) {
addGlobalAction(actionId, new FlowMethodAction(actionMethodName));
return this;
}
public Flow withCallMethodAction(Class> controllerClass, String actionId,
String actionMethodName) {
addActionToView(controllerClass, actionId, new FlowMethodAction(actionMethodName));
return this;
}
public Flow withBackAction(Class> controllerClass, String actionId) {
addActionToView(controllerClass, actionId, new FlowBackAction());
return this;
}
public void addActionToView(Class> controllerClass, String actionId,
FlowAction action) {
if (viewFlowMap.get(controllerClass) == null) {
viewFlowMap.put(controllerClass, new HashMap());
}
viewFlowMap.get(controllerClass).put(actionId, action);
}
public void addGlobalAction(String actionId, FlowAction action) {
globalFlowMap.put(actionId, action);
}
public FlowAction getGlobalActionById(String actionId) {
return globalFlowMap.get(actionId);
}
public Class> getStartViewControllerClass() {
return startViewControllerClass;
}
public void addActionsToView(FlowView newView) {
Map viewActionMap = viewFlowMap.get(newView
.getViewContext().getController().getClass());
if (viewActionMap != null) {
for (String actionId : viewActionMap.keySet()) {
newView.addAction(actionId, viewActionMap.get(actionId));
}
}
for (Method method : newView
.getViewContext().getController().getClass().getMethods()) {
ActionMethod actionMethod = method.getAnnotation(ActionMethod.class);
if (actionMethod != null) {
newView.addAction(actionMethod.value(), new FlowMethodAction(method.getName()));
}
}
}
/**
* Start the flow with a default handler and a default FlowContainer
* This will start the flow using a DefaultFlowContainer, which returns a
* StackPane.
*
* @return the Parent that will be used for rendering
* @throws FlowException
*/
public StackPane start() throws FlowException {
return start(new DefaultFlowContainer());
}
/**
* Starts the flow directly in a Stage. This method is usefull if an application contains of one main flow. Because
* this flow can contain several sub-flows this is the prefered way to create a DataFX based application. The title
* of the Stage will be bound to the title of the flow metadata and will change whenever the flow title fill change.
* This can happen if a view of the flow defines its own title by using the title attribute of the @FXMLController
* annotation or the ViewMetadata of an view is changed in code.
*
* By using the method a flow based application can be created by only a few lines of code as shown in this example:
*
* public class Example extends Application {
*
* public static void main(String[] args) {
* launch(args);
* }
*
* @Override
* public void start(Stage primaryStage) throws Exception {
* new Flow(SimpleController.class).startInStage(primaryStage);
* }
*}
*
* @param stage The stage in that the flow should be displayed.
* @throws FlowException If the flow can't be created or started
*/
public void startInStage(Stage stage) throws FlowException {
FlowHandler handler = createHandler();
stage.setScene(new Scene(handler.start(new DefaultFlowContainer())));
handler.getCurrentViewMetadata().addListener((e) -> {
stage.titleProperty().unbind();
ViewMetadata metadata = handler.getCurrentViewMetadata().get();
if (metadata != null) {
stage.titleProperty().bind(metadata.titleProperty());
}
});
stage.titleProperty().unbind();
ViewMetadata metadata = handler.getCurrentViewMetadata().get();
if (metadata != null) {
stage.titleProperty().bind(metadata.titleProperty());
}
stage.show();
}
public Tab startInTab() throws FlowException {
return startInTab(new DefaultFlowContainer());
}
public Tab startInTab(FlowContainer container) throws FlowException {
return createHandler().startInTab(container);
}
/**
* Start the flow with a default handler and a provided FlowContainer
*
* @param
* @param flowContainer the FlowContainer used to visualize this flow
* @return the Parent that will be used for rendering
* @throws FlowException
*/
public T start(FlowContainer flowContainer) throws FlowException {
createHandler().start(flowContainer);
return flowContainer.getView();
}
}
© 2015 - 2025 Weber Informatics LLC | Privacy Policy