/******************************************************************************* * Copyright 2011 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 com.badlogic.gdx.scenes.scene2d.ui; import com.badlogic.gdx.graphics.Color; import com.badlogic.gdx.graphics.g2d.BitmapFont; import com.badlogic.gdx.graphics.g2d.BitmapFont.TextBounds; import com.badlogic.gdx.graphics.g2d.SpriteBatch; 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.utils.ChangeListener.ChangeEvent; import com.badlogic.gdx.scenes.scene2d.utils.Cullable; import com.badlogic.gdx.scenes.scene2d.utils.Drawable; import com.badlogic.gdx.utils.GdxRuntimeException; import com.badlogic.gdx.utils.Pools; /** * A list (aka list box) displays textual items and highlights the currently selected item. * <p> * {@link ChangeEvent} is fired when the list selection changes. * <p> * The preferred size of the list is determined by the text bounds of the items and the size of the * {@link ListStyle#selection}. * * @author mzechner */ public class List extends Widget implements Cullable { private ListStyle style; private String[] items; private int selectedIndex; private Rectangle cullingArea; private float prefWidth, prefHeight; private float itemHeight; private float textOffsetX, textOffsetY; public List(Object[] items, Skin skin) { this(items, skin.get(ListStyle.class)); } public List(Object[] items, Skin skin, String styleName) { this(items, skin.get(styleName, ListStyle.class)); } public List(Object[] items, ListStyle style) { setStyle(style); setItems(items); setWidth(getPrefWidth()); setHeight(getPrefHeight()); addListener(new InputListener() { public boolean touchDown(InputEvent event, float x, float y, int pointer, int button) { if (pointer == 0 && button != 0) return false; List.this.touchDown(y); return true; } }); } void touchDown(float y) { int oldIndex = selectedIndex; selectedIndex = (int) ((getHeight() - y) / itemHeight); selectedIndex = Math.max(0, selectedIndex); selectedIndex = Math.min(items.length - 1, selectedIndex); ChangeEvent changeEvent = Pools.obtain(ChangeEvent.class); if (fire(changeEvent)) selectedIndex = oldIndex; Pools.free(changeEvent); } public void setStyle(ListStyle style) { if (style == null) throw new IllegalArgumentException("style cannot be null."); this.style = style; if (items != null) setItems(items); else invalidateHierarchy(); } /** * Returns the list's style. Modifying the returned style may not have an effect until {@link #setStyle(ListStyle)} * is called. */ public ListStyle getStyle() { return style; } @Override public void draw(SpriteBatch batch, float parentAlpha) { BitmapFont font = style.font; Drawable selectedDrawable = style.selection; Color fontColorSelected = style.fontColorSelected; Color fontColorUnselected = style.fontColorUnselected; Color color = getColor(); batch.setColor(color.r, color.g, color.b, color.a * parentAlpha); float x = getX(); float y = getY(); font.setColor(fontColorUnselected.r, fontColorUnselected.g, fontColorUnselected.b, fontColorUnselected.a * parentAlpha); float itemY = getHeight(); for (int i = 0; i < items.length; i++) { if (cullingArea == null || (itemY - itemHeight <= cullingArea.y + cullingArea.height && itemY >= cullingArea.y)) { if (selectedIndex == i) { selectedDrawable .draw(batch, x, y + itemY - itemHeight, Math.max(prefWidth, getWidth()), itemHeight); font.setColor(fontColorSelected.r, fontColorSelected.g, fontColorSelected.b, fontColorSelected.a * parentAlpha); } font.draw(batch, items[i], x + textOffsetX, y + itemY - textOffsetY); if (selectedIndex == i) { font.setColor(fontColorUnselected.r, fontColorUnselected.g, fontColorUnselected.b, fontColorUnselected.a * parentAlpha); } } else if (itemY < cullingArea.y) { break; } itemY -= itemHeight; } } /** @return The index of the currently selected item. The top item has an index of 0. */ public int getSelectedIndex() { return selectedIndex; } public void setSelectedIndex(int index) { if (index < 0 || index >= items.length) throw new GdxRuntimeException("index must be >= 0 and < " + items.length + ": " + index); selectedIndex = index; } /** @return The text of the currently selected item or null if the list is empty. */ public String getSelection() { if (items.length == 0) return null; return items[selectedIndex]; } /** @return The index of the item that was selected, or -1. */ public int setSelection(String item) { selectedIndex = -1; for (int i = 0, n = items.length; i < n; i++) { if (items[i].equals(item)) { selectedIndex = i; break; } } return selectedIndex; } public void setItems(Object[] objects) { if (objects == null) throw new IllegalArgumentException("items cannot be null."); if (!(objects instanceof String[])) { String[] strings = new String[objects.length]; for (int i = 0, n = objects.length; i < n; i++) strings[i] = String.valueOf(objects[i]); items = strings; } else items = (String[]) objects; selectedIndex = 0; final BitmapFont font = style.font; final Drawable selectedDrawable = style.selection; itemHeight = font.getCapHeight() - font.getDescent() * 2; itemHeight += selectedDrawable.getTopHeight() + selectedDrawable.getBottomHeight(); prefWidth += selectedDrawable.getLeftWidth() + selectedDrawable.getRightWidth(); textOffsetX = selectedDrawable.getLeftWidth(); textOffsetY = selectedDrawable.getTopHeight() - font.getDescent(); prefWidth = 0; for (int i = 0; i < items.length; i++) { TextBounds bounds = font.getBounds(items[i]); prefWidth = Math.max(bounds.width, prefWidth); } prefHeight = items.length * itemHeight; invalidateHierarchy(); } public String[] getItems() { return items; } public float getPrefWidth() { return prefWidth; } public float getPrefHeight() { 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 ListStyle { public BitmapFont font; public Color fontColorSelected = new Color(1, 1, 1, 1); public Color fontColorUnselected = new Color(1, 1, 1, 1); public Drawable selection; public ListStyle() { } public ListStyle(BitmapFont font, Color fontColorSelected, Color fontColorUnselected, Drawable selection) { this.font = font; this.fontColorSelected.set(fontColorSelected); this.fontColorUnselected.set(fontColorUnselected); this.selection = selection; } public ListStyle(ListStyle style) { this.font = style.font; this.fontColorSelected.set(style.fontColorSelected); this.fontColorUnselected.set(style.fontColorUnselected); this.selection = style.selection; } } }