/******************************************************************************* * Copyright (c) 2015 * * Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), * to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, * and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions: * * The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software. * * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER * DEALINGS IN THE SOFTWARE. *******************************************************************************/ package jsettlers.graphics.ui; import go.graphics.GLDrawContext; import java.util.List; import jsettlers.common.images.EImageLinkType; import jsettlers.common.images.OriginalImageLink; import jsettlers.common.position.FloatRectangle; import jsettlers.graphics.action.Action; import jsettlers.graphics.action.ExecutableAction; import jsettlers.graphics.map.draw.ImageProvider; public class UIList<T> implements UIElement { private static final OriginalImageLink SCROLLBAR_TOP = new OriginalImageLink(EImageLinkType.GUI, 2, 2, 0); private static final OriginalImageLink SCROLLBAR_MIDDLE = new OriginalImageLink(EImageLinkType.GUI, 2, 3, 0); private static final OriginalImageLink SCROLLBAR_BOTTOM = new OriginalImageLink(EImageLinkType.GUI, 2, 4, 0); private static final float RIGHTBORDER = .97f; private static final float EDGEPART = .02f; private static final float SLIDER_MIN_HEIGHT = 0.1f; private final float itemheight; private FloatRectangle position; private float listoffset = 0; // relative in UI space private List<? extends T> items; private final Object itemsMutex = new Object(); private T activeItem; private final ListItemGenerator<T> generator; public interface ListItemGenerator<T> { UIListItem getItem(T item); } public UIList(List<? extends T> items, ListItemGenerator<T> generator, float itemheight) { setItems(items); this.generator = generator; this.itemheight = itemheight; } public void setItems(List<? extends T> list) { synchronized (itemsMutex) { this.items = list; if (!items.contains(activeItem)) { if (items.size() > 0) { activeItem = items.get(0); } else { activeItem = null; } } } } @Override public void setPosition(FloatRectangle position) { this.position = position; } @Override public void drawAt(GLDrawContext gl) { synchronized (itemsMutex) { float totallistheigt = itemheight * items.size(); // relative if (listoffset < 0) { listoffset = 0; } else if (listoffset + 1 > totallistheigt) { listoffset = Math.max(0, totallistheigt - 1); } float minY = position.getMinY(); float minX = position.getMinX(); float height = position.getHeight(); float width = position.getWidth(); int startindex = (int) (listoffset / itemheight); int itemsperpage = (int) (1 / itemheight); for (int i = startindex; i < startindex + itemsperpage && i < items.size(); i++) { T item = items.get(i); // TODO: Cache UIListItem listItem = generator.getItem(item); // relative float itemtop = 1 - i * itemheight + listoffset; float itembottom = itemtop - itemheight; listItem.setHighlighted(activeItem == item); listItem.setPosition(new FloatRectangle(minX, minY + itembottom * height, minX + RIGHTBORDER * width, minY + itemtop * height)); listItem.drawAt(gl); } float sliderHeight = Math.min(1, Math.max(SLIDER_MIN_HEIGHT, 1 / totallistheigt)); float slide = (1 - sliderHeight) * listoffset / (totallistheigt - 1); float sliderMax = 1 - slide; float sliderMin = sliderMax - sliderHeight; float edgeHeight = height * EDGEPART; // bottom / top edge at side. float sliderMinY = sliderMin * height + minY; float sliderMaxY = sliderMax * height + minY; // side ImageProvider provider = ImageProvider.getInstance(); gl.color(1, 1, 1, 1); provider.getImage(SCROLLBAR_TOP).drawImageAtRect(gl, minX + width * RIGHTBORDER, sliderMaxY - edgeHeight, minX + width, sliderMaxY); provider.getImage(SCROLLBAR_MIDDLE).drawImageAtRect(gl, minX + width * RIGHTBORDER, sliderMinY + edgeHeight, minX + width, sliderMaxY - edgeHeight); provider.getImage(SCROLLBAR_BOTTOM).drawImageAtRect(gl, minX + width * RIGHTBORDER, sliderMinY, minX + width, sliderMinY + edgeHeight); } } @Override public Action getAction(float relativex, float relativey) { synchronized (itemsMutex) { if (relativex < RIGHTBORDER) { float listy = 1 - relativey + listoffset; int itemIndex = (int) (listy / itemheight); if (itemIndex >= 0 && itemIndex < items.size()) { return new SelectAction(items.get(itemIndex)); } else { return null; } } else { final float halfNumberOfDisplayedItems = (int) (0.5f / itemheight); final int destinationOffset = (int) ((1 - relativey) * items.size() - halfNumberOfDisplayedItems); // subtract this to get the center of the scrollbar where the player klicked. return new ExecutableAction() { @Override public void execute() { scrollBy(destinationOffset); } }; } } } protected void scrollBy(int offset) { listoffset = itemheight * offset; } private class SelectAction extends ExecutableAction { private final T item; public SelectAction(T item) { this.item = item; } @Override public void execute() { // TODO: setActiveUIElement(); setActiveItem(item); } } @Override public String getDescription(float relativex, float relativey) { return null; } public void setActiveItem(T activeItem) { synchronized (itemsMutex) { if (items.contains(activeItem)) { this.activeItem = activeItem; } } } public T getActiveItem() { return activeItem; } @Override public void onAttach() { } @Override public void onDetach() { } }