/* * 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 com.google.common.collect.Lists; import org.terasology.math.Border; import org.terasology.math.geom.Rect2i; import org.terasology.math.geom.Vector2i; import org.terasology.rendering.assets.font.Font; 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.SubRegion; 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.itemRendering.ItemRenderer; import org.terasology.rendering.nui.itemRendering.ToStringTextRenderer; import java.util.ArrayList; import java.util.List; /** * A dropdown widget. * @param <T> the list element type */ public class UIDropdown<T> extends CoreWidget { private static final String LIST = "list"; private static final String LIST_ITEM = "list-item"; private Binding<List<T>> options = new DefaultBinding<>(new ArrayList<>()); private Binding<T> selection = new DefaultBinding<>(); private List<InteractionListener> optionListeners = Lists.newArrayList(); private ItemRenderer<T> optionRenderer = new ToStringTextRenderer<>(); private boolean opened; private InteractionListener mainListener = new BaseInteractionListener() { @Override public boolean onMouseClick(NUIMouseClickEvent event) { opened = !opened; optionListeners.clear(); if (opened) { for (int i = 0; i < getOptions().size(); ++i) { optionListeners.add(new ItemListener(i)); } } return true; } }; public UIDropdown() { } public UIDropdown(String id) { super(id); } @Override public boolean isSkinAppliedByCanvas() { return false; } @Override public void onDraw(Canvas canvas) { canvas.drawBackground(); try (SubRegion ignored = canvas.subRegion(canvas.getCurrentStyle().getMargin().shrink(canvas.getRegion()), false)) { if (selection.get() != null) { optionRenderer.draw(selection.get(), canvas); } } if (!isEnabled()) { // do not open and do not add an interaction region } else if (opened) { canvas.setPart(LIST); canvas.setDrawOnTop(true); Font font = canvas.getCurrentStyle().getFont(); Border itemMargin = canvas.getCurrentStyle().getMargin(); int height = (font.getLineHeight() + itemMargin.getTotalHeight()) * options.get().size() + canvas.getCurrentStyle().getBackgroundBorder().getTotalHeight(); canvas.addInteractionRegion(mainListener, Rect2i.createFromMinAndSize(0, 0, canvas.size().x, canvas.size().y + height)); Rect2i location = Rect2i.createFromMinAndSize(0, canvas.size().y, canvas.size().x, height); canvas.drawBackground(location); int itemHeight = itemMargin.getTotalHeight() + font.getLineHeight(); canvas.setPart(LIST_ITEM); for (int i = 0; i < optionListeners.size(); ++i) { if (optionListeners.get(i).isMouseOver()) { canvas.setMode(HOVER_MODE); } else { canvas.setMode(DEFAULT_MODE); } Rect2i itemRegion = Rect2i.createFromMinAndSize(0, canvas.size().y + itemHeight * i, canvas.size().x, itemHeight); canvas.drawBackground(itemRegion); optionRenderer.draw(options.get().get(i), canvas, itemMargin.shrink(itemRegion)); canvas.addInteractionRegion(optionListeners.get(i), itemRegion); } } else { canvas.addInteractionRegion(mainListener); } } @Override public Vector2i getPreferredContentSize(Canvas canvas, Vector2i areaHint) { if (selection.get() != null) { return canvas.getCurrentStyle().getMargin().grow(optionRenderer.getPreferredSize(selection.get(), canvas)); } return canvas.getCurrentStyle().getMargin().grow(new Vector2i(1, canvas.getCurrentStyle().getFont().getLineHeight())); } @Override public String getMode() { if (!isEnabled()) { return DISABLED_MODE; } else if (opened) { return ACTIVE_MODE; } return DEFAULT_MODE; } @Override public void onLoseFocus() { super.onLoseFocus(); opened = false; } public void bindOptions(Binding<List<T>> binding) { options = binding; } /** * Get all the options from the dropdown. * * @return A List containing all the options. */ public List<T> getOptions() { return options.get(); } /** * Set a new set of options for the dropdown. * * @param values A List containing the new options. */ public void setOptions(List<T> values) { this.options.set(values); } public void bindSelection(Binding<T> binding) { this.selection = binding; } /** * Get the currently selected item. * * @return The currently selected item. */ public T getSelection() { return selection.get(); } /** * Set the item from that should be selected. * Note that this item does not actually have to be contained in the List of options. * * @param value The item to set as selected. */ public void setSelection(T value) { selection.set(value); } /** * Set the renderer to use for the options. * This is used to display the options on the dropdown list and in the selection box. * * @param itemRenderer The new item renderer. */ public void setOptionRenderer(ItemRenderer<T> itemRenderer) { optionRenderer = itemRenderer; } private class ItemListener extends BaseInteractionListener { private int index; ItemListener(int index) { this.index = index; } @Override public boolean onMouseClick(NUIMouseClickEvent event) { setSelection(getOptions().get(index)); opened = false; return true; } } }