com.thetruthbeyond.gui.objects.controllers.scrollpanel.ScrollPanel.java Source code

Java tutorial

Introduction

Here is the source code for com.thetruthbeyond.gui.objects.controllers.scrollpanel.ScrollPanel.java

Source

/*
 * Copyleft (C) 2015 Piotr Siatkowski find me on Facebook;
 * This file is part of BotMaker. BotMaker is free software; you can
 * redistribute it and/or modify it under the terms of the GNU General Public
 * License as published by the Free Software Foundation; either version 2 of the
 * License, or (at your option) any later version. BotMaker is distributed in
 * the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the
 * implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See
 * the GNU General Public License for more details. You should have received a
 * copy of the GNU General Public License along with BotMaker (look at the
 * Documents directory); if not, either write to the Free Software Foundation,
 * Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA, or visit
 * (http://www.gnu.org/licenses/gpl.txt).
 */

/*
 * BotMaker - file created and updated by Piotr Siatkowski (2015).
 */

package com.thetruthbeyond.gui.objects.controllers.scrollpanel;

import com.thetruthbeyond.gui.action.EventEmitter;
import com.thetruthbeyond.gui.action.emitters.OnChooseOption;
import com.thetruthbeyond.gui.configuration.Consts;
import com.thetruthbeyond.gui.interfaces.FBODrawable;
import com.thetruthbeyond.gui.interfaces.FileManager;
import com.thetruthbeyond.gui.objects.Clickable;
import com.thetruthbeyond.gui.objects.controllers.ControllerConfiguration;
import com.thetruthbeyond.gui.objects.controllers.scrollbar.ScrollBar;
import com.thetruthbeyond.gui.structures.Area;
import com.thetruthbeyond.gui.structures.Position;
import com.thetruthbeyond.gui.utility.drawing.SmartSpriteBatch;
import com.thetruthbeyond.gui.utility.drawing.SmartTexture;

import java.lang.reflect.Constructor;
import java.lang.reflect.InvocationTargetException;
import java.util.ArrayList;
import java.util.List;

import com.thetruthbeyond.chatterbean.utility.logging.Logger;
import com.badlogic.gdx.Gdx;
import com.badlogic.gdx.graphics.Color;
import com.badlogic.gdx.graphics.GL20;
import com.badlogic.gdx.graphics.Texture;
import com.badlogic.gdx.graphics.g2d.TextureRegion;
import com.badlogic.gdx.graphics.glutils.FrameBuffer;
import com.thetruthbeyond.gui.utility.gl.BUFFER_NUMBER;
import com.thetruthbeyond.gui.utility.gl.Buffers;

/**
 * Scroll panel class is written in top-down y coordinate system. StartY is negative if panel is scrolled down.
 */
public class ScrollPanel extends Clickable {

    private SmartTexture canvas;
    private SmartTexture border;

    private int startY;
    private int totalH;

    private final List<Clickable> objects = new ArrayList<>(8);
    private final List<Integer> positionsY = new ArrayList<>(8);

    private Area objectArea;
    private int padding;
    private int borderSize;

    private int gapH;
    private int objectH;

    private int rows;
    private int columns;

    private FrameBuffer buffer;
    private TextureRegion region;

    private ScrollBar scrollBar;

    private float backgroundAlpha;

    private static final float APPEAR_TIME = 1.0f;
    private float appearTime = 0.0f;

    private float[] ratios = null;
    private float[] gaps = null;

    private boolean drawBackground = true;
    private boolean includeBoundaryPadding = true;
    private boolean drawScrollBarEbenIfCantBeScrolled = false;

    private int pressedObjectIndex;

    private FileManager loader;

    // For decorating purposes. ///////////////////////////////////////////////////////////////////////
    protected ScrollPanel() {
        super(null);
    }

    public ScrollPanel getDecoratorParent() {
        return null;
    }

    protected ScrollBar getScrollBar() {
        return scrollBar;
    }

    @SuppressWarnings("ReturnOfCollectionOrArrayField")
    protected List<Clickable> getObjects() {
        return objects;
    }

    @SuppressWarnings("ReturnOfCollectionOrArrayField")
    protected List<Integer> getPositionsY() {
        return positionsY;
    }

    protected int getPressedObjectIndex() {
        return pressedObjectIndex;
    }

    protected int getRows() {
        return rows;
    }

    protected int getColumns() {
        return columns;
    }

    protected int getObjectAreaX() {
        return objectArea.getX();
    }

    protected int getObjectAreaY() {
        return objectArea.getY();
    }

    protected int getObjectAreaW() {
        return objectArea.getW();
    }

    protected int getObjectAreaH() {
        return objectArea.getH();
    }

    protected int getObjectH() {
        return objectH;
    }

    protected int getGapH() {
        return gapH;
    }

    protected void setStartY(int startY) {
        this.startY = startY;
    }

    protected int getStartY() {
        return startY;
    }

    protected void setTotalH(int totalH) {
        this.totalH = totalH;
    }

    protected int getTotalH() {
        return totalH;
    }

    protected float getBackgroundAlpha() {
        return backgroundAlpha;
    }

    protected void setBackgroundAlpha(float alpha) {
        backgroundAlpha = alpha;
    }

    ///////////////////////////////////////////////////////////////////////////////////////////////////

    public ScrollPanel(ScrollPanelConfiguration configuration, FileManager loader, Clickable parent) {
        super(configuration, parent);

        canvas = loader.getTexture("Canvas");
        border = loader.getTexture("Border");

        this.loader = loader;

        borderSize = configuration.border;

        padding = Math.round(configuration.relativePadding * Math.min(area.getW(), area.getH()));

        objectArea = new Area(configuration.area);
        if (configuration.drawBackground)
            objectArea.cutArea(padding + borderSize);

        columns = configuration.columnsNumber;
        rows = configuration.rowsNumber;

        gapH = Math.round(objectArea.getH() * configuration.relativeGapH);

        if (configuration.letResizeArea) {
            int gapsNumber = includeBoundaryPadding ? rows + 1 : rows - 1;
            objectH = Math.round((objectArea.getH() - gapsNumber * gapH) / (float) rows);

            int freePixels = objectArea.getH() - gapsNumber * gapH - rows * objectH;
            if (freePixels != 0) {
                if (freePixels > gapsNumber / 2.0f) {
                    objectH = objectH + 1;
                    area.setH(area.getH() + (freePixels - rows));
                    objectArea.setH(objectArea.getH() + (freePixels - rows));
                } else {
                    area.setH(area.getH() - freePixels);
                    objectArea.setH(objectArea.getH() - freePixels);
                }
            }
        } else {
            if (configuration.includeBoundaryPadding) {
                objectH = Math.round((objectArea.getH() - (rows + 1) * gapH) / (float) rows);

                // Estimate more proper size of gap or element height.
                int freePixels = objectArea.getH() - (rows + 1) * gapH - rows * objectH;
                if (freePixels != 0) {
                    if (freePixels > rows / 2)
                        objectH = objectH + 1;
                    else
                        gapH = gapH + 1;
                }
            } else {
                objectH = Math.round((objectArea.getH() - (rows - 1) * gapH) / (float) rows);

                // Estimate more proper size of gap or element height.
                int freePixels = objectArea.getH() - (rows - 1) * gapH - rows * objectH;
                if (freePixels != 0) {
                    if (freePixels > rows / 2)
                        objectH = objectH + 1;
                    else
                        gapH = gapH + 1;
                }
            }
        }

        buffer = Buffers.getBuffer(BUFFER_NUMBER.ONE);
        scrollBar = configuration.scrollbar;

        backgroundAlpha = configuration.backgroundAlpha;

        // Calculate field and horizontal gap width ratios.
        ratios = new float[columns];
        float defaultW = (1 - (columns + 1) * configuration.relativeGapW) / columns;
        for (int i = 0; i != columns; i++)
            ratios[i] = defaultW;

        gaps = new float[columns + 1];
        for (int i = 0; i != columns + 1; i++)
            gaps[i] = configuration.relativeGapW;

        totalH = objectArea.getH();

        if ((scrollBar = configuration.scrollbar) != null)
            getObserver().observeEmitter(scrollBar.getEmitter(OnChooseOption.Id));

        drawBackground = configuration.drawBackground;
        includeBoundaryPadding = configuration.includeBoundaryPadding;
        drawScrollBarEbenIfCantBeScrolled = configuration.drawScrollBarEvenIfCantBeScrolled;
    }

    protected boolean isBoundaryPaddingIncluded() {
        return includeBoundaryPadding;
    }

    private void updateScrollBar() {
        startY = Math.max(startY, 0);
        startY = Math.min(startY, totalH - objectArea.getH());

        scrollBar.setBarNumbers(startY, objectArea.getH(), Math.max(0, totalH - startY - objectArea.getH()));
    }

    private void readOffsetFromScrollBar() {
        float scrollPosition = scrollBar.getPercentagePosition();
        startY = (int) (scrollPosition * (totalH - objectArea.getH()));
    }

    private void updatePositionOfObjects() {
        for (int i = 0; i != objects.size(); i++)
            objects.get(i).changeY(positionsY.get(i) - startY);
    }

    @Override
    public void reactToEmittedSignal(EventEmitter object, int emitterID) {
        if (emitterID == OnChooseOption.Id) {

            OnChooseOption onChooseOption = object.getEmitter(emitterID);
            int h = gapH + objectH;
            int rest = startY % h;

            switch (onChooseOption.getChosenOption()) {

            case OPTION_A: {
                readOffsetFromScrollBar();
                updatePositionOfObjects();
                break;
            }

            case OPTION_B: {
                if (rest - gapH < objectH / 2)
                    startY = startY - rest - h;
                else
                    startY -= rest;
                updateScrollBar();
                updatePositionOfObjects();
                break;
            }

            case OPTION_C: {
                if (rest - gapH < objectH / 2)
                    startY = startY + rest + h;
                else
                    startY += rest;
                updateScrollBar();
                updatePositionOfObjects();
                break;
            }
            }
        }
    }

    public void setFieldRatios(float[] ratios) {
        if (ratios.length == columns) {
            float totalW = 0.0f;
            for (int i = 0; i != columns; i++)
                totalW += ratios[i];

            // Normalization.
            if (totalW > 1) {
                for (int i = 0; i != columns; i++)
                    ratios[i] /= totalW;
                gaps = new float[columns + 1];
            } else {
                gaps = new float[columns + 1];
                float gapW = (1 - totalW) / (columns + 1);
                for (int i = 0; i != columns + 1; i++)
                    gaps[i] = gapW;
            }

            this.ratios = ratios;
        } else {
            float totalW = 0.0f;
            for (int i = 0; i != ratios.length; i++)
                totalW += ratios[i];

            if (totalW > 1) {
                for (int i = 0; i != ratios.length; i++)
                    ratios[i] /= totalW;
            }

            for (int i = 0, j = 0; i < ratios.length; i += 2, j++)
                gaps[j] = ratios[i];

            for (int i = 1, j = 0; i < ratios.length; i += 2, j++)
                this.ratios[j] = ratios[i];
        }
    }

    @Override
    public void changeX(float x) {
        int dx = Math.round(x) - area.getX();

        objectArea.setX(objectArea.getX() + dx);
        for (int i = 0; i != objects.size(); i++) {
            Clickable object = objects.get(i);
            object.changeX(object.getX() + dx);
        }

        area.setX(x);
        objectArea.setY(x + padding + borderSize);
    }

    @Override
    public void changeY(float y) {
        int dy = Math.round(y) - area.getY();

        for (int i = 0; i != objects.size(); i++) {
            Clickable object = objects.get(i);
            object.changeY(object.getY() + dy);
        }

        area.setY(y);
        objectArea.setY(y + padding + borderSize);
    }

    public int getObjectsSize() {
        return objects.size();
    }

    public Clickable getObject(int index) {
        return objects.get(index);
    }

    public Clickable getObject(int row, int column) {
        return objects.get(row * columns + column);
    }

    public <T extends Clickable> T substituteObject(Clickable object, T decorated) {
        int index = objects.indexOf(object);
        objects.set(index, decorated);
        return decorated;
    }

    public <T extends ControllerConfiguration, U extends Clickable> U addObjectImmediately(Class<U> type,
            T configuration) {
        return addObject(type, configuration, false);
    }

    public <T extends ControllerConfiguration, U extends Clickable> U addObjectWithAppear(Class<U> type,
            T configuration) {
        return addObject(type, configuration, true);
    }

    protected <T extends ControllerConfiguration, U extends Clickable> U addObject(Class<U> type, T configuration,
            boolean isAppear) {
        int size = objects.size();

        int column = size % columns;
        int row = size / columns;

        try {
            Constructor<U> constructor;

            if (configuration != null)
                constructor = type.getConstructor(configuration.getClass(), FileManager.class, Clickable.class);
            else
                constructor = type.getConstructor(Area.class, FileManager.class, Clickable.class);

            float currentX = 0.0f;
            for (int i = 0; i < column; i++)
                currentX += ratios[i];
            for (int i = 0; i < column + 1; i++)
                currentX += gaps[i];

            Area area = new Area();
            area.setX(objectArea.getX() + objectArea.getW() * currentX);
            if (includeBoundaryPadding)
                area.setY(row * objectH + (row + 1) * gapH + objectArea.getY());
            else
                area.setY(row * objectH + row * gapH + objectArea.getY());
            area.setW(ratios[column] * objectArea.getW());
            area.setH(objectH);

            U object;
            if (configuration != null) {
                configuration.area = area;
                object = constructor.newInstance(configuration, loader, this);
            } else
                object = constructor.newInstance(area, loader, this);

            objects.add(object);
            positionsY.add(object.getY());

            object.changeY(object.getY() - startY);

            // Set new bar ratios only if new row is added.
            if (scrollBar != null) {
                if (objects.size() > rows * columns && (columns == 1 || objects.size() % columns == 1)) {
                    totalH = totalH + objectH + gapH;
                    updateScrollBar();
                }
            }

            if (isAppear) {
                appearTime = 0.0f;
            } else
                appearTime = APPEAR_TIME;

            return object;
        } catch (InstantiationException exception) {
            new Logger().writeMessage("Error: ScrollPanel -> Instantation Esception", exception.getMessage());
        } catch (IllegalAccessException exception) {
            new Logger().writeMessage("Error: ScrollPanel -> Illegal Access Exception", exception.getMessage());
        } catch (IllegalArgumentException exception) {
            new Logger().writeMessage("Error: ScrollPanel -> Illegal Argument Exception", exception.getMessage());
        } catch (InvocationTargetException exception) {
            new Logger().writeMessage("Error: ScrollPanel -> Invocation Target Exception", exception.getMessage());
        } catch (NoSuchMethodException exception) {
            new Logger().writeMessage("Error: ScrollPanel -> No such Method Exception", exception.getMessage());
        } catch (SecurityException exception) {
            new Logger().writeMessage("Error: ScrollPanel -> Security Exception", exception.getMessage());
        }

        return null;
    }

    public void removeRow(int row) {
        if (row * columns > objects.size())
            return;

        if (objects.size() > rows * columns) {
            totalH = totalH - objectH - gapH;
            updateScrollBar();
        }

        // Removing objects.
        for (int column = columns - 1; column >= 0; column--)
            objects.remove(row * columns + column);

        // Removing last positions.
        for (int column = 0; column != columns; ++column)
            positionsY.remove(positionsY.size() - 1);

        //      readOffsetFromScrollBar();
        updatePositionOfObjects();

        // Updating position.
        for (int i = row * columns; i < objects.size(); ++i)
            objects.get(i).changeY(positionsY.get(i) - startY);
    }

    protected boolean hasUnionWith(Area compareArea) {
        if (compareArea.getX() >= area.getX() && compareArea.getX() <= area.getX() + area.getW()
                && compareArea.getY() >= area.getY() + startY && compareArea.getY() <= area.getY() + area.getH())
            return true;
        if (compareArea.getX() + compareArea.getW() >= area.getX()
                && compareArea.getX() + compareArea.getW() <= area.getX() + area.getW()
                && compareArea.getY() >= area.getY() && compareArea.getY() <= area.getY() + area.getH())
            return true;
        if (compareArea.getX() >= area.getX() && compareArea.getX() <= area.getX() + area.getW()
                && compareArea.getY() + compareArea.getH() >= area.getY()
                && compareArea.getY() + compareArea.getH() <= area.getY() + area.getH())
            return true;
        return compareArea.getX() + compareArea.getW() >= area.getX()
                && compareArea.getX() + compareArea.getW() <= area.getX() + area.getW()
                && compareArea.getY() + compareArea.getH() >= area.getY()
                && compareArea.getY() + compareArea.getH() <= area.getY() + area.getH();
    }

    @Override
    public boolean contains(Position position) {
        return !(position.x <= objectArea.getX() || position.x >= objectArea.getX() + objectArea.getW()
                || position.y <= objectArea.getY() || position.y >= objectArea.getY() + objectArea.getH());
    }

    @Override
    public boolean executeMouseHover(Position position) {
        for (Clickable object : objects)
            object.executeMouseHover(position);
        if (scrollBar != null)
            scrollBar.executeMouseHover(position);
        return false;
    }

    @Override
    public boolean executeMouseDrag(Position position) {
        if (scrollBar != null && scrollBar.executeMouseDrag(position)) {
            readOffsetFromScrollBar();
            updatePositionOfObjects();
            return true;
        } else
            return contains(position);
    }

    @Override
    public boolean executeMousePress(Position position) {

        boolean isFieldPressed = false;
        for (int i = 0; i != objects.size(); i++) {
            Clickable object = objects.get(i);
            if (object.executeMousePress(position)) {
                isFieldPressed = true;
                pressedObjectIndex = i;
            }
        }

        if (isFieldPressed)
            return true;

        return scrollBar != null && scrollBar.executeMousePress(position);

    }

    @Override
    public boolean executeMousePressRight(Position position) {
        for (int i = 0; i != objects.size(); i++) {
            Clickable object = objects.get(i);
            object.executeMousePressRight(position);
        }

        return false;
    }

    @Override
    public boolean executeMouseRelease(Position position) {
        if (scrollBar != null)
            scrollBar.executeMouseRelease(position);
        return true;
    }

    @Override
    public void update(float delta) {
        if (appearTime < APPEAR_TIME)
            appearTime = Math.min(APPEAR_TIME, appearTime + delta);

        for (Clickable object : objects)
            object.update(delta);

        if (scrollBar != null)
            scrollBar.update(delta);
    }

    @Override
    public void draw(SmartSpriteBatch batch) {
        drawBackground(batch);

        if (!objects.isEmpty()) {
            batch.end();
            drawObjectsToBuffer(batch);
            batch.begin();
            drawBuffer(batch);
        }

        drawScrollBar(batch);
    }

    protected void drawBackground(SmartSpriteBatch batch) {
        if (drawBackground) {
            Color color = batch.getColor();

            batch.setColor(1.0f, 1.0f, 1.0f, color.a * backgroundAlpha);
            batch.draw(canvas, area.getX(), area.getY(), area.getW(), area.getH());

            batch.setColor(color);

            // Drawing border
            batch.draw(border, area.getX(), area.getY(), area.getW(), borderSize);
            batch.draw(border, area.getX(), area.getY(), borderSize, area.getH());
            batch.draw(border, area.getX(), area.getY() + area.getH() - borderSize, area.getW(), borderSize);
            batch.draw(border, area.getX() + area.getW() - borderSize, area.getY(), borderSize, area.getH());
        }
    }

    protected void drawObjectsToBuffer(SmartSpriteBatch batch) {
        Color color = batch.getColor();

        float alpha = color.a * appearTime / APPEAR_TIME;
        buffer.begin();
        batch.begin();
        Gdx.graphics.getGL20().glClearColor(0.6f, 0.6f, 0.0f, 0.0f);
        Gdx.graphics.getGL20().glClear(GL20.GL_COLOR_BUFFER_BIT | GL20.GL_DEPTH_BUFFER_BIT);

        for (int i = 0; i != objects.size(); i++) {
            Clickable object = objects.get(i);
            if (!hasUnionWith(object.getArea()))
                continue;

            if (i >= objects.size() - columns)
                batch.setColor(1.0f, 1.0f, 1.0f, alpha);
            else
                batch.setColor(color);

            if (object instanceof FBODrawable)
                ((FBODrawable) object).hintFBODrawing();
            object.draw(batch);
        }

        batch.setColor(Color.WHITE);
        batch.end();

        buffer.end();
    }

    protected void drawBuffer(SmartSpriteBatch batch) {

        Texture bufferTexture = buffer.getColorBufferTexture();
        if (region == null) {
            region = new TextureRegion(bufferTexture, objectArea.getX(),
                    Consts.SCREEN_H - (objectArea.getY() + objectArea.getH()), objectArea.getW(),
                    objectArea.getH());
            region.flip(false, true);
        } else
            region.setTexture(bufferTexture);

        batch.draw(region, objectArea.getX(), objectArea.getY(), objectArea.getW(), objectArea.getH());
        batch.setColor(Color.WHITE);
    }

    protected void drawScrollBar(SmartSpriteBatch batch) {
        if (scrollBar != null && (drawScrollBarEbenIfCantBeScrolled || objects.size() > rows * columns))
            scrollBar.draw(batch);
    }

    public void clear() {
        objects.clear();
        positionsY.clear();

        startY = 0;
        totalH = objectArea.getH();
    }
}