All Downloads are FREE. Search and download functionalities are using the official Maven repository.

de.saxsys.mvvmfx.utils.commands.CompositeCommand Maven / Gradle / Ivy

/*******************************************************************************
 * Copyright 2015 Alexander Casall, Manuel Mauky
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *   http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 ******************************************************************************/
package de.saxsys.mvvmfx.utils.commands;

import javafx.beans.binding.Bindings;
import javafx.beans.binding.BooleanBinding;
import javafx.beans.binding.DoubleBinding;
import javafx.beans.binding.DoubleExpression;
import javafx.beans.property.ReadOnlyBooleanProperty;
import javafx.beans.property.ReadOnlyDoubleProperty;
import javafx.beans.property.ReadOnlyDoubleWrapper;
import javafx.beans.value.ObservableDoubleValue;
import javafx.collections.FXCollections;
import javafx.collections.ListChangeListener;
import javafx.collections.ObservableList;
import eu.lestard.doc.Beta;

import java.util.concurrent.Callable;
import java.util.function.Function;

/**
 * CompositeCommand is an aggregation of other commands - a list of {@link Command} references internally.
 * 

* It allows you to hook up multiple command targets to a single root command that itself can be hooked up to a command * source such as a button or menu item. *

* The {@link CompositeCommand} can hold references to any {@link Command object} but typically you will use it in * conjunction with {@link DelegateCommand}s. * *

* When the {@link CompositeCommand#execute} method is invoked it will invoke the {@link Command#execute} method on each * of the child commands. * *

* * When {@link #isExecutable()} is called to determine whether the command is enabled, it polls its child commands for * their result from {@link #isExecutable()}. * * @author alexander.casall * */ @Beta public class CompositeCommand extends CommandBase { private final ObservableList registeredCommands = FXCollections.observableArrayList(); ReadOnlyDoubleWrapper progress = new ReadOnlyDoubleWrapper(); /** * Creates a {@link CompositeCommand} with given commands. * * @param commands * to aggregate */ public CompositeCommand(Command... commands) { initRegisteredCommandsListener(); this.registeredCommands.addAll(commands); } /** * Registers a new {@link Command} for aggregation. * * @param command * to register */ public void register(Command command) { registeredCommands.add(command); } /** * Unregisters a {@link Command} from aggregation. * * @param command * to unregister */ public void unregister(Command command) { registeredCommands.remove(command); } private void initRegisteredCommandsListener() { this.registeredCommands.addListener((ListChangeListener) c -> { while (c.next()) { if (registeredCommands.isEmpty()) { executable.unbind(); running.unbind(); progress.unbind(); } else { BooleanBinding executableBinding = constantOf(true); BooleanBinding runningBinding = constantOf(false); for (Command registeredCommand : registeredCommands) { ReadOnlyBooleanProperty currentExecutable = registeredCommand.executableProperty(); ReadOnlyBooleanProperty currentRunning = registeredCommand.runningProperty(); executableBinding = executableBinding.and(currentExecutable); runningBinding = runningBinding.or(currentRunning); } executable.bind(executableBinding); running.bind(runningBinding); initProgressBinding(); } } }); } private void initProgressBinding() { DoubleExpression tmp = constantOf(0); for (Command command : registeredCommands) { final ReadOnlyDoubleProperty progressProperty = command.progressProperty(); /** * When the progress of a command is "undefined", the progress property has a value of -1. * But in our use case we like to have a value of 0 in this case. * Therefore we create a custom binding here. */ final DoubleBinding normalizedProgress = Bindings .createDoubleBinding(() -> (progressProperty.get() == -1) ? 0.0 : progressProperty.get(), progressProperty); tmp = tmp.add(normalizedProgress); } int divisor = registeredCommands.isEmpty() ? 1 : registeredCommands.size(); progress.bind(Bindings.divide(tmp, divisor)); } @Override public void execute() { if (!isExecutable()) { throw new RuntimeException("Not executable"); } else { if (!registeredCommands.isEmpty()) { registeredCommands.forEach(t -> t.execute()); } } } @Override public double getProgress() { return progressProperty().get(); } @Override public ReadOnlyDoubleProperty progressProperty() { return progress; } private BooleanBinding constantOf(boolean defaultValue) { return new BooleanBinding() { @Override protected boolean computeValue() { return defaultValue; } }; } private DoubleBinding constantOf(double defaultValue) { return new DoubleBinding() { @Override protected double computeValue() { return defaultValue; } }; } }





© 2015 - 2025 Weber Informatics LLC | Privacy Policy