// Copyright (c) 2023 Tobias Briones. All rights reserved.
// SPDX-License-Identifier: BSD-3-Clause
// This file is part of https://github.com/tobiasbriones/blog
package engineer.mathsoftware.blog.slides.ui;
import engineer.mathsoftware.blog.slides.*;
import engineer.mathsoftware.blog.slides.data.ImageItem;
import engineer.mathsoftware.blog.slides.drawing.GroupSlideDrawing;
import engineer.mathsoftware.blog.slides.drawing.SlideDrawing;
import javafx.beans.InvalidationListener;
import javafx.beans.property.*;
import javafx.beans.value.ObservableValue;
import javafx.scene.control.Button;
import javafx.scene.control.ComboBox;
import javafx.scene.control.ScrollPane;
import javafx.scene.layout.HBox;
import javafx.scene.paint.Color;
import java.util.Optional;
class SlideDrawingView {
public record SlideState(
SlideItem slideItem,
ImageItem imageItem,
Language language,
Color background,
String code,
SlideSize size,
boolean captionEnable,
Optional<Slide.Caption> caption
) {}
interface ChangeListener {
void onSlideChange(SlideState state);
void setState(ImageItem item);
}
private final SlideDrawingController controller;
private final SlideDrawing drawing;
private final ObjectProperty<SlideItem> slideProperty;
private final ObjectProperty<ImageItem> imageProperty;
private final ObjectProperty<Language> languageProperty;
private final ObjectProperty<Color> backgroundProperty;
private final ObjectProperty<String> codeProperty;
private final ObjectProperty<SlideSize> sizeProperty;
private final BooleanProperty captionEnableProperty;
private final StringProperty captionTitleProperty;
private final StringProperty captionSubTitleProperty;
private ChangeListener l;
private boolean isStateLoading;
SlideDrawingView(HBox view) {
controller = new SlideDrawingController();
drawing = new GroupSlideDrawing(view);
slideProperty = new SimpleObjectProperty<>();
imageProperty = new SimpleObjectProperty<>();
languageProperty = new SimpleObjectProperty<>();
backgroundProperty = new SimpleObjectProperty<>();
codeProperty = new SimpleObjectProperty<>();
sizeProperty = new SimpleObjectProperty<>();
captionEnableProperty = new SimpleBooleanProperty();
captionTitleProperty = new SimpleStringProperty();
captionSubTitleProperty = new SimpleStringProperty();
l = null;
isStateLoading = false;
}
void setStatus(BackgroundStatus newStatus) {
controller.setStatus(newStatus);
}
void setOnChangeListener(ChangeListener newListener) {
l = newListener;
}
ObjectProperty<SlideItem> slideProperty() {
return slideProperty;
}
ObjectProperty<ImageItem> imageProperty() {
return imageProperty;
}
ObjectProperty<Language> languageProperty() {
return languageProperty;
}
ObjectProperty<Color> backgroundProperty() {
return backgroundProperty;
}
ObjectProperty<String> codeProperty() {
return codeProperty;
}
ObjectProperty<SlideSize> sizeProperty() {
return sizeProperty;
}
BooleanProperty captionEnableProperty() {
return captionEnableProperty;
}
StringProperty captionTitleProperty() {
return captionTitleProperty;
}
StringProperty captionSubTitleProperty() {
return captionSubTitleProperty;
}
BooleanProperty autoSaveProperty() {
return controller.autoSaveProperty();
}
void init() {
InvalidationListener updateAll = ignore -> updateSlide();
slideProperty.addListener(updateAll);
languageProperty.addListener(updateAll);
backgroundProperty.addListener(updateAll);
codeProperty.addListener(updateAll);
sizeProperty.addListener(updateAll);
captionEnableProperty.addListener(updateAll);
captionTitleProperty.addListener(updateAll);
captionSubTitleProperty.addListener(updateAll);
imageProperty.addListener(this::onImageChange);
updateSlide();
}
void setViews(
ScrollPane scrollPane,
ComboBox<ShapeItem> shapeComboBox,
ComboBox<Palette> shapePaletteComboBox,
Button shapeBackButton
) {
controller.setScrollPane(scrollPane);
controller.setShapeComboBox(shapeComboBox);
controller.setShapePaletteComboBox(shapePaletteComboBox);
controller.setShapeBackButton(shapeBackButton);
}
void saveCurrentSlide() {
controller.saveCurrentSlide();
}
private void onImageChange(
ObservableValue<? extends ImageItem> observable,
ImageItem oldValue,
ImageItem newValue
) {
if (l == null) {
return;
}
// Avoids rendering while all properties are being set
isStateLoading = true;
l.setState(newValue);
isStateLoading = false;
controller.onSlideChanged(newValue);
// Update manually
updateSlide();
}
private void updateSlide() {
if (isStateLoading) {
return;
}
if (imageProperty.isNull().get()) {
return;
}
var item = slideProperty.get();
var slide = switch (item) {
case CodeSnippet -> new Slide.CodeSnippet(
codeProperty.get(),
languageProperty.get(),
getCaption()
);
case CodeShot -> new Slide.CodeShot(
imageProperty.get().image(),
languageProperty.get(),
getCaption()
);
case Screenshot -> new Slide.Screenshot(
imageProperty.get().image(),
backgroundProperty.get(),
getCaption()
);
};
drawing.setup(sizeProperty.get());
var group = drawing.draw(slide);
if (l != null) {
l.onSlideChange(new SlideState(
slideProperty.get(),
imageProperty.get(),
languageProperty.get(),
backgroundProperty.get(),
codeProperty.get(),
sizeProperty.get(),
captionEnableProperty.get(),
getCaption()
));
}
controller.setDrawing(group);
controller.onDrawingChanged(imageProperty.get());
}
private Optional<Slide.Caption> getCaption() {
return captionEnableProperty.get()
? Optional
.of(new Slide.Caption(
captionTitleProperty.get(),
captionSubTitleProperty.get()
))
: Optional.empty();
}
}