/* * Copyright 2014 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.input.MouseInput; import org.terasology.math.TeraMath; import org.terasology.math.geom.Rect2i; import org.terasology.math.geom.Vector2i; import org.terasology.rendering.nui.BaseInteractionListener; import org.terasology.rendering.nui.Canvas; import org.terasology.rendering.nui.CoreWidget; import org.terasology.rendering.nui.InteractionListener; import org.terasology.rendering.nui.LayoutConfig; import org.terasology.rendering.nui.databinding.Binding; import org.terasology.rendering.nui.databinding.DefaultBinding; import org.terasology.rendering.nui.events.NUIMouseClickEvent; import org.terasology.rendering.nui.events.NUIMouseDragEvent; import org.terasology.rendering.nui.events.NUIMouseReleaseEvent; /** * A simple scrollbar */ public class UIScrollbar extends CoreWidget { @LayoutConfig private Binding<Integer> minimum = new DefaultBinding<>(0); @LayoutConfig private Binding<Integer> range = new DefaultBinding<>(100); @LayoutConfig private Binding<Integer> value = new DefaultBinding<>(0); @LayoutConfig private boolean vertical; private int sliderSize; private int handleSize; private boolean dragging; private int mouseOffset; private InteractionListener handleListener = new BaseInteractionListener() { @Override public boolean onMouseClick(NUIMouseClickEvent event) { if (event.getMouseButton() == MouseInput.MOUSE_LEFT) { dragging = true; Vector2i pos = event.getRelativeMousePosition(); if (vertical) { mouseOffset = pos.y - pixelOffsetFor(getValue()); } else { mouseOffset = pos.x - pixelOffsetFor(getValue()); } return true; } return false; } @Override public void onMouseRelease(NUIMouseReleaseEvent event) { dragging = false; } @Override public void onMouseDrag(NUIMouseDragEvent event) { Vector2i pos = event.getRelativeMousePosition(); if (vertical) { updatePosition(pos.y - mouseOffset); } else { updatePosition(pos.x - mouseOffset); } } }; private InteractionListener sliderListener = new BaseInteractionListener() { @Override public boolean onMouseClick(NUIMouseClickEvent event) { if (event.getMouseButton() == MouseInput.MOUSE_LEFT) { Vector2i pos = event.getRelativeMousePosition(); mouseOffset = (sliderSize > handleSize) ? (handleSize / 2) : 0; int pixelPosition = vertical ? pos.y - mouseOffset : pos.x - mouseOffset; updatePosition(pixelPosition); if (sliderSize > 0) { int clamped = TeraMath.clamp(pixelPosition, 0, sliderSize); setValue(clamped * getRange() / sliderSize); } else { setValue(0); } dragging = true; return true; } return false; } @Override public void onMouseDrag(NUIMouseDragEvent event) { Vector2i pos = event.getRelativeMousePosition(); if (vertical) { updatePosition(pos.y - mouseOffset); } else { updatePosition(pos.x - mouseOffset); } } @Override public void onMouseRelease(NUIMouseReleaseEvent event) { dragging = false; } }; public UIScrollbar() { this(true); } public UIScrollbar(boolean vertical) { this.vertical = vertical; } @Override public void onDraw(Canvas canvas) { if (vertical) { canvas.setPart("sliderVertical"); } else { canvas.setPart("sliderHorizontal"); } canvas.drawBackground(); canvas.addInteractionRegion(sliderListener); canvas.setPart("handle"); if (vertical) { sliderSize = canvas.size().y - canvas.getCurrentStyle().getFixedHeight(); } else { sliderSize = canvas.size().x - canvas.getCurrentStyle().getFixedWidth(); } if (sliderSize > handleSize) { int drawLocation = pixelOffsetFor(getValue()); Rect2i handleRegion; if (vertical) { handleSize = canvas.getCurrentStyle().getFixedHeight(); handleRegion = Rect2i.createFromMinAndSize(0, drawLocation, canvas.getCurrentStyle().getFixedWidth(), handleSize); } else { handleSize = canvas.getCurrentStyle().getFixedWidth(); handleRegion = Rect2i.createFromMinAndSize(drawLocation, 0, handleSize, canvas.getCurrentStyle().getFixedHeight()); } canvas.drawBackground(handleRegion); canvas.addInteractionRegion(handleListener, handleRegion); } } @Override public Vector2i getPreferredContentSize(Canvas canvas, Vector2i sizeHint) { canvas.setPart("handle"); int x = canvas.getCurrentStyle().getFixedWidth(); if (x == 0) { x = canvas.getCurrentStyle().getMinWidth(); } int y = canvas.getCurrentStyle().getFixedHeight(); if (y == 0) { y = canvas.getCurrentStyle().getMinHeight(); } return new Vector2i(x, y); } private int pixelOffsetFor(int newValue) { final int r = getRange(); return (r > 0) ? (sliderSize * newValue / r) : 0; } @Override public String getMode() { if (dragging) { return ACTIVE_MODE; } else if (handleListener.isMouseOver()) { return HOVER_MODE; } return DEFAULT_MODE; } public void bindMinimum(Binding<Integer> binding) { minimum = binding; } /** * @return The minimum value scrollable to. */ public int getMinimum() { return minimum.get(); } /** * @param val The new minimum above zero. */ public void setMinimum(int val) { minimum.set(val); } public void bindRange(Binding<Integer> binding) { range = binding; } /** * @return The max value scrollable to. */ public int getRange() { return range.get(); } /** * @param val The new maximum scrollable. */ public void setRange(int val) { range.set(val); } public void bindValue(Binding<Integer> binding) { value = binding; } /** * @return The current scroll value. */ public int getValue() { return TeraMath.clamp(value.get(), getMinimum(), getMinimum() + getRange()); } /** * @param val The new level of scrolling to set. */ public void setValue(int val) { value.set(val); } private void updatePosition(int pixelPos) { int newPosition = TeraMath.clamp(pixelPos, 0, sliderSize); setValue((sliderSize > 0) ? (newPosition * getRange() / sliderSize) : 0); } }