/*
* Copyright 2016 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.widgets;
import org.terasology.math.geom.Rect2i;
import org.terasology.math.geom.Vector2i;
import org.terasology.rendering.assets.font.Font;
import org.terasology.rendering.nui.Canvas;
import org.terasology.rendering.nui.CoreWidget;
import org.terasology.rendering.nui.LayoutConfig;
import org.terasology.rendering.nui.TextLineBuilder;
import org.terasology.rendering.nui.databinding.Binding;
import org.terasology.rendering.nui.databinding.DefaultBinding;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.Map.Entry;
/**
* A widget that scrolls through long strings of text
*/
public class UIScrollingText extends CoreWidget {
/**
* The text to be shown by this widget
*/
@LayoutConfig
private Binding<String> text = new DefaultBinding<>("");
/**
* Specifies the change in the Y values of the text every frame
*/
@LayoutConfig
private int step = 1;
/**
* Text offset from the top of the canvas, in pixels
*/
@LayoutConfig
private int offsetTop;
/**
* Text offset from the bottom of the canvas, in pixels
*/
@LayoutConfig
private int offsetBottom;
/**
* Spacing between the lines of text, in pixels
*/
@LayoutConfig
private int lineSpacing = 3;
/**
* Maps text to their Y coordinates
*/
private Map<String, Integer> textY = new HashMap<String, Integer>();
/**
* Specifies whether scrolling will restart from the beginning when all text has been scrolled through
*/
private boolean autoReset;
private boolean isScrolling = true;
/**
* Creates an empty {@code UIScrollingText}.
*/
public UIScrollingText() {
}
/**
* Creates an empty {@code UIScrollingText} with the given id.
*
* @param text The text to be shown in this {@code UIScrollingText}
*/
public UIScrollingText(String text) {
this.text.set(text);
}
/**
* Creates a {@code UIScrollingText} containing the text in the given {@link Binding}.
*
* @param text the {@code Binding} containing the text to be shown in this {@code UIScrollingText}
*/
public UIScrollingText(Binding<String> text) {
this.text = text;
}
/**
* Creates a {@code UIScrollingText} with the given id, containing the given text.
*
* @param id The id assigned to this {@code UIScrollingText}
* @param text The text to be shown in this {@code UIScrollingText}
*/
public UIScrollingText(String id, String text) {
super(id);
this.text.set(text);
}
/**
* Creates a {@code UIScrollingText} with the given id, containing the text in the given {@code Binding}.
*
* @param id The id assigned to this {@code UIScrollingText}
* @param text The text to be shown in this {@code UIScrollingText}
*/
public UIScrollingText(String id, Binding<String> text) {
super(id);
this.text = text;
}
/**
* Retrieves the text shown in this {@code UIScrollingText}.
* }
* @return The text shown in this {@code UIScrollingText}
*/
public String getText() {
if (text.get() == null) {
return "";
}
return text.get();
}
/**
* Sets the text shown in this {@code UIScrollingText}.
*
* @param text The text to be shown in the {@code UIScrollingText}
*/
public void setText(String text) {
this.text.set(text);
}
/**
* Binds the text to be shown in this {@code UIScrollingText}.
*
* @param binding The {@code Binding} containing the text to be shown in this {@code UIScrollingText}
*/
public void bindText(Binding<String> binding) {
this.text = binding;
}
/**
* Starts the scrolling of the text shown in this {@code UIScrollingText}.
*/
public void startScrolling() {
isScrolling = true;
}
/**
* Stops the scrolling of the text shown in this {@code UIScrollingText}.
*/
public void stopScrolling() {
isScrolling = false;
}
/**
* Resets the {@code UIScrollingText} to the beginning of the text.
*/
public void resetScrolling() {
textY.clear();
}
/**
* Sets the scrolling speed of the text shown in the {@code UIScrollingText}.
*
* @param speed The increase in the Y values of the text shown each frame, in pixels
*/
public void setScrollingSpeed(int speed) {
this.step = speed;
}
/**
* Specifies whether scrolling will restart from the beginning when all text has been scrolled through.
*
* @param reset Whether the {@code UIScrollingText} will reset automatically
*/
public void setAutoReset(boolean reset) {
this.autoReset = reset;
}
/**
* Handles how this {@code UIScrollingText} is drawn.
* This is called every frame.
*
* @param canvas The {@link Canvas} on which this {@code UIScrollingText} is drawn
*/
@Override
public void onDraw(Canvas canvas) {
if (isScrolling) {
updateYValues(canvas);
}
Font font = canvas.getCurrentStyle().getFont();
int w = canvas.size().x / 2;
boolean finished = true;
for (Entry<String, Integer> entry : textY.entrySet()) {
int y = entry.getValue();
//ignores offsets
if (y >= -offsetTop && y <= canvas.size().y - offsetBottom + font.getHeight(entry.getKey())) {
String line = entry.getKey();
Rect2i coords = Rect2i.createFromMinAndSize(w - font.getWidth(line) / 2, y, font.getWidth(line), font.getHeight(line));
canvas.drawText(entry.getKey(), coords);
}
if (y >= -offsetTop) {
finished = false;
}
}
if (finished && autoReset) {
resetScrolling();
}
}
/**
* Retrieves the preferred content size of this {@code UIScrollingText}.
* This is the minimum size this layout will take, given no space restrictions.
*
* @param canvas The {@code Canvas} on which this {@code UIScrollingText} is drawn
* @param areaHint A {@link Vector2i} representing the available space for this {@code UIScrollingText}
* @return A {@code Vector2i} representing the preferred content size of this {@code UIScrollingText}
*/
@Override
public Vector2i getPreferredContentSize(Canvas canvas, Vector2i areaHint) {
Font font = canvas.getCurrentStyle().getFont();
List<String> lines = TextLineBuilder.getLines(font, getText(), areaHint.x);
return font.getSize(lines);
}
/**
* Updates the Y values of the text shown in this {@code UIScrollingText} each frame
* or initialises them if they are empty.
*
* @param canvas The {@code Canvas} on which this {@code UIScrollingText} is drawn
*/
private void updateYValues(Canvas canvas) {
if (!textY.isEmpty()) {
for (Entry<String, Integer> entry : textY.entrySet()) {
textY.put(entry.getKey(), entry.getValue() - step);
}
} else {
String[] parsed = getText().split("\\r?\\n", -1);
Font font = canvas.getCurrentStyle().getFont();
int y = canvas.size().y + lineSpacing;
for (String line : parsed) {
textY.put(line, y);
y += font.getHeight(line) + lineSpacing;
}
}
}
}