Java tutorial
/** * 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); } } }