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

io.github.palexdev.mfxeffects.animations.TransitionPane Maven / Gradle / Ivy

There is a newer version: 11.26.0
Show newest version
/*
 * Copyright (C) 2023 Parisi Alessandro - [email protected]
 * This file is part of MaterialFX (https://github.com/palexdev/MaterialFX)
 *
 * MaterialFX is free software: you can redistribute it and/or
 * modify it under the terms of the GNU Lesser General Public License
 * as published by the Free Software Foundation; either version 3 of the License,
 * or (at your option) any later version.
 *
 * MaterialFX is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
 * See the GNU Lesser General Public License for more details.
 *
 * You should have received a copy of the GNU Lesser General Public License
 * along with MaterialFX. If not, see .
 */

package io.github.palexdev.mfxeffects.animations;

import io.github.palexdev.mfxeffects.animations.base.ITransitionType;
import io.github.palexdev.mfxeffects.beans.Position;
import io.github.palexdev.mfxeffects.beans.Size;
import io.github.palexdev.mfxeffects.utils.LayoutUtils;
import io.github.palexdev.mfxeffects.utils.TriFunction;
import javafx.animation.Animation;
import javafx.beans.property.ObjectProperty;
import javafx.beans.property.ReadOnlyBooleanProperty;
import javafx.beans.property.ReadOnlyBooleanWrapper;
import javafx.beans.property.SimpleObjectProperty;
import javafx.collections.ObservableList;
import javafx.css.PseudoClass;
import javafx.scene.Node;
import javafx.scene.layout.StackPane;

import java.util.function.Supplier;

public class TransitionPane extends StackPane {
	//================================================================================
	// Properties
	//================================================================================
	private final String STYLE_CLASS = "transition-pane";
	public static final PseudoClass CLOSED_PSEUDO_CLASS = PseudoClass.getPseudoClass("closed");
	public static final PseudoClass OPEN_PSEUDO_CLASS = PseudoClass.getPseudoClass("open");

	private final ObjectProperty closedNode = new SimpleObjectProperty<>() {
		@Override
		public void set(Node newValue) {
			Node oldValue = get();
			if (oldValue != null)
				TransitionPane.super.getChildren().remove(oldValue);

			if (newValue != null)
				TransitionPane.super.getChildren().add(newValue);
			super.set(newValue);
		}
	};
	private final ObjectProperty openNode = new SimpleObjectProperty<>() {
		@Override
		public void set(Node newValue) {
			Node oldValue = get();
			if (oldValue != null)
				TransitionPane.super.getChildren().remove(oldValue);

			if (newValue != null) {
				TransitionPane.super.getChildren().add(newValue);
				newValue.setManaged(false);
				newValue.setVisible(false);
				newValue.setOpacity(0.0);
			}
			super.set(newValue);
		}
	};
	private final ReadOnlyBooleanWrapper open = new ReadOnlyBooleanWrapper(false);

	private final ObjectProperty> targetSize = new SimpleObjectProperty<>() {
		@Override
		protected void invalidated() {
			cachedClosedSize = null;
			cachedOpenSize = null;
		}
	};
	private final ObjectProperty> targetOffset = new SimpleObjectProperty<>(Position::origin);
	private Size cachedClosedSize;
	private Size cachedOpenSize;

	private final ObjectProperty> closeAnimationFactory = new SimpleObjectProperty<>();
	private final ObjectProperty> openAnimationFactory = new SimpleObjectProperty<>();
	private Animation cachedCloseAnimation;
	private Animation cachedOpenAnimation;

	//================================================================================
	// Constructors
	//================================================================================
	public TransitionPane() {
		this(null, null);
	}

	public TransitionPane(Node closedNode, Node openNode) {
		setClosedNode(closedNode);
		setOpenNode(openNode);
		initialize();
	}

	//================================================================================
	// Methods
	//================================================================================
	private void initialize() {
		getStyleClass().add(STYLE_CLASS);
		pseudoClassStateChanged(CLOSED_PSEUDO_CLASS, true);
		setTargetSize(() -> {
			Node node = getOpenNode();
			if (node == null) return Size.invalid();
			return Size.of(
				LayoutUtils.boundWidth(node),
				LayoutUtils.boundHeight(node)
			);
		});

		setMinSize(USE_PREF_SIZE, USE_PREF_SIZE);
		setMaxSize(USE_PREF_SIZE, USE_PREF_SIZE);
	}

	public void open() {
		if (isOpen()) return;
		Node openNode = getOpenNode();
		Size size = getOpenSize();
		if (openNode == null || Size.invalid().equals(size)) return;

		setOpen(true);
		if (cachedOpenAnimation == null)
			cachedOpenAnimation = getOpenAnimationFactory().apply(this, openNode, getClosedNode());
		if (cachedCloseAnimation != null) cachedCloseAnimation.stop();
		cachedOpenAnimation.playFromStart();
	}

	public void close() {
		if (!isOpen()) return;
		Node closedNode = getClosedNode();
		if (closedNode == null) return;
		getClosedSize();

		setOpen(false);
		if (cachedCloseAnimation == null)
			cachedCloseAnimation = getCloseAnimationFactory().apply(this, getOpenNode(), closedNode);
		if (cachedOpenAnimation != null) cachedOpenAnimation.stop();
		cachedCloseAnimation.playFromStart();
	}

	public void setAnimationType(ITransitionType type) {
		setOpenAnimationFactory(type::open);
		setCloseAnimationFactory(type::close);
	}

	public Size getOpenSize() {
		if (cachedOpenSize == null || Size.invalid().equals(cachedOpenSize)) {
			cachedOpenSize = getTargetSize().get();
		}
		return cachedOpenSize;
	}

	public Size getClosedSize() {
		if (cachedClosedSize == null || Size.invalid().equals(cachedClosedSize)) {
			Node closedNode = getClosedNode();
			cachedClosedSize = (closedNode != null) ?
				Size.of(
					LayoutUtils.boundWidth(closedNode),
					LayoutUtils.boundHeight(closedNode)
				) :
				Size.invalid();
		}
		return cachedClosedSize;
	}

	//================================================================================
	// Overridden Methods
	//================================================================================
	@Override
	public ObservableList getChildren() {
		return getChildrenUnmodifiable();
	}

	@Override
	protected void layoutChildren() {
		super.layoutChildren();

		Node node = getOpenNode();
		if (node == null) return;
		node.resizeRelocate(0, 0, getWidth(), getHeight());
	}

	//================================================================================
	// Getters/Setters
	//================================================================================

	public Node getClosedNode() {
		return closedNode.get();
	}

	public ObjectProperty closedNodeProperty() {
		return closedNode;
	}

	public void setClosedNode(Node closedNode) {
		this.closedNode.set(closedNode);
	}

	public Node getOpenNode() {
		return openNode.get();
	}

	public ObjectProperty openNodeProperty() {
		return openNode;
	}

	public void setOpenNode(Node openNode) {
		this.openNode.set(openNode);
	}

	public boolean isOpen() {
		return open.get();
	}

	public ReadOnlyBooleanProperty openProperty() {
		return open.getReadOnlyProperty();
	}

	protected void setOpen(boolean open) {
		this.open.set(open);
		pseudoClassStateChanged(CLOSED_PSEUDO_CLASS, !open);
		pseudoClassStateChanged(OPEN_PSEUDO_CLASS, open);
	}

	public Supplier getTargetSize() {
		return targetSize.get();
	}

	public ObjectProperty> targetSizeProperty() {
		return targetSize;
	}

	public void setTargetSize(Supplier targetSize) {
		this.targetSize.set(targetSize);
	}

	public Supplier getTargetOffset() {
		return targetOffset.get();
	}

	public ObjectProperty> targetOffsetProperty() {
		return targetOffset;
	}

	public void setTargetOffset(Supplier targetOffset) {
		this.targetOffset.set(targetOffset);
	}

	public TriFunction getCloseAnimationFactory() {
		return closeAnimationFactory.get();
	}

	public ObjectProperty> closeAnimationFactoryProperty() {
		return closeAnimationFactory;
	}

	public void setCloseAnimationFactory(TriFunction closeAnimationFactory) {
		this.closeAnimationFactory.set(closeAnimationFactory);
	}

	public TriFunction getOpenAnimationFactory() {
		return openAnimationFactory.get();
	}

	public ObjectProperty> openAnimationFactoryProperty() {
		return openAnimationFactory;
	}

	public void setOpenAnimationFactory(TriFunction openAnimationFactory) {
		this.openAnimationFactory.set(openAnimationFactory);
	}
}




© 2015 - 2024 Weber Informatics LLC | Privacy Policy