/******************************************************************************* * Copyright 2014 Rafael Garcia Moreno. * * 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 com.bladecoder.engineeditor.ui.panels; import java.util.Comparator; import com.badlogic.gdx.graphics.Color; import com.badlogic.gdx.graphics.g2d.Batch; import com.badlogic.gdx.graphics.g2d.BitmapFont; import com.badlogic.gdx.graphics.g2d.GlyphLayout; import com.badlogic.gdx.graphics.g2d.TextureRegion; import com.badlogic.gdx.math.Rectangle; import com.badlogic.gdx.scenes.scene2d.InputEvent; import com.badlogic.gdx.scenes.scene2d.InputListener; import com.badlogic.gdx.scenes.scene2d.ui.List; import com.badlogic.gdx.scenes.scene2d.ui.Skin; import com.badlogic.gdx.scenes.scene2d.ui.Widget; import com.badlogic.gdx.scenes.scene2d.utils.ArraySelection; import com.badlogic.gdx.scenes.scene2d.utils.Cullable; import com.badlogic.gdx.scenes.scene2d.utils.Drawable; import com.badlogic.gdx.utils.Array; import com.badlogic.gdx.utils.ObjectSet; /** * List witch items has and icon, a title and a subtitle * * @author rgarcia * * @param <T> */ public class CustomList<T> extends Widget implements Cullable { private CellRenderer<T> cellRenderer; private CustomListStyle style; private final Array<T> items = new Array<T>(); private Rectangle cullingArea; private float prefWidth, prefHeight; final ArraySelection<T> selection; public CustomList(Skin skin, CellRenderer<T> r) { this(skin.get(CustomListStyle.class), r); } public CustomList(Skin skin) { this(skin.get(CustomListStyle.class), new CellRenderer<T>()); } public CustomList(Skin skin, String styleName) { this(skin.get(styleName, CustomListStyle.class), new CellRenderer<T>()); } public CustomList(CustomListStyle style, CellRenderer<T> r) { selection = new ArraySelection<T>(items); selection.setActor(this); selection.setRequired(true); cellRenderer = r; setStyle(style); setSize(getPrefWidth(), getPrefHeight()); addListener(new InputListener() { public boolean touchDown(InputEvent event, float x, float y, int pointer, int button) { if (pointer == 0 && button != 0) return false; if (selection.isDisabled()) return false; CustomList.this.touchDown(y); return true; } }); } public void setCellRenderer(CellRenderer<T> r) { cellRenderer = r; } void touchDown(float y) { if (items.size == 0) return; float height = getHeight(); if (style.background != null) { height -= style.background.getTopHeight() + style.background.getBottomHeight(); y -= style.background.getBottomHeight(); } int index = (int) ((height - y) / cellRenderer.getItemHeight()); index = Math.max(0, index); index = Math.min(items.size - 1, index); selection.choose(items.get(index)); } public void setStyle(CustomListStyle style) { if (style == null) throw new IllegalArgumentException("style cannot be null."); this.style = style; invalidateHierarchy(); } /** * Returns the list's style. Modifying the returned style may not have an * effect until {@link #setStyle(CustomListStyle)} is called. */ public CustomListStyle getStyle() { return style; } public void layout() { final BitmapFont font = style.font; final BitmapFont subfont = style.subtitleFont; final Drawable selectedDrawable = style.selection; cellRenderer.layout(style); GlyphLayout textLayout = new GlyphLayout(); prefWidth = 0; for (int i = 0; i < items.size; i++) { textLayout.setText(font, cellRenderer.getCellTitle(items.get(i))); prefWidth = Math.max(textLayout.width, prefWidth); if (cellRenderer.hasImage()) { TextureRegion r = cellRenderer.getCellImage(items.get(i)); float ih = r.getRegionHeight(); float iw = r.getRegionWidth(); if (ih > getItemHeight() - 10) { ih = getItemHeight() - 10; iw *= ih / r.getRegionHeight(); } prefWidth = Math.max(iw + textLayout.width, prefWidth); } if (cellRenderer.hasSubtitle()) { String subtitle = cellRenderer.getCellSubTitle(items.get(i)); if (subtitle != null) { textLayout.setText(subfont, subtitle); prefWidth = Math.max(textLayout.width, prefWidth); } } } prefWidth += selectedDrawable.getLeftWidth() + selectedDrawable.getRightWidth(); prefHeight = items.size * cellRenderer.getItemHeight(); Drawable background = style.background; if (background != null) { prefWidth += background.getLeftWidth() + background.getRightWidth(); prefHeight += background.getTopHeight() + background.getBottomHeight(); } } @Override public void draw(Batch batch, float parentAlpha) { validate(); Color color = getColor(); batch.setColor(color.r, color.g, color.b, color.a * parentAlpha); float x = getX(), y = getY(), width = getWidth(), height = getHeight(); float itemY = height; Drawable background = style.background; if (background != null) { background.draw(batch, x, y, width, height); float leftWidth = background.getLeftWidth(); x += leftWidth; itemY -= background.getTopHeight(); width -= leftWidth + background.getRightWidth(); } for (int i = 0; i < items.size; i++) { if (cullingArea == null || (itemY - cellRenderer.getItemHeight() <= cullingArea.y + cullingArea.height && itemY >= cullingArea.y)) { T item = items.get(i); boolean selected = selection.contains(item); cellRenderer.draw(batch, parentAlpha, item, selected, x, y + itemY, width, cellRenderer.getItemHeight()); } else if (itemY < cullingArea.y) { break; } itemY -= cellRenderer.getItemHeight(); } } public void sortByTitle() { getItems().sort(new Comparator<T>() { @Override public int compare(T o1, T o2) { String s1 = cellRenderer.getCellTitle(o1); String s2 = cellRenderer.getCellTitle(o2); return s1.compareTo(s2); } }); } public ArraySelection<T> getSelection() { return selection; } /** Returns the first selected item, or null. */ public T getSelected() { return selection.first(); } /** * @return The index of the first selected item. The top item has an index * of 0. Nothing selected has an index of -1. */ public int getSelectedIndex() { ObjectSet<T> selected = selection.items(); return selected.size == 0 ? -1 : items.indexOf(selected.first(), false); } /** Sets the selection to only the selected index. */ public void setSelectedIndex(int index) { if (index < -1 || index >= items.size) throw new IllegalArgumentException("index must be >= -1 and < " + items.size + ": " + index); selection.set(items.get(index)); } /** * Sets the current items, clearing the selection if it is no longer valid. * If a selection is {@link ArraySelection#getRequired()}, the first item is * selected. */ public void setItems(Array<T> newItems) { if (newItems == null) throw new IllegalArgumentException("newItems cannot be null."); items.clear(); items.addAll(newItems); T selected = getSelected(); if (!items.contains(selected, false)) { if (selection.getRequired() && items.size > 0) selection.set(items.first()); else selection.clear(); } invalidateHierarchy(); } public Array<T> getItems() { return items; } public float getItemHeight() { return cellRenderer.getItemHeight(); } public float getPrefWidth() { validate(); return prefWidth; } public float getPrefHeight() { validate(); return prefHeight; } public void setCullingArea(Rectangle cullingArea) { this.cullingArea = cullingArea; } /** * The style for a list, see {@link List}. * * @author mzechner * @author Nathan Sweet */ static public class CustomListStyle { public BitmapFont font; public BitmapFont subtitleFont; public Color fontColorSelected = new Color(1, 1, 1, 1); public Color fontColorUnselected = new Color(1, 1, 1, 1); public Drawable selection; /** Optional. */ public Drawable background; public CustomListStyle() { } public CustomListStyle(BitmapFont font, Color fontColorSelected, Color fontColorUnselected, Drawable selection) { this.font = font; this.fontColorSelected.set(fontColorSelected); this.fontColorUnselected.set(fontColorUnselected); this.selection = selection; } public CustomListStyle(CustomListStyle style) { this.font = style.font; this.fontColorSelected.set(style.fontColorSelected); this.fontColorUnselected.set(style.fontColorUnselected); this.selection = style.selection; } } }