/* * Copyright 2014 MovingBlocks * * 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 org.terasology.rendering.nui.layers.mainMenu; import com.google.common.base.Preconditions; import org.slf4j.Logger; import org.slf4j.LoggerFactory; import org.terasology.assets.ResourceUrn; import org.terasology.rendering.nui.CoreScreenLayer; import org.terasology.rendering.nui.WidgetUtil; import org.terasology.rendering.nui.widgets.UIButton; import org.terasology.rendering.nui.widgets.UILabel; import java.util.concurrent.Callable; import java.util.concurrent.ExecutionException; import java.util.concurrent.FutureTask; import java.util.function.Consumer; /** * A popup message that is shown while a long-term background operation is running. * Some of them can be cancelled. * */ public class WaitPopup<T> extends CoreScreenLayer { public static final ResourceUrn ASSET_URI = new ResourceUrn("engine:waitPopup!instance"); private static final Logger logger = LoggerFactory.getLogger(WaitPopup.class); private FutureTask<T> parallelTask; private Thread thread; private Consumer<T> resultEvent; private UILabel titleLabel; private UILabel messageLabel; private UIButton cancelButton; @Override public void initialise() { titleLabel = find("title", UILabel.class); Preconditions.checkNotNull(titleLabel, "UILabel 'title' not found"); messageLabel = find("message", UILabel.class); Preconditions.checkNotNull(messageLabel, "UILabel 'message' not found"); cancelButton = find("cancel", UIButton.class); Preconditions.checkNotNull(cancelButton, "UIButton 'cancel' not found"); } public void setMessage(String title, String message) { titleLabel.setText(title); messageLabel.setText(message); } @Override public void update(float delta) { super.update(delta); if (parallelTask == null) { return; // idle } if (!parallelTask.isDone()) { return; // still running } if (parallelTask.isCancelled()) { // wait for the thread to die if (!thread.isAlive()) { getManager().popScreen(); } return; } try { T result = parallelTask.get(); getManager().popScreen(); if (resultEvent != null) { resultEvent.accept(result); } } catch (InterruptedException | ExecutionException e) { logger.warn("An error occurred during execution", e); getManager().popScreen(); } } /** * @param runnable will be called once the result is available */ public void onSuccess(Consumer<T> runnable) { this.resultEvent = runnable; } /** * @param operation the operation to run - the executing thread will be interrupted when the operation is cancelled * @param canBeCancelled true if the operation is aborted when the {@link Thread#isInterrupted()} flag is set * @throws NullPointerException if operation is null * @throws IllegalArgumentException if startOperation() was called before */ public void startOperation(Callable<T> operation, boolean canBeCancelled) { Preconditions.checkState(parallelTask == null, "startOperation() cannot be called twice"); cancelButton.setVisible(canBeCancelled); parallelTask = new FutureTask<>(operation); thread = new Thread(parallelTask, "Parallel Operation"); thread.start(); WidgetUtil.trySubscribe(this, "cancel", button -> parallelTask.cancel(true)); } public boolean canBeCancelled() { return cancelButton.isVisible(); } }