package org.ege.widget;
import java.util.List;
import org.ege.utils.Debug;
import org.ege.utils.E;
import org.ege.utils.Properties;
import org.ege.utils.Refreshable;
import com.badlogic.gdx.graphics.g2d.SpriteBatch;
import com.badlogic.gdx.math.Interpolation;
import com.badlogic.gdx.scenes.scene2d.Actor;
import com.badlogic.gdx.scenes.scene2d.Stage;
import com.badlogic.gdx.scenes.scene2d.ui.ScrollPane;
import com.badlogic.gdx.scenes.scene2d.ui.ScrollPane.ScrollPaneStyle;
import com.badlogic.gdx.scenes.scene2d.ui.Skin;
import com.badlogic.gdx.scenes.scene2d.ui.Table;
import com.badlogic.gdx.scenes.scene2d.utils.Align;
import com.badlogic.gdx.scenes.scene2d.utils.Drawable;
import com.badlogic.gdx.utils.Array;
import com.badlogic.gdx.utils.Disposable;
import com.badlogic.gdx.utils.IntArray;
import com.badlogic.gdx.utils.ObjectMap;
import com.esotericsoftware.tablelayout.Cell;
import com.esotericsoftware.tablelayout.Value;
public abstract class SwipeView extends Table implements Refreshable,Debug,Disposable{
private int orientation;
private final FlickPane mFlickScrollPane ;
private final FlickTable mTable;
private int mCurrentRow;
private int mCurrentCol;
private final Interpolation mAutoScrollInterpolation = Interpolation.elasticOut;
private SwipeEffect mSwipeEffect;
/**
* Auto scroll to focus on one child at center
*/
private boolean isAutoFocusScroll = false;
/**
* Make a child map
*/
private final ObjectMap<Integer, Array<Actor>> RCChildMap = new ObjectMap<Integer, Array<Actor>>();
// ------------------------------------------------------------
// Auto focus data
private float currentScroll;
private float targetScroll;
private boolean justPanning = false;
private boolean startAutoScroll = false;
private float timer = 0;
private float previousVelX = 0;
private boolean checkAutoFocusX = false;
private boolean checkAutoFocusY = false;
private float previousVelY = 0;
// -------------------------------------------------------------
// Swipe effect data to get current and last focus
float cellSize ;
float spacing ;
float containerSize ;
private int mCurrentFocusID = 0;
private int mLastFocusID = -1;
private Array<Actor> tmp;
// -------------------------------------------------------------
// // Data for auto gen table property
IntArray mUnFocusID = new IntArray(2);
int tmpInt;
public SwipeView(){
super();
mTable = new FlickTable();
mFlickScrollPane = new FlickPane(mTable);
mFlickScrollPane.setFlickScroll(true);
}
public SwipeView(Drawable backgroundRegion){
super();
setBackground(backgroundRegion);
mTable = new FlickTable();
mFlickScrollPane = new FlickPane(mTable);
mFlickScrollPane.setFlickScroll(true);
}
public SwipeView(Skin skin){
super();
mTable = new FlickTable();
mFlickScrollPane = new FlickPane(mTable,skin);
mFlickScrollPane.setFlickScroll(true);
}
public SwipeView (ScrollPaneStyle style){
super();
mTable = new FlickTable();
mFlickScrollPane = new FlickPane(mTable, style);
mFlickScrollPane.setFlickScroll(true);
}
public abstract void initialize(final FlickTable flickTable);
/****************************************************************
* Container method
****************************************************************/
@Override
public void invalidate () {
super.invalidate();
refresh();
}
public void show(Stage stage){
initialize(mTable);
add(mFlickScrollPane).align(Align.center).expand();
stage.addActor(this);
}
public Table parse(Properties property){
property.apply(this);
return this;
}
/****************************************************************
* Calculate method
****************************************************************/
private void generateContainerData(){
if(orientation == 0)
return;
switch (orientation) {
case E.orientation.HORIZONTAL:
final float containerPadLeft = getPadLeft();
final float containerPadRight =getPadRight();
containerSize = (int)(getPrefWidth() - containerPadLeft - containerPadRight);
// D.out("container " + containerPadLeft + " " + containerPadRight + " " + containerSize);
break;
case E.orientation.VERTICAL:
final float containerPadTop = getPadTop();
final float containerPadBottom = getPadBottom();
containerSize = (int)(getPrefHeight() - containerPadBottom - containerPadTop);
break;
}
}
private void generateFlickData(){
if(mTable.getCells().size() == 0 || orientation == 0)
return;
switch (orientation) {
case E.orientation.HORIZONTAL:
final Cell sampleCell = mTable.getCells().get(0);
cellSize = sampleCell.getPrefWidth();
spacing = sampleCell.getSpaceRight();
// D.out("flick " +cellSize + " " + spacing + " " );
break;
case E.orientation.VERTICAL:
final Cell sampleCell1 = mTable.getCells().get(0);
cellSize = sampleCell1.getPrefHeight();
spacing = sampleCell1.getSpaceBottom();
break;
}
}
@Override
public void refresh () {
generateContainerData();
generateFlickData();
}
/****************************************************************
* Swipe method
****************************************************************/
public SwipeView putDefaultProperty(Properties property){
property.pad(0);
property.apply(mTable.defaults());
return this;
}
public void addSwipeEffect(SwipeEffect effect){
mSwipeEffect = effect;
if(mCurrentFocusID == 0 && mTable.getCells().size() > 1)
switch (orientation) {
case E.orientation.HORIZONTAL:
for(int i =0 ;i <= mCurrentRow;i++)
effect.CurrentFocusChild(getChild(i, 0));
break;
case E.orientation.VERTICAL:
final int max = mCurrentRow ;
for(int i =0 ;i < max ;i++)
effect.CurrentFocusChild(getChild(0, i));
break;
}
}
public void setAutoFocusScroll(boolean isAuto){
if(orientation != E.orientation.HORIZONTAL && orientation != E.orientation.VERTICAL )
isAutoFocusScroll = false;
else
isAutoFocusScroll = isAuto;
}
@Override
public void draw (SpriteBatch batch, float parentAlpha) {
super.draw(batch, parentAlpha);
if(isAutoFocusScroll){
if(previousVelX != mFlickScrollPane.getVelocityX()){
checkAutoFocusX = true;
previousVelX = mFlickScrollPane.getVelocityX();
}
if(previousVelY != mFlickScrollPane.getVelocityY()){
checkAutoFocusY = true;
previousVelY = mFlickScrollPane.getVelocityY();
}
if(mFlickScrollPane.isPanning()){
justPanning = true;
}
if(!mFlickScrollPane.isFlinging() && !mFlickScrollPane.isPanning()){
switch (orientation) {
case E.orientation.HORIZONTAL:
if(checkAutoFocusX || justPanning)
autoScrollX();
break;
case E.orientation.VERTICAL:
if(checkAutoFocusY || justPanning)
autoScrollY();
break;
}
}else{
startAutoScroll = false;
timer = 0;
}
}
}
@Override
public void act (float delta) {
super.act(delta);
if(orientation == 0)
return;
if(mFlickScrollPane.isFlinging() || mFlickScrollPane.isPanning()){
switch (orientation) {
case E.orientation.HORIZONTAL:
//Caculate current focus ID
mCurrentFocusID = (int) ((mFlickScrollPane.getScrollX() + spacing/2 + (containerSize/2))
/
(cellSize + spacing));
// D.out((mCurrentFocusID + " " + mFlickScrollPane.getScrollX()+ " result " + (mFlickScrollPane.getScrollX() + spacing/2 + (containerSize/2))));
tmpInt = getChildSize();
if( mCurrentFocusID >= tmpInt)
mCurrentFocusID = tmpInt - 1;
// check unfocus array
if(mUnFocusID.contains(mCurrentFocusID)){
if(mCurrentFocusID < table().getCells().size()/2)
mCurrentFocusID++;
else
mCurrentFocusID--;
}
// If nothign return;
if(mCurrentFocusID == mLastFocusID || mSwipeEffect == null )
return;
//run swipe effect
for(int i = 0 ; i <= mCurrentRow; i++){
mSwipeEffect.CurrentFocusChild(getChild(i, mCurrentFocusID));
if(mLastFocusID > 0 )
mSwipeEffect.PreviousFocusChild(getChild(i, mLastFocusID));
else
mSwipeEffect.PreviousFocusChild(getChild(i, 0));
}
mLastFocusID = mCurrentFocusID;
break;
case E.orientation.VERTICAL:
//Caculate current focus ID
mCurrentFocusID = (int) ((mFlickScrollPane.getScrollY() + (spacing/2) + (containerSize/2))
/
(cellSize + spacing ) );
tmpInt = getChildSize();
if( mCurrentFocusID >= tmpInt)
mCurrentFocusID = tmpInt - 1;
// check unfocus array
if(mUnFocusID.contains(mCurrentFocusID)){
if(mCurrentFocusID < table().getCells().size()/2)
mCurrentFocusID++;
else
mCurrentFocusID--;
}
// If nothign return;
if(mCurrentFocusID == mLastFocusID || mSwipeEffect == null)
return;
//run swipe effect
tmp = getChildList(mCurrentFocusID);
for(int i = 0 ; i < tmp.size; i++){
mSwipeEffect.CurrentFocusChild(tmp.get(i));
}
if(mLastFocusID > 0 )
tmp = getChildList(mLastFocusID);
else
tmp = getChildList(0);
for(int i = 0 ; i < tmp.size; i++){
mSwipeEffect.PreviousFocusChild(tmp.get(i));
}
mLastFocusID = mCurrentFocusID;
break;
}
}
if(startAutoScroll){
timer += delta ;
float mdelta = targetScroll - currentScroll;
float alpha = mAutoScrollInterpolation.apply(Math.min(1f, timer/1.5f));
switch (orientation) {
case E.orientation.HORIZONTAL:
mFlickScrollPane.setScrollX(currentScroll+ mdelta*alpha);
break;
case E.orientation.VERTICAL:
mFlickScrollPane.setScrollY(currentScroll+ mdelta*alpha);
break;
}
if(timer >= 1.5f){
startAutoScroll = false;
timer = 0;
}
}
}
private void autoScrollX(){
currentScroll = mFlickScrollPane.getScrollX();
targetScroll = (mCurrentFocusID*(cellSize+spacing))
-
((containerSize/2) - (cellSize/2));
startAutoScroll = true;
checkAutoFocusX = false;
justPanning = false;
}
private void autoScrollY(){
currentScroll = mFlickScrollPane.getScrollY();
targetScroll = (mCurrentFocusID*(cellSize+spacing))
-
((containerSize/2) - (cellSize/2));
startAutoScroll = true;
justPanning = false;
checkAutoFocusY = false;
}
/****************************************************************
* Child method
****************************************************************/
public void addUnfocusID(int...id){
for(int i:id)
mUnFocusID.add(i);
}
public void removeUnfocusID(int...id){
for(int i :id)
mUnFocusID.removeValue(i);
}
public int getCurrentFocusID(){
return mCurrentFocusID;
}
public int getChildSize(){
return mTable.getCells().size();
}
public ObjectMap<Integer, Array<Actor>> getRCChildMap(){
return RCChildMap;
}
public Array<Actor> getChildList(int givenRow){
return RCChildMap.get(givenRow);
}
public Actor getChild(int givenRow,int givenCol){
return getChildList(givenRow).get(givenCol);
}
/****************************************************************
* Simple get and set method
****************************************************************/
public int getCurrentRow(){
return mCurrentRow;
}
public int getCurrentCol(){
return mCurrentCol;
}
public boolean isAutoFocus(){
return isAutoFocusScroll;
}
public int getOrientation(){
return orientation;
}
public float getCellSize(){
return cellSize;
}
public float getCellSpacing(){
return spacing;
}
public float getContainerSize(){
return containerSize;
}
public String info(){
return "Orientation: " +orientation + " auto: " +isAutoFocusScroll +" row: " + mCurrentRow + " col: " + mCurrentCol;
}
/****************************************************************
* Table method
****************************************************************/
public Table table(){
return mTable;
}
public Cell tableCell(){
return mTable.getCells().get(0);
}
public Table spacing(int space){
List<Cell> cellList = mTable.getCells();
for(int i =0 ; i < cellList.size(); i++)
cellList.get(i).space(space);
generateFlickData();
return mTable;
}
/****************************************************************
* Flick scroll pane method
****************************************************************/
/** Prevents scrolling out of the widget's bounds. Default is true. */
public SwipeView setClamp(boolean isClamp){
mFlickScrollPane.setClamp(isClamp);
return this;
}
public SwipeView setSwipeOrientation(int orientation){
if(orientation == E.orientation.HORIZONTAL ||orientation == E.orientation.LANDSCAPE){
this.orientation = E.orientation.HORIZONTAL;
mFlickScrollPane.setScrollingDisabled(false, true);
}else if(orientation == E.orientation.VERTICAL ||orientation == E.orientation.PORTRAIT){
this.orientation = E.orientation.VERTICAL;
mFlickScrollPane.setScrollingDisabled(true, false);
}else{
mFlickScrollPane.setScrollingDisabled(true, true);
}
return this;
}
/** If true, the widget can be scrolled slightly past its bounds and will animate back to its bounds when scrolling is stopped.
* Default is true. */
public SwipeView setOverscroll (boolean overscroll) {
mFlickScrollPane.setOverscroll(overscroll,overscroll);
return this;
}
/** Sets the overscroll distance in pixels and the speed it returns to the widgets bounds in seconds. Default is 50, 30, 200. */
public SwipeView setupOverscroll (float distance, float speedMin, float speedMax) {
mFlickScrollPane.setupOverscroll(distance, speedMin, speedMax);
return this;
}
/** Forces the enabling of overscrolling in a direction, even if the contents do not exceed the bounds in that direction. */
public SwipeView setForceOverscroll (boolean x, boolean y) {
mFlickScrollPane.setForceOverscroll(x, y);
return this;
}
/** Sets the amount of time in seconds that a fling will continue to scroll. Default is 1. */
public SwipeView setFlingTime (float flingTime) {
mFlickScrollPane.setFlingTime(flingTime);
return this;
}
public SwipeView setVelocityX (float velocityX) {
mFlickScrollPane.setVelocityX(velocityX);
return this;
}
public SwipeView setVelocityY (float velocityY) {
mFlickScrollPane.setVelocityY(velocityY);
return this;
}
public SwipeView setFadeScrollBars (boolean fadeScrollBars) {
mFlickScrollPane.setFadeScrollBars(fadeScrollBars);
return this;
}
public SwipeView setupFadeScrollBars (float fadeAlphaSeconds, float fadeDelaySeconds) {
mFlickScrollPane.setupFadeScrollBars(fadeAlphaSeconds, fadeDelaySeconds);
return this;
}
/**
* From 0.0f to 1.0f ( slow to fast)
*/
public SwipeView setFlingSensitive (float sensitive){
mFlickScrollPane.setFlingSensitive(sensitive);
return this;
}
public float getScrollX(){
return mFlickScrollPane.getScrollX();
}
public float getScrollY(){
return mFlickScrollPane.getScrollY();
}
public float getScrollPercentX(){
return mFlickScrollPane.getScrollPercentX();
}
public float getScrollPercentY(){
return mFlickScrollPane.getScrollPercentY();
}
/**************************************************************
*
**************************************************************/
@Override
public void dispose () {
Array<Actor> a = mTable.getChildren();
final int size = a.size;
for(int i =0 ;i < size;i++){
if(a instanceof Disposable)
((Disposable)a).dispose();
}
mTable.clear();
mFlickScrollPane.clear();
clear();
}
/**************************************************************
*
**************************************************************/
private void addChild(int row,Actor actor){
Array<Actor> tmp = RCChildMap.get(row);
if(tmp == null){
tmp = new Array<Actor>();
RCChildMap.put(row, tmp);
}
tmp.add(actor);
}
/**
*
* @author trung
*/
public class FlickTable extends Table{
private int row = 0;
@Override
public Cell add (String text) {
Cell cell = super.add(text).pad(0);
addChild(row,(Actor) cell.getWidget());
return cell;
}
@Override
public Cell add (String text, String labelStyleName) {
Cell cell = super.add(text, labelStyleName).pad(0);
addChild(row,(Actor) cell.getWidget());
return cell;
}
@Override
public Cell add () {
Cell cell = super.add().pad(0);
addChild(row,(Actor) cell.getWidget());
return cell;
}
@Override
public Cell add (Actor actor) {
addChild(row,actor);
return super.add(actor).pad(0);
}
@Override
public Cell row () {
row ++;
return super.row();
}
@Override
public Table pad (Value pad) {
return super.pad(0);
}
@Override
public Table pad (Value top, Value left, Value bottom, Value right) {
return super.pad(0,0,0,0);
}
@Override
public Table padTop (Value padTop) {
return super.padTop(0);
}
@Override
public Table padLeft (Value padLeft) {
return super.padLeft(0);
}
@Override
public Table padBottom (Value padBottom) {
return super.padBottom(0);
}
@Override
public Table padRight (Value padRight) {
return super.padRight(0);
}
@Override
public Table pad (float pad) {
return super.pad(0);
}
@Override
public Table pad (float top, float left, float bottom, float right) {
return super.pad(0, 0,0,0);
}
@Override
public Table padTop (float padTop) {
return super.padTop(0);
}
@Override
public Table padLeft (float padLeft) {
return super.padLeft(0);
}
@Override
public Table padBottom (float padBottom) {
return super.padBottom(0);
}
@Override
public Table padRight (float padRight) {
return super.padRight(0);
}
}
/**
*
* @author trung
*/
private static class FlickPane extends ScrollPane{
public FlickPane (Actor widget, ScrollPaneStyle style) {
super(widget,style);
}
public FlickPane (Actor widget){
super(widget);
}
public FlickPane (Actor widget,Skin skin){
super(widget,skin);
}
@Override
public void setFlickScroll (boolean flickScroll) {
super.setFlickScroll(true);
}
}
public static interface SwipeEffect{
public void PreviousFocusChild(Actor actor);
public void CurrentFocusChild(Actor actor);
}
}