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

org.mini2Dx.ui.render.RenderNode Maven / Gradle / Ivy

There is a newer version: 2.0.0-alpha.32
Show newest version
/*******************************************************************************
 * Copyright 2019 See AUTHORS file
 *
 * 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.mini2Dx.ui.render;

import org.mini2Dx.core.Graphics;
import org.mini2Dx.core.Mdx;
import org.mini2Dx.core.collision.CollisionBox;
import org.mini2Dx.core.exception.MdxException;
import org.mini2Dx.core.geom.Rectangle;
import org.mini2Dx.gdx.math.MathUtils;
import org.mini2Dx.gdx.utils.Array;
import org.mini2Dx.ui.effect.UiEffect;
import org.mini2Dx.ui.element.ParentUiElement;
import org.mini2Dx.ui.element.UiElement;
import org.mini2Dx.ui.element.Visibility;
import org.mini2Dx.ui.layout.LayoutState;
import org.mini2Dx.ui.style.StyleRule;

/**
 * Base class for implementing rendering of {@link UiElement} implementations
 */
public abstract class RenderNode implements HoverableRenderNode {
	private static final String LOGGING_TAG = RenderNode.class.getSimpleName();

	protected final Array effects = new Array(true, 1, UiEffect.class);
	protected final CollisionBox outerArea = new CollisionBox();
	protected final Rectangle innerArea = new Rectangle();
	protected final Rectangle targetOuterArea = new Rectangle();
	protected final ParentRenderNode parent;
	protected final T element;

	protected UiContainerRenderTree rootNode;
	protected S style;
	protected float preferredContentWidth, preferredContentHeight;
	protected float xOffset, yOffset;
	protected int zIndex;
	protected boolean hiddenByLayoutRule = false;
	protected boolean initialLayoutOccurred = false, initialUpdateOccurred = false;
	private float relativeX, relativeY;
	private boolean dirty;
	private boolean includeInRender = false;
	private NodeState state = NodeState.NORMAL;

	public RenderNode(ParentRenderNode parent, T element) {
		this.parent = parent;
		this.element = element;
		this.zIndex = element.getZIndex();

		setDirty();
	}

	public void update(UiContainerRenderTree uiContainer, float delta) {
		if (!initialLayoutOccurred) {
			if (element.isDebugEnabled()) {
				Mdx.log.debug(element.getId(), "UPDATE - initial layout not occurred");
			}
			return;
		}
		if (style == null) {
			throw new MdxException("No style found for element: " + getId());
		}
		if (parent == null) {
			targetOuterArea.set(relativeX, relativeY,
					getPreferredOuterWidth(), getPreferredOuterHeight());
		} else {
			targetOuterArea.set(parent.getInnerX() + relativeX,
					parent.getInnerY() + relativeY, getPreferredOuterWidth(),
					getPreferredOuterHeight());
		}

		boolean visible = isScheduledToRender();
		if (effects.size == 0) {
			outerArea.forceTo(targetOuterArea.getX(), targetOuterArea.getY(),
					targetOuterArea.getWidth(), targetOuterArea.getHeight());
		} else {
			for (int i = 0; i < effects.size; i++) {
				UiEffect effect = effects.get(i);
				if (effect.isFinished()) {
					effect.postEnd(element);
					effects.removeIndex(i);
					element.notifyEffectListenersOnFinished(effect);
					i--;
					continue;
				}

				visible &= effect.update(uiContainer, outerArea, targetOuterArea, delta);
			}
		}
		includeInRender = visible;

		if (element.isDebugEnabled()) {
			Mdx.log.debug(element.getId(), "UPDATE - outerArea: " + outerArea + ", targetArea: " + targetOuterArea
					+ ", visibility: " + element.getVisibility());
		}

		innerArea.set(getInnerX(), getInnerY(), getInnerWidth(), getInnerHeight());
		initialUpdateOccurred = true;

		element.syncWithUpdate(rootNode);
	}

	public void render(Graphics g) {
		if (!isIncludedInRender()) {
			if (element.isDebugEnabled()) {
				Mdx.log.debug(element.getId(), "RENDER - Element not included in render");
			}
			return;
		}
		if (element.isDebugEnabled()) {
			Mdx.log.debug(element.getId(), "RENDER - x,y: " + getOuterRenderX() + "," + getOuterRenderY() + " width: "
					+ getOuterRenderWidth() + ", height: " + getOuterRenderHeight());
		}

		for (int i = 0; i < effects.size; i++) {
			effects.get(i).preRender(g);
		}
		renderElement(g);
		for (int i = 0; i < effects.size; i++) {
			effects.get(i).postRender(g);
		}

		element.syncWithRender(rootNode);
	}

	public void beginFakeHover() {
		mouseMoved(MathUtils.round(innerArea.getCenterX()), MathUtils.round(innerArea.getCenterY()));
		if(rootNode == null) {
			return;
		}
		if(this instanceof ActionableRenderNode) {
			rootNode.getElement().setActiveAction((ActionableRenderNode) this);
		}
	}

	public void endFakeHover() {
		mouseMoved(MathUtils.round(innerArea.getX()) - 1, MathUtils.round(innerArea.getY() - 1));
		if(rootNode == null) {
			return;
		}
		if(this instanceof ActionableRenderNode) {
			rootNode.getElement().clearActiveAction();
		}
	}

	public boolean mouseMoved(int screenX, int screenY) {
		if (innerArea.contains(screenX, screenY)) {
			beginHover();
			return true;
		} else if (state != NodeState.NORMAL) {
			endHover();
		}
		return false;
	}
	
	public boolean mouseScrolled(int screenX, int screenY, float amount) {
		return false;
	}

	public ActionableRenderNode mouseDown(int screenX, int screenY, int pointer, int button) {
		return null;
	}

	public void mouseUp(int screenX, int screenY, int pointer, int button) {
	}

	public boolean contains(float screenX, float screenY) {
		return innerArea.contains(screenX, screenY);
	}

	public void beginHover() {
		state = NodeState.HOVER;
		element.notifyHoverListenersOnBeginHover();
	}

	public void endHover() {
		state = NodeState.NORMAL;
		element.notifyHoverListenersOnEndHover();
	}

	protected abstract void renderElement(Graphics g);

	protected abstract S determineStyleRule(LayoutState layoutState);

	protected abstract float determinePreferredContentWidth(LayoutState layoutState);

	protected abstract float determinePreferredContentHeight(LayoutState layoutState);

	protected abstract float determineXOffset(LayoutState layoutState);

	protected abstract float determineYOffset(LayoutState layoutState);

	public void layout(LayoutState layoutState) {
		if (!isDirty() && !layoutState.isScreenSizeChanged()) {
			return;
		}
		if (element.isDebugEnabled()) {
			Mdx.log.debug(LOGGING_TAG, "Layout triggered");
		}
		rootNode = layoutState.getUiContainerRenderTree();
		style = determineStyleRule(layoutState);

		if (this.zIndex != element.getZIndex()) {
			parent.removeChild(this);
			zIndex = element.getZIndex();
			parent.addChild(this);
		}

		switch (element.getVisibility()) {
		case HIDDEN:
			preferredContentWidth = 0f;
			preferredContentHeight = 0f;
			xOffset = 0f;
			yOffset = 0f;
			element.syncWithLayout(rootNode);
			return;
		default:
			preferredContentWidth = determinePreferredContentWidth(layoutState);
			preferredContentHeight = determinePreferredContentHeight(layoutState);
			xOffset = determineXOffset(layoutState);
			yOffset = determineYOffset(layoutState);
			break;
		}

		dirty = false;
		initialLayoutOccurred = true;
		element.syncWithLayout(rootNode);
	}

	public boolean isIncludedInLayout() {
		if (hiddenByLayoutRule) {
			return false;
		}
		if (element.getVisibility() == Visibility.HIDDEN) {
			return false;
		}
		return getPreferredInnerWidth() > 0f || getPreferredInnerHeight() > 0f;
	}

	private boolean isScheduledToRender() {
		if (!initialLayoutOccurred) {
			return false;
		}
		if (element.isDebugEnabled()) {
			Mdx.log.debug(element.getId(), "SCHEDULED_TO_RENDER - hiddenByLayoutRule:" +
					hiddenByLayoutRule + ", style:" + style + ", visible:" + element.getVisibility() +
					", preferredWidth:" + getPreferredInnerWidth() + ", preferredHeight:" + getPreferredInnerHeight());
		}
		if (hiddenByLayoutRule) {
			return false;
		}
		if (style == null) {
			return false;
		}
		if (element.getVisibility() != Visibility.VISIBLE) {
			return false;
		}
		return getPreferredInnerWidth() > 0f || getPreferredInnerHeight() > 0f;
	}

	public boolean isIncludedInRender() {
		return includeInRender;
	}

	public boolean isDirty() {
		return dirty;
	}

	protected void clearDirty() {
		this.dirty = false;
	}

	public boolean setDirty() {
		final boolean result = this.dirty != true;
		if(this.dirty) {
			return result;
		}
		this.dirty = true;

		if(parent != null) {
			parent.setChildDirty();
		}
		return result;
	}

	public void applyEffect(UiEffect effect) {
		effect.preBegin(element);
		effects.add(effect);
	}

	public float getRelativeX() {
		return relativeX;
	}

	public void setRelativeX(float relativeX) {
		this.relativeX = relativeX;
	}

	public float getRelativeY() {
		return relativeY;
	}

	public void setRelativeY(float relativeY) {
		this.relativeY = relativeY;
	}

	public float getPreferredContentWidth() {
		return preferredContentWidth;
	}

	public float getPreferredInnerWidth() {
		if(style == null) {
			return preferredContentWidth;
		}
		return preferredContentWidth + style.getPaddingLeft() + style.getPaddingRight();
	}

	public float getPreferredOuterWidth() {
		if(style == null) {
			return preferredContentWidth;
		}
		return preferredContentWidth + style.getPaddingLeft() + style.getPaddingRight() + style.getMarginLeft()
				+ style.getMarginRight();
	}

	public float getPreferredContentHeight() {
		return preferredContentHeight;
	}

	public float getPreferredInnerHeight() {
		if(style == null) {
			return preferredContentWidth;
		}
		return preferredContentHeight + style.getPaddingTop() + style.getPaddingBottom();
	}

	public float getPreferredOuterHeight() {
		if(style == null) {
			return preferredContentWidth;
		}
		return preferredContentHeight + style.getPaddingTop() + style.getPaddingBottom() + style.getMarginTop()
				+ style.getMarginBottom();
	}

	public float getXOffset() {
		return xOffset;
	}

	public float getYOffset() {
		return yOffset;
	}

	public float getOuterX() {
		return outerArea.getX();
	}

	public float getOuterY() {
		return outerArea.getY();
	}

	public float getOuterWidth() {
		return outerArea.getWidth();
	}

	public float getOuterHeight() {
		return outerArea.getHeight();
	}

	public int getOuterRenderX() {
		return outerArea.getRenderX();
	}

	public int getOuterRenderY() {
		return outerArea.getRenderY();
	}

	public int getOuterRenderWidth() {
		return outerArea.getRenderWidth();
	}

	public int getOuterRenderHeight() {
		return outerArea.getRenderHeight();
	}
	
	public float getInnerX() {
		if(style == null) {
			return outerArea.getX();
		}
		return outerArea.getX() + style.getMarginLeft();
	}

	public float getInnerY() {
		if(style == null) {
			return outerArea.getY();
		}
		return outerArea.getY() + style.getMarginTop();
	}

	public float getInnerWidth() {
		if(style == null) {
			return outerArea.getWidth();
		}
		return outerArea.getWidth() - style.getMarginLeft() - style.getMarginRight();
	}

	public float getInnerHeight() {
		if(style == null) {
			return outerArea.getHeight();
		}
		return outerArea.getHeight() - style.getMarginTop() - style.getMarginBottom();
	}

	public int getInnerRenderX() {
		if(style == null) {
			return outerArea.getRenderX();
		}
		return outerArea.getRenderX() + style.getMarginLeft();
	}

	public int getInnerRenderY() {
		if(style == null) {
			return outerArea.getRenderY();
		}
		return outerArea.getRenderY() + style.getMarginTop();
	}

	public int getInnerRenderWidth() {
		if(style == null) {
			return outerArea.getRenderWidth();
		}
		return outerArea.getRenderWidth() - style.getMarginLeft() - style.getMarginRight();
	}

	public int getInnerRenderHeight() {
		if(style == null) {
			return outerArea.getRenderHeight();
		}
		return outerArea.getRenderHeight() - style.getMarginTop() - style.getMarginBottom();
	}
	
	public int getContentRenderX() {
		if(style == null) {
			return getInnerRenderX();
		}
		return getInnerRenderX()+ style.getPaddingLeft();
	}
	
	public int getContentRenderY() {
		if(style == null) {
			return getInnerRenderY();
		}
		return getInnerRenderY() + style.getPaddingTop();
	}
	
	public int getContentRenderWidth() {
		if(style == null) {
			return getInnerRenderWidth();
		}
		return getInnerRenderWidth() - style.getPaddingLeft() - style.getPaddingRight();
	}
	
	public int getContentRenderHeight() {
		if(style == null) {
			return getInnerRenderHeight();
		}
		return getInnerRenderHeight() - style.getPaddingTop() - style.getPaddingBottom();
	}

	public NodeState getState() {
		return state;
	}

	public void setState(NodeState state) {
		NodeState previousState = this.state;
		this.state = state;
		if (previousState != state) {
			element.notifyNodeStateListeners(state);
			if (state == NodeState.HOVER) {
				element.notifyHoverListenersOnBeginHover();
			} else if (previousState == NodeState.HOVER) {
				element.notifyHoverListenersOnEndHover();
			}
		}
	}

	public S getStyle() {
		return style;
	}

	public RenderNode getElementById(String id) {
		if (element.getId().equals(id)) {
			return this;
		}
		return null;
	}

	public String getId() {
		return element.getId();
	}

	public int getZIndex() {
		return zIndex;
	}

	public T getElement() {
		return element;
	}

	public ParentRenderNode getParent() {
		return parent;
	}

	public boolean isInitialLayoutOccurred() {
		return initialLayoutOccurred;
	}

	public boolean isInitialUpdateOccurred() {
		return initialUpdateOccurred;
	}

	public boolean isChildOfUiContainer() {
		if(parent == null) {
			return false;
		}
		if(rootNode == null) {
			return false;
		}
		return parent.getId().equals(rootNode.getId());
	}

	@Override
	public String toString() {
		return "RenderNode [outerArea=" + outerArea + ", targetOuterArea=" + targetOuterArea + ", parent=" + parent.getId()
				+ ", style=" + style + ", preferredWidth=" + preferredContentWidth + ", preferredHeight="
				+ preferredContentHeight + ", xOffset=" + xOffset + ", yOffset=" + yOffset + "]";
	}
}




© 2015 - 2025 Weber Informatics LLC | Privacy Policy