package lighthouse.controls;
import de.jensd.fx.fontawesome.*;
import javafx.application.*;
import javafx.beans.binding.*;
import javafx.beans.property.*;
import javafx.beans.value.*;
import javafx.fxml.*;
import javafx.scene.*;
import javafx.scene.control.*;
import javafx.scene.effect.*;
import javafx.scene.image.*;
import javafx.scene.input.*;
import javafx.scene.layout.*;
import javafx.scene.shape.*;
import lighthouse.*;
import lighthouse.protocol.*;
import lighthouse.subwindows.*;
import lighthouse.utils.*;
import org.bitcoinj.utils.*;
import org.slf4j.*;
import javax.annotation.*;
import static javafx.beans.binding.Bindings.*;
import static lighthouse.protocol.LHUtils.*;
import static lighthouse.utils.GuiUtils.*;
import static lighthouse.utils.I18nUtil.*;
/** An entry in the project list that is shown on the overview page */
public class ProjectOverviewWidget extends HBox {
private static final Logger log = LoggerFactory.getLogger(ProjectOverviewWidget.class);
@FXML Label titleLabel;
@FXML MarkDownNode blurbFlow;
@FXML Label ownershipIcon;
@FXML HBox titleHBox;
@FXML Circle progressCircle;
@FXML Line progressLine;
@FXML ImageView coverImage;
@FXML Node loadingIndicatorArea;
@FXML ProgressIndicator loadingIndicator;
private Project project;
private final SimpleBooleanProperty isLoading = new SimpleBooleanProperty();
public ProjectOverviewWidget(Project project, LongProperty pledgedAmount,
ObservableObjectValue<LighthouseBackend.ProjectState> state) {
this.project = project;
FXMLLoader loader = new FXMLLoader(GuiUtils.getResource("controls/project_overview_widget.fxml"), I18nUtil.translations);
loader.setRoot(this);
loader.setController(this);
uncheck(loader::load);
titleLabel.setText(project.getTitle());
blurbFlow.setText(project.getMemo());
blurbFlow.setUrlOpener(url -> Main.instance.getHostServices().showDocument(url));
if (Main.wallet.isProjectMine(project)) {
AwesomeDude.setIcon(ownershipIcon, AwesomeIcon.HOME, "25");
} else {
titleHBox.getChildren().remove(ownershipIcon);
}
// Make the cover image go grey when claimed and blurred when loading. Make a loading indicator fade in/out.
final Image image = new Image(project.getCoverImage().newInput(), Project.COVER_IMAGE_WIDTH, Project.COVER_IMAGE_HEIGHT, false, true);
ColorAdjust colorAdjust = new ColorAdjust();
colorAdjust.saturationProperty().bind(
when(
or(
equal(state, LighthouseBackend.ProjectState.CLAIMED),
equal(state, LighthouseBackend.ProjectState.UNKNOWN)
)
).then(-0.9).otherwise(0.0)
);
if (GuiUtils.isSoftwarePipeline()) {
// SW pipeline cannot handle gaussian blurs with acceptable performance.
coverImage.setEffect(colorAdjust);
} else {
GaussianBlur blur = new GaussianBlur();
blur.setInput(colorAdjust);
animatedBind(coverImage, blur.radiusProperty(), when(isLoading).then(25).otherwise(0.0));
coverImage.setEffect(blur);
}
coverImage.setImage(image);
coverImage.setClip(new Rectangle(coverImage.getFitWidth(), coverImage.getFitHeight()));
animatedBind(loadingIndicatorArea, loadingIndicatorArea.opacityProperty(), when(isLoading).then(1.0).otherwise(0.0));
// Hack around a bug in jfx: progress indicator leaks the spinner animation even if it's invisible so we have
// to forcibly end the animation here to avoid burning cpu.
loadingIndicator.progressProperty().bind(
when(loadingIndicatorArea.opacityProperty().greaterThan(0.0)).then(-1).otherwise(0)
);
// Make the progress line+circle follow the pledged amount and disappear if there are no pledges yet.
//
// This is all calculated lazily, so changes in pledgedProperty propagate through to the widgets only
// when they actually need to be drawn.
DoubleBinding progress = min(1.0, divide(pledgedAmount, (double) project.getGoalAmount().value));
NumberBinding pixelWidth = multiply(widthProperty(), progress);
// These come pre-bound in the FXML just to make things look more clear in Scene Builder, so unbind them here.
progressLine.endXProperty().unbind();
progressCircle.centerXProperty().unbind();
animatedBind(progressLine, progressLine.endXProperty(), pixelWidth);
animatedBind(progressCircle, progressCircle.centerXProperty(), pixelWidth);
progressLine.visibleProperty().bind(pixelWidth.greaterThan(0.0));
progressCircle.visibleProperty().bind(progressLine.visibleProperty());
// Creating a tooltip is only possible on the FX thread, annoyingly enough.
Platform.runLater(() -> {
Tooltip tooltip = new Tooltip();
// TODO: Maybe use Adam's BtcFormat class here instead.
// TRANS: %s = amount in BTC
tooltip.textProperty().bind(new ReactiveCoinFormatter(tr("%s BTC raised so far"), MonetaryFormat.BTC, pledgedAmount));
Tooltip.install(progressCircle, tooltip);
Tooltip tt = new Tooltip(tr("You created this project"));
tt.getStyleClass().add("default-font");
ownershipIcon.setTooltip(tt);
});
}
public void onCheckStatusChanged(@Nullable LighthouseBackend.CheckStatus checkStatus) {
isLoading.set(checkStatus != null && checkStatus.getInProgress());
}
private ExportWindow.DragData dragData;
@FXML
public void dragDetected(MouseEvent event) {
dragData = ExportWindow.startDrag(project.getSuggestedFileName(), project.getProto(),
ExportWindow.PROJECT_MIME_TYPE, this);
}
@FXML
public void dragDone(DragEvent event) {
log.info("Drag of project done");
dragData.done();
}
}