package com.crashinvaders.texturepackergui.views.seekbar; import com.badlogic.gdx.Input; import com.badlogic.gdx.graphics.Color; import com.badlogic.gdx.graphics.g2d.Batch; import com.badlogic.gdx.scenes.scene2d.Actor; import com.badlogic.gdx.scenes.scene2d.Event; import com.badlogic.gdx.scenes.scene2d.InputEvent; import com.badlogic.gdx.scenes.scene2d.InputListener; import com.badlogic.gdx.scenes.scene2d.ui.Slider; import com.badlogic.gdx.scenes.scene2d.ui.Table; import com.badlogic.gdx.scenes.scene2d.utils.ChangeListener; import com.badlogic.gdx.scenes.scene2d.utils.Drawable; import com.badlogic.gdx.utils.Pools; import com.kotcrab.vis.ui.FocusManager; import com.kotcrab.vis.ui.Focusable; import com.kotcrab.vis.ui.util.BorderOwner; import com.kotcrab.vis.ui.widget.VisSlider; import com.kotcrab.vis.ui.widget.VisTextField; import com.kotcrab.vis.ui.widget.VisValidatableTextField; import com.kotcrab.vis.ui.widget.spinner.Spinner; //TODO Implement Disableable. public class SeekBar extends Table implements Focusable, BorderOwner { private final SeekBarModel model; private final Style style; private final VisValidatableTextField textField; private final VisSlider slider; private ChangeEventPolicy changeEventPolicy = ChangeEventPolicy.ON_FOCUS_LOST; private boolean focusBorderEnabled = true; private boolean focused = false; private boolean programmaticChangeEvents = true; private boolean listenTextFieldChangeEvents = false; private boolean listenSliderChangeEvents = false; public SeekBar(final SeekBarModel model, final Style style) { this.model = model; this.style = style; this.setTransform(false); textField = new VisValidatableTextField("", style.textFieldStyle); slider = new VisSlider(0f, 1f, 0.01f, false, style.sliderStyle); this.add(textField).width(style.textFieldWidth); this.add(slider).minWidth(72f).growX(); addListener(new InputListener() { @Override public boolean touchDown (InputEvent event, float x, float y, int pointer, int button) { /*if (isDisabled() == false)*/ FocusManager.switchFocus(getStage(), SeekBar.this); return false; } @Override public boolean handle(Event e) { if (super.handle(e)) return true; if (!(e instanceof InputEvent)) return false; InputEvent event = (InputEvent)e; switch (event.getType()) { case enter: case exit: return textField.notify(e, false); default: return false; } } }); // Register view listeners. { textField.addListener(new ChangeListener() { @Override public void changed(ChangeEvent event, Actor actor) { event.stop(); if (!listenTextFieldChangeEvents) return; model.onTextValueChanged(); if (textField.isInputValid() && changeEventPolicy == ChangeEventPolicy.ON_TEXT_SLIDER_CHANGED) { notifyValueChanged(false, true, true); } } }); textField.addListener(new InputListener() { @Override public boolean keyDown (InputEvent event, int keycode) { if (keycode == Input.Keys.ENTER) { notifyValueChanged(false, false, true); return true; } return false; } }); slider.addListener(new ChangeListener() { @Override public void changed(ChangeEvent event, Actor actor) { event.stop(); if (!listenSliderChangeEvents) return; model.onSliderValueChanged(); if (changeEventPolicy == ChangeEventPolicy.ON_TEXT_SLIDER_CHANGED) { notifyValueChanged(false, false, true); } textField.selectAll(); } }); } model.bind(this); setListenTextChangeEvents(true); setListenSliderChangeEvents(true); } @Override public boolean isFocusBorderEnabled() { return focusBorderEnabled; } @Override public void setFocusBorderEnabled(boolean focusBorderEnabled) { this.focusBorderEnabled = focusBorderEnabled; } @Override public void focusGained() { focused = true; getStage().setKeyboardFocus(textField); textField.focusGained(); } @Override public void focusLost() { focused = false; notifyValueChanged(true, true, false); if (changeEventPolicy == ChangeEventPolicy.ON_FOCUS_LOST) { notifyValueChanged(false, false, true); } } @Override public void draw(Batch batch, float parentAlpha) { super.draw(batch, parentAlpha); if (focused && focusBorderEnabled && style.focusBorder != null) { Color color = getColor(); batch.setColor(color.r, color.g, color.b, color.a * parentAlpha); style.focusBorder.draw(batch, getX(), getY(), getWidth(), getHeight()); } } public VisValidatableTextField getTextField() { return textField; } public VisSlider getSlider() { return slider; } /** If false, methods changing spinner value form code won't trigger change event, it will be fired only when user has changed value. */ public void setProgrammaticChangeEvents (boolean programmaticChangeEvents) { this.programmaticChangeEvents = programmaticChangeEvents; } public boolean isProgrammaticChangeEvents () { return programmaticChangeEvents; } /** * Called by {@link SeekBarModel}. Notifies when underlying model value has changed and text field and slider must update. * Typically there is no need to call this method manually. * @param fireEvent if true then {@link ChangeListener.ChangeEvent} will be fired */ public void notifyValueChanged (boolean updateTextField, boolean updateSlider, boolean fireEvent) { if (updateTextField) { VisValidatableTextField textField = getTextField(); int cursor = textField.getCursorPosition(); textField.setCursorPosition(0); this.setListenTextChangeEvents(false); textField.setText(model.prepareTextValue()); this.setListenTextChangeEvents(true); textField.setCursorPosition(cursor); } if (updateSlider) { VisSlider slider = getSlider(); this.setListenSliderChangeEvents(false); slider.setValue(model.prepareSliderValue()); this.setListenSliderChangeEvents(true); } if (fireEvent) { ChangeListener.ChangeEvent changeEvent = Pools.obtain(ChangeListener.ChangeEvent.class); fire(changeEvent); Pools.free(changeEvent); } } public void setListenTextChangeEvents(boolean listenChangeEvents) { this.listenTextFieldChangeEvents = listenChangeEvents; } public void setListenSliderChangeEvents(boolean listenChangeEvents) { this.listenSliderChangeEvents = listenChangeEvents; } public ChangeEventPolicy getChangeEventPolicy() { return changeEventPolicy; } public void setChangeEventPolicy(ChangeEventPolicy changeEventPolicy) { this.changeEventPolicy = changeEventPolicy; } public SeekBarModel getModel() { return model; } public Style getStyle() { return style; } public static class Style { public VisTextField.VisTextFieldStyle textFieldStyle; public Slider.SliderStyle sliderStyle; public float textFieldWidth = 54f; /** Optional */ public Drawable focusBorder; } /** @see Spinner.TextFieldEventPolicy */ public enum ChangeEventPolicy { /** * Spinner change event will be only fired after user has pressed enter in text field. */ ON_ENTER_ONLY, /** * Seek bar change event will be always fired after it has lost focus and entered value is valid. Note * that event will be fired even if user has not changed actual value of seek bar. * This mode is the default one. */ ON_FOCUS_LOST, /** * Seek bar change event will be fired right after user has dragged slider or typed something in the text field and model has * determined that entered value is valid. Event won't be fired if entered value is invalid. */ ON_TEXT_SLIDER_CHANGED } }