/******************************************************************************* * 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 static com.badlogic.gdx.scenes.scene2d.actions.Actions.fadeIn; import static com.badlogic.gdx.scenes.scene2d.actions.Actions.fadeOut; import static com.badlogic.gdx.scenes.scene2d.actions.Actions.sequence; import com.badlogic.gdx.Input.Keys; import com.badlogic.gdx.graphics.g2d.Batch; import com.badlogic.gdx.math.Interpolation; import com.badlogic.gdx.math.Vector2; import com.badlogic.gdx.scenes.scene2d.Actor; import com.badlogic.gdx.scenes.scene2d.InputEvent; import com.badlogic.gdx.scenes.scene2d.InputListener; import com.badlogic.gdx.scenes.scene2d.Stage; import com.badlogic.gdx.scenes.scene2d.Touchable; import com.badlogic.gdx.scenes.scene2d.actions.Actions; import com.badlogic.gdx.scenes.scene2d.ui.List; import com.badlogic.gdx.scenes.scene2d.ui.List.ListStyle; import com.badlogic.gdx.scenes.scene2d.ui.ScrollPane; import com.badlogic.gdx.scenes.scene2d.ui.SelectBox.SelectBoxStyle; import com.badlogic.gdx.scenes.scene2d.ui.Skin; import com.badlogic.gdx.scenes.scene2d.ui.Table; import com.badlogic.gdx.scenes.scene2d.ui.TextButton; import com.badlogic.gdx.scenes.scene2d.ui.TextField; import com.badlogic.gdx.scenes.scene2d.utils.ChangeListener; import com.badlogic.gdx.scenes.scene2d.utils.ClickListener; import com.badlogic.gdx.scenes.scene2d.utils.Drawable; public class EditableSelectBox<T> extends Table { static final Vector2 temp = new Vector2(); private TextField input; private TextButton showListButton; private SelectList<T> selectList; @SuppressWarnings("unused") private ClickListener clickListener; private boolean disabled; public EditableSelectBox (Skin skin) { super(skin); input = new TextField("", skin); showListButton = new TextButton(">", skin); selectList = new SelectList<>(skin, input); add(input); add(showListButton); addListener(clickListener = new ClickListener() { public boolean touchDown (InputEvent event, float x, float y, int pointer, int button) { if (pointer == 0 && button != 0) return false; if (disabled) return false; if (selectList.hasParent()) hideList(); else showList(); return true; } }); // selectList.getList().addListener(new ChangeListener() { // @Override // public void changed(ChangeEvent event, Actor actor) { // fire(event); // } // }); input.setProgrammaticChangeEvents(true); input.addListener(new ChangeListener() { @Override public void changed(ChangeEvent event, Actor actor) { fire(event); } }); } public String getSelected() { return input.getText(); } public void setSelected(String str) { input.setText(str); } public int getSelectedIndex() { return selectList.list.getSelectedIndex(); } public void setSelectedIndex(int idx) { if(selectList.list.getItems().size == 0) return; input.setText(selectList.list.getItems().get(idx).toString()); } public TextField getInput() { return input; } @SafeVarargs public final void setItems(T... newItems) { if (newItems == null) throw new IllegalArgumentException("newItems cannot be null."); float oldPrefWidth = getPrefWidth(); selectList.list.setItems(newItems); if(newItems.length > 0) selectList.list.setSelectedIndex(0); invalidate(); if (oldPrefWidth != getPrefWidth()) invalidateHierarchy(); } public void showList () { if (selectList.getList().getItems().size == 0) return; selectList.show(getStage()); } public void hideList () { selectList.hide(); } protected void onShow (Actor selectBoxList, boolean below) { selectBoxList.getColor().a = 0; selectBoxList.addAction(fadeIn(0.3f, Interpolation.fade)); } static class SelectList<T> extends ScrollPane { private final TextField selectBox; private int selectedIndex; int maxListCount; private final Vector2 screenPosition = new Vector2(); final List<T> list; private InputListener hideListener; private Actor previousScrollFocus; public SelectList (Skin skin, final TextField inputBox) { super(null, skin.get(SelectBoxStyle.class).scrollStyle); this.selectBox = inputBox; setOverscroll(false, false); setFadeScrollBars(false); ListStyle listStyle = skin.get(SelectBoxStyle.class).listStyle; list = new List<>(listStyle); list.setTouchable(Touchable.disabled); setWidget(list); list.addListener(new ClickListener() { public void clicked (InputEvent event, float x, float y) { selectBox.setText(list.getSelected().toString()); selectedIndex = list.getSelectedIndex(); hide(); } public boolean mouseMoved (InputEvent event, float x, float y) { list.setSelectedIndex(Math.min(list.getItems().size - 1, (int)((list.getHeight() - y) / list.getItemHeight()))); return true; } }); addListener(new InputListener() { public void exit (InputEvent event, float x, float y, int pointer, Actor toActor) { if (toActor == null || !isAscendantOf(toActor)) list.setSelectedIndex(selectedIndex); } }); hideListener = new InputListener() { public boolean touchDown (InputEvent event, float x, float y, int pointer, int button) { Actor target = event.getTarget(); if (isAscendantOf(target)) return false; list.setSelectedIndex(selectedIndex); hide(); return false; } public boolean keyDown (InputEvent event, int keycode) { if (keycode == Keys.ESCAPE) hide(); return false; } }; } public void show (Stage stage) { if (list.isTouchable()) return; stage.removeCaptureListener(hideListener); stage.addCaptureListener(hideListener); stage.addActor(this); selectBox.localToStageCoordinates(screenPosition.set(0, 0)); // Show the list above or below the select box, limited to a number of items and the available height in the stage. float itemHeight = list.getItemHeight(); float height = itemHeight * (maxListCount <= 0 ? list.getItems().size : Math.min(maxListCount, list.getItems().size)); Drawable scrollPaneBackground = getStyle().background; if (scrollPaneBackground != null) height += scrollPaneBackground.getTopHeight() + scrollPaneBackground.getBottomHeight(); Drawable listBackground = list.getStyle().background; if (listBackground != null) height += listBackground.getTopHeight() + listBackground.getBottomHeight(); float heightBelow = screenPosition.y; float heightAbove = stage.getCamera().viewportHeight - screenPosition.y - selectBox.getHeight(); boolean below = true; if (height > heightBelow) { if (heightAbove > heightBelow) { below = false; height = Math.min(height, heightAbove); } else height = heightBelow; } if (below) setY(screenPosition.y - height); else setY(screenPosition.y + selectBox.getHeight()); setX(screenPosition.x); setSize(Math.max(getPrefWidth(), selectBox.getWidth()), height); validate(); scrollTo(0, list.getHeight() - selectedIndex * itemHeight - itemHeight / 2, 0, 0, true, true); updateVisualScroll(); previousScrollFocus = null; Actor actor = stage.getScrollFocus(); if (actor != null && !actor.isDescendantOf(this)) previousScrollFocus = actor; stage.setScrollFocus(this); list.setSelectedIndex(selectedIndex); list.setTouchable(Touchable.enabled); clearActions(); getColor().a = 0; addAction(fadeIn(0.3f, Interpolation.fade)); } public void hide () { if (!list.isTouchable() || !hasParent()) return; list.setTouchable(Touchable.disabled); Stage stage = getStage(); if (stage != null) { stage.removeCaptureListener(hideListener); if (previousScrollFocus != null && previousScrollFocus.getStage() == null) previousScrollFocus = null; Actor actor = stage.getScrollFocus(); if (actor == null || isAscendantOf(actor)) stage.setScrollFocus(previousScrollFocus); } clearActions(); getColor().a = 1; addAction(sequence(fadeOut(0.15f, Interpolation.fade), Actions.removeActor())); } public void draw (Batch batch, float parentAlpha) { selectBox.localToStageCoordinates(temp.set(0, 0)); if (!temp.equals(screenPosition)) hide(); super.draw(batch, parentAlpha); } public void act (float delta) { super.act(delta); toFront(); } public List<T> getList() { return list; } public int getSelectedIndex() { return list.getSelectedIndex(); } } }