cn.liutils.cgui.gui.LIGui.java Source code

Java tutorial

Introduction

Here is the source code for cn.liutils.cgui.gui.LIGui.java

Source

/**
 * Copyright (c) Lambda Innovation, 2013-2015
 * ??Lambda Innovation
 * http://www.li-dev.cn/
 *
 * This project is open-source, and it is distributed under 
 * the terms of GNU General Public License. You can modify
 * and distribute freely as long as you follow the license.
 * ??GNU???
 * ????
 * http://www.gnu.org/licenses/gpl.html
 */
package cn.liutils.cgui.gui;

import java.util.Iterator;

import org.lwjgl.opengl.GL11;
import org.lwjgl.util.glu.GLU;

import cn.liutils.cgui.gui.component.Transform;
import cn.liutils.cgui.gui.event.DragEvent;
import cn.liutils.cgui.gui.event.FrameEvent;
import cn.liutils.cgui.gui.event.GainFocusEvent;
import cn.liutils.cgui.gui.event.GuiEvent;
import cn.liutils.cgui.gui.event.GuiEventBus;
import cn.liutils.cgui.gui.event.GuiEventHandler;
import cn.liutils.cgui.gui.event.KeyEvent;
import cn.liutils.cgui.gui.event.LostFocusEvent;
import cn.liutils.cgui.gui.event.MouseDownEvent;
import cn.liutils.cgui.gui.event.RefreshEvent;
import cn.liutils.cgui.gui.event.global.AddWidgetEvent;
import cn.liutils.cgui.gui.event.global.GlobalMouseEvent;
import cn.liutils.core.LIUtils;
import cn.liutils.util.client.HudUtils;
import cn.liutils.util.helper.GameTimer;

/**
 * @author WeathFolD
 */
public class LIGui extends WidgetContainer {

    double width, height; //Only useful when calculating 'CENTER' align preference

    //Absolute mouse position.
    public double mouseX, mouseY;

    Widget focus; //last input focus

    GuiEventBus eventBus = new GuiEventBus();

    public LIGui() {
    }

    public LIGui(double width, double height) {
        this.width = width;
        this.height = height;
    }

    public void dispose() {
    }

    /**
     * Called when screen is being resized.
     * @param w new width
     * @param h new height
     */
    public void resize(double w, double h) {
        boolean diff = width != w || height != h;

        this.width = w;
        this.height = h;

        if (diff) {
            for (Widget widget : this) {
                widget.dirty = true;
            }
        }
    }

    //---Event callback---

    public void draw() {
        draw(-1, -1);
    }

    /**
     * Go down the hierarchy tree and draw each widget node.
     */
    public void draw(double mx, double my) {
        frameUpdate();
        updateMouse(mx, my);

        GL11.glDisable(GL11.GL_ALPHA_TEST);
        GL11.glEnable(GL11.GL_BLEND);
        GL11.glBlendFunc(GL11.GL_SRC_ALPHA, GL11.GL_ONE_MINUS_SRC_ALPHA);

        drawTraverse(mx, my, null, this, getTopWidget(mx, my));

        GL11.glEnable(GL11.GL_ALPHA_TEST);
    }

    @Override
    public boolean addWidget(String name, Widget w) {
        if (this.hasWidget(name))
            return false;
        super.addWidget(name, w);
        this.postEvent(new AddWidgetEvent(w));
        return true;
    }

    static final long DRAG_TIME_TOLE = 100;
    long lastStartTime, lastDragTime;
    Widget draggingNode;
    double xOffset, yOffset;

    /**
     * Standard GUI class callback.
     * @param mx
     * @param my
     * @param btn the mouse button ID.
     * @param dt how long is this button being pressed(ms)
     */
    public boolean mouseClickMove(int mx, int my, int btn, long dt) {
        updateMouse(mx, my);
        if (btn == 0) {
            long time = GameTimer.getAbsTime();
            if (Math.abs(time - dt - lastStartTime) > DRAG_TIME_TOLE) {
                lastStartTime = time;
                draggingNode = getTopWidget(mx, my);
                if (draggingNode == null)
                    return false;
                xOffset = mx - draggingNode.x;
                yOffset = my - draggingNode.y;
            }
            if (draggingNode != null) {
                lastDragTime = time;
                draggingNode.postEvent(new DragEvent());
                return true;
            }
        }
        return false;
    }

    /**
    * Standard GUI mouseClicked callback.
    * @param mx
    * @param my
    * @param btn the mouse button ID.
    * @return if any action was performed on a widget.
    */
    public boolean mouseClicked(int mx, int my, int bid) {
        updateMouse(mx, my);
        eventBus.postEvent(null, new GlobalMouseEvent(mx, my, bid));

        if (bid == 0) {
            Widget node = getTopWidget(mx, my);
            if (node != null) {
                gainFocus(node);
                node.postEvent(new MouseDownEvent((mx - node.x) / node.scale, (my - node.y) / node.scale));
                return true;
            } else {
                removeFocus();
            }
        }

        return false;
    }

    public void removeFocus() {
        if (focus != null) {
            focus.postEvent(new LostFocusEvent());
            focus = null;
        }
    }

    /**
     * Gain a widget's focus with force.
     */
    public void gainFocus(Widget node) {
        if (node == focus) {
            return;
        }
        if (focus != null) {
            removeFocus();
        }
        focus = node;
        focus.postEvent(new GainFocusEvent());
    }

    public void keyTyped(char ch, int key) {
        if (focus != null) {
            focus.postEvent(new KeyEvent(ch, key));
        }
    }

    //---Helper Methods---
    public Widget getTopWidget(double x, double y) {
        return gtnTraverse(x, y, null, this);
    }

    public Widget getHoveringWidget() {
        return getTopWidget(mouseX, mouseY);
    }

    public void updateDragWidget() {
        if (draggingNode != null) {
            moveWidgetToAbsPos(draggingNode, mouseX - xOffset, mouseY - yOffset);
        }
    }

    /**
     * Inverse calculation. Move this widget to the ABSOLUTE window position (x0, y0).
     * Note that the widget's position may be further changed because of its parent widget's position change.
     */
    public void moveWidgetToAbsPos(Widget widget, double x0, double y0) {
        Transform transform = widget.transform;
        double tx, ty;
        double tw, th;
        double parentScale;

        if (widget.isWidgetParent()) {
            Widget p = widget.getWidgetParent();
            tx = p.x;
            ty = p.y;
            tw = p.transform.width * p.scale;
            th = p.transform.height * p.scale;
            parentScale = p.scale;

            widget.scale = transform.scale * p.scale;
        } else {
            tx = ty = 0;
            tw = width;
            th = height;
            parentScale = 1;

            widget.scale = transform.scale;
        }

        double xx = 0;
        switch (transform.alignWidth) {
        case CENTER:
            xx = (x0 - tx - (tw - transform.width * transform.scale) / 2) / parentScale;
            break;
        case LEFT:
            xx = (x0 - tx) / parentScale;
            break;
        case RIGHT:
            xx = (x0 - tx - (tw - transform.width * transform.scale)) / widget.scale;
            break;
        }
        transform.x = xx;

        double yy = 0;
        switch (transform.alignHeight) {
        case CENTER:
            yy = (y0 - ty - (tw - transform.width * transform.scale) / 2) / parentScale;
            break;
        case TOP:
            yy = (y0 - ty) / parentScale;
            break;
        case BOTTOM:
            yy = (y0 - ty - (tw - transform.width * transform.scale)) / parentScale;
            break;
        }
        transform.y = yy;

        widget.x = x0;
        widget.y = y0;

        widget.dirty = true;
    }

    public Widget getDraggingWidget() {
        return Math.abs(GameTimer.getAbsTime() - lastDragTime) > DRAG_TIME_TOLE || draggingNode == null ? null
                : draggingNode;
    }

    public Widget getFocus() {
        return focus;
    }

    //---Key Handling

    //---Internal Processing
    public void updateWidget(Widget widget) {
        widget.gui = this;

        Transform transform = widget.transform;

        double tx, ty;
        double tw, th;
        double parentScale;
        if (widget.isWidgetParent()) {
            Widget p = widget.getWidgetParent();
            tx = p.x;
            ty = p.y;
            tw = p.transform.width * p.scale;
            th = p.transform.height * p.scale;
            parentScale = p.scale;

            widget.scale = transform.scale * p.scale;
        } else {
            tx = ty = 0;
            tw = width;
            th = height;

            parentScale = 1;
            widget.scale = transform.scale;
        }

        double x0 = 0;
        switch (transform.alignWidth) {
        case CENTER:
            x0 = tx + (tw - transform.width * widget.scale) / 2 + transform.x * parentScale;
            break;
        case LEFT:
            x0 = tx + transform.x * parentScale;
            break;
        case RIGHT:
            x0 = tx + (tw - transform.width * widget.scale) + transform.x * parentScale;
            break;
        }
        widget.x = x0;

        double y0 = 0;
        switch (transform.alignHeight) {
        case CENTER:
            y0 = ty + (th - transform.height * widget.scale) / 2 + transform.y * parentScale;
            break;
        case TOP:
            y0 = ty + transform.y * parentScale;
            break;
        case BOTTOM:
            y0 = ty + (th - transform.height * widget.scale) + transform.y * parentScale;
            break;
        }
        widget.y = y0;

        widget.dirty = false;

        //Check sub widgets
        for (Widget w : widget) {
            updateWidget(w);
        }
    }

    /**
     * Generic checking.
     */
    private void frameUpdate() {
        updateTraverse(null, this);
        this.update();
    }

    private void updateTraverse(Widget cur, WidgetContainer set) {
        if (cur != null) {
            if (cur.dirty) {
                cur.postEvent(new RefreshEvent());
                this.updateWidget(cur);
            }
        }

        Iterator<Widget> iter = set.iterator();
        while (iter.hasNext()) {
            Widget widget = iter.next();
            if (!widget.disposed) {
                updateTraverse(widget, widget);
                widget.update();
            }
        }
    }

    private void updateMouse(double mx, double my) {
        this.mouseX = mx;
        this.mouseY = my;
    }

    private void drawTraverse(double mx, double my, Widget cur, WidgetContainer set, Widget top) {
        try {
            if (cur != null && cur.isVisible()) {
                GL11.glPushMatrix();
                GL11.glTranslated(cur.x, cur.y, 0);

                double px = cur.transform.pivotX * cur.scale, py = cur.transform.pivotY * cur.scale;

                GL11.glScaled(cur.scale, cur.scale, 1);
                GL11.glTranslated(-cur.transform.pivotX, -cur.transform.pivotY, 0);

                GL11.glColor4d(1, 1, 1, 1); //Force restore color for any widget
                cur.postEvent(new FrameEvent((mx - cur.x) / cur.scale, (my - cur.y) / cur.scale, cur == top));
                GL11.glPopMatrix();
            }
        } catch (Exception e) {
            LIUtils.log.error("Error occured handling widget draw. instance class: " + cur.getClass().getName()
                    + ", name: " + cur.getName());
            e.printStackTrace();
        }

        if (cur == null || cur.isVisible()) {
            Iterator<Widget> iter = set.iterator();
            while (iter.hasNext()) {
                Widget wn = iter.next();
                drawTraverse(mx, my, wn, wn, top);
            }
        }
    }

    protected Widget gtnTraverse(double x, double y, Widget node, WidgetContainer set) {
        Widget res = null;
        boolean checkSub = node == null || node.transform.doesDraw;
        if (node != null && node.transform.doesDraw && node.transform.doesListenKey && node.isPointWithin(x, y)) {
            res = node;
        }

        if (!checkSub)
            return res;

        Widget next = null;
        for (Widget wn : set) {
            Widget tmp = gtnTraverse(x, y, wn, wn);
            if (tmp != null)
                next = tmp;
        }
        return next == null ? res : next;
    }

    @Override
    protected void onWidgetAdded(String name, Widget w) {
        w.gui = this;
        updateWidget(w);
    }

    public static void drawBlackout() {
        GL11.glMatrixMode(GL11.GL_PROJECTION);
        GL11.glDisable(GL11.GL_CULL_FACE);
        GL11.glDisable(GL11.GL_DEPTH_TEST);
        GL11.glPushMatrix();
        GL11.glLoadIdentity();
        GLU.gluOrtho2D(1, 0, 1, 0);

        GL11.glMatrixMode(GL11.GL_MODELVIEW);
        GL11.glPushMatrix();
        GL11.glLoadIdentity();

        GL11.glColor4d(0, 0, 0, 0.7);
        GL11.glTranslated(0, 0, 0);
        HudUtils.colorRect(0, 0, 1, 1);

        GL11.glPopMatrix();

        GL11.glMatrixMode(GL11.GL_PROJECTION);
        GL11.glPopMatrix();

        GL11.glEnable(GL11.GL_TEXTURE_2D);
        GL11.glColor4d(1, 1, 1, 1);

        GL11.glEnable(GL11.GL_DEPTH_TEST);
        GL11.glEnable(GL11.GL_CULL_FACE);
        GL11.glMatrixMode(GL11.GL_MODELVIEW);
    }

    /**
     * Event bus delegator, will post every widget inside this LIGui.
     */
    public void postEvent(GuiEvent event) {
        eventBus.postEvent(null, event);
        for (Widget w : getDrawList()) {
            hierPostEvent(w, event);
        }
    }

    public void regEventHandler(GuiEventHandler geh) {
        eventBus.regEventHandler(geh);
    }

    public void removeEventHandler(GuiEventHandler geh) {
        eventBus.remove(geh);
    }

    private void hierPostEvent(Widget w, GuiEvent event) {
        w.postEvent(event);
        for (Widget ww : w.widgetList) {
            hierPostEvent(ww, event);
        }
    }
}