/* * This file is part of SpoutcraftPlugin. * * Copyright (c) 2011 SpoutcraftDev <http://spoutcraft.org//> * SpoutcraftPlugin is licensed under the GNU Lesser General Public License. * * SpoutcraftPlugin is free software: you can redistribute it and/or modify * it under the terms of the GNU Lesser General Public License as published by * the Free Software Foundation, either version 3 of the License, or * (at your option) any later version. * * SpoutcraftPlugin 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 Lesser General Public License for more details. * * You should have received a copy of the GNU Lesser General Public License * along with this program. If not, see <http://www.gnu.org/licenses/>. */ package org.getspout.spoutapi.gui; import java.util.ArrayList; import java.util.Arrays; import java.util.Collections; import java.util.List; import org.bukkit.Bukkit; import org.bukkit.plugin.Plugin; public class GenericContainer extends GenericWidget implements Container { protected List<Widget> children = new ArrayList<Widget>(); protected ContainerType type = ContainerType.VERTICAL; protected WidgetAnchor align = WidgetAnchor.TOP_LEFT; protected boolean reverse = false; protected int minWidthCalc = 0, maxWidthCalc = 427, minHeightCalc = 0, maxHeightCalc = 240; protected boolean auto = true; protected boolean recalculating = false; protected boolean needsLayout = true; public GenericContainer() { } public GenericContainer(Widget... children) { // Shortcuts because we don't have any of the insertChild values setup yet this.children.addAll(Arrays.asList(children)); for (Widget child : children) { child.setContainer(this); } updateSize(); } @Override public Container addChild(Widget child) { return insertChild(-1, child); } @Override public Container insertChild(int index, Widget child) { if (index < 0 || index > this.children.size()) { this.children.add(child); } else { this.children.add(index, child); } child.setContainer(this); child.savePos(); child.shiftXPos(super.getX()); child.shiftYPos(super.getY()); child.setAnchor(super.getAnchor()); child.setVisible(super.isVisible()); if (getScreen() != null) { Plugin p = child.getPlugin(); getScreen().attachWidget(p == Bukkit.getServer().getPluginManager().getPlugin("Spout") ? getPlugin() : p, child); } updateSize(); deferLayout(); return this; } @Override public Container addChildren(Widget... children) { for (Widget child : children) { this.insertChild(-1, child); } return this; } @Override public Widget[] getChildren() { Widget[] list = new Widget[children.size()]; children.toArray(list); return list; } @Override public void setDirty(boolean dirty) { super.setDirty(dirty); for (Widget widget : children) { widget.setDirty(dirty); } } @Override public boolean isDirty() { return false; } @Override public Container setVisible(boolean enable) { if (enable != isVisible()) { super.setVisible(enable); for (Widget widget : children) { widget.setVisible(enable); } } return this; } @Override public Container setPriority(RenderPriority priority) { super.setPriority(priority); for (Widget widget : children) { widget.setPriority(priority); } return this; } @Override public Container setAnchor(WidgetAnchor anchor) { super.setAnchor(anchor); for (Widget widget : children) { widget.setAnchor(anchor); } return this; } @Override public WidgetType getType() { return WidgetType.Container; } @Override public Widget setX(int pos) { int delta = pos - super.getX(); super.setX(pos); for (Widget widget : children) { widget.shiftXPos(delta); } return this; } @Override public Widget setY(int pos) { int delta = pos - super.getY(); super.setY(pos); for (Widget widget : children) { widget.shiftYPos(delta); } return this; } @Override public Container removeChild(Widget child) { children.remove(child); child.setContainer(null); child.restorePos(); if (child.getScreen() != null) { child.getScreen().removeWidget(child); } updateSize(); deferLayout(); return this; } @Override public Container setScreen(Screen screen) { super.setScreen(getPlugin(), screen); for (Widget child : children) { if (screen != null) { screen.attachWidget(getPlugin(), child); } else if (child.getScreen() != null) { child.getScreen().removeWidget(child); } } return this; } @Override public Container setHeight(int height) { if (super.getHeight() != height) { super.setHeight(height); deferLayout(); } return this; } @Override public Container setWidth(int width) { if (super.getWidth() != width) { super.setWidth(width); deferLayout(); } return this; } @Override public Container setLayout(ContainerType type) { if (this.type != type) { this.type = type; deferLayout(); } return this; } @Override public ContainerType getLayout() { return type; } @Override public Container setAlign(WidgetAnchor align) { if (this.align != align) { this.align = align; deferLayout(); } return this; } @Override public WidgetAnchor getAlign() { return align; } @Override public Container setReverse(boolean reverse) { if (this.reverse != reverse) { this.reverse = reverse; deferLayout(); } return this; } @Override public boolean getReverse() { return reverse; } @Override public Container deferLayout() { needsLayout = true; return this; } @Override public Container updateLayout() { if (!recalculating && super.getWidth() > 0 && super.getHeight() > 0 && !children.isEmpty()) { recalculating = true; // Prevent us from getting into a loop List<Widget> visibleChildren = new ArrayList<Widget>(); int totalwidth = 0, totalheight = 0, newwidth, newheight, vcount = 0, hcount = 0; int availableWidth = auto ? getWidth() : getMinWidth(), availableHeight = auto ? getHeight() : getMinHeight(); // We only layout visible children, invisible ones have zero physical presence on screen for (Widget widget : children) { if (widget.isVisible()) { visibleChildren.add(widget); } } // Reverse drawing order if we need to if (reverse) { Collections.reverse(visibleChildren); } // First - get the total space by fixed widgets and borders if (type == ContainerType.OVERLAY) { newwidth = availableWidth; newheight = availableHeight; } else { for (Widget widget : visibleChildren) { int horiz = widget.getMarginLeft() + widget.getMarginRight(); int vert = widget.getMarginTop() + widget.getMarginBottom(); if (widget.isFixed()) { horiz += widget.getWidth(); vert += widget.getHeight(); } if (type == ContainerType.VERTICAL) { totalheight += vert; if (!widget.isFixed()) { vcount++; } } else if (type == ContainerType.HORIZONTAL) { totalwidth += horiz; if (!widget.isFixed()) { hcount++; } } } // Work out the width and height for children newwidth = (availableWidth - totalwidth) / Math.max(1, hcount); newheight = (availableHeight - totalheight) / Math.max(1, vcount); // Deal with minWidth and minHeight - change newwidth/newheight if needed for (Widget widget : visibleChildren) { if (!widget.isFixed()) { if (type == ContainerType.VERTICAL) { if (widget.getMinHeight() > newheight) { totalheight += widget.getMinHeight() - newheight; newheight = (availableHeight - totalheight) / Math.max(1, vcount); } else if (newheight >= widget.getMaxHeight()) { totalheight += widget.getMaxHeight(); vcount--; newheight = (availableHeight - totalheight) / Math.max(1, vcount); } } else if (type == ContainerType.HORIZONTAL) { if (widget.getMinWidth() > newwidth) { totalwidth += widget.getMinWidth() - newwidth; newwidth = (availableWidth - totalwidth) / Math.max(1, hcount); } else if (newwidth >= widget.getMaxWidth()) { totalwidth += widget.getMaxWidth(); hcount--; newwidth = (availableWidth - totalwidth) / Math.max(1, hcount); } } } } newheight = Math.max(newheight, 0); newwidth = Math.max(newwidth, 0); } totalheight = totalwidth = 0; // Resize any non-fixed widgets for (Widget widget : visibleChildren) { int vMargin = widget.getMarginTop() + widget.getMarginBottom(); int hMargin = widget.getMarginLeft() + widget.getMarginRight(); if (!widget.isFixed()) { if (auto) { widget.setHeight(Math.max(widget.getMinHeight(), Math.min(newheight - (this.type == ContainerType.VERTICAL ? 0 : vMargin), widget.getMaxHeight()))); widget.setWidth(Math.max(widget.getMinWidth(), Math.min(newwidth - (this.type == ContainerType.HORIZONTAL ? 0 : hMargin), widget.getMaxWidth()))); } else { widget.setHeight(widget.getMinHeight() == 0 ? newheight - vMargin : widget.getMinHeight()); widget.setWidth(widget.getMinWidth() == 0 ? newwidth - hMargin : widget.getMinWidth()); } } if (type == ContainerType.VERTICAL) { totalheight += widget.getHeight() + vMargin; } else { totalheight = Math.max(totalheight, widget.getHeight() + vMargin); } if (type == ContainerType.HORIZONTAL) { totalwidth += widget.getWidth() + hMargin; } else { totalwidth = Math.max(totalwidth, widget.getWidth() + hMargin); } } // Work out the new top-left position taking into account Align int left = super.getX(); int top = super.getY(); if (align == WidgetAnchor.TOP_CENTER || align == WidgetAnchor.CENTER_CENTER || align == WidgetAnchor.BOTTOM_CENTER) { left += (super.getWidth() - totalwidth) / 2; } else if (align == WidgetAnchor.TOP_RIGHT || align == WidgetAnchor.CENTER_RIGHT || align == WidgetAnchor.BOTTOM_RIGHT) { left += super.getWidth() - totalwidth; } if (align == WidgetAnchor.CENTER_LEFT || align == WidgetAnchor.CENTER_CENTER || align == WidgetAnchor.CENTER_RIGHT) { top += (super.getHeight() - totalheight) / 2; } else if (align == WidgetAnchor.BOTTOM_LEFT || align == WidgetAnchor.BOTTOM_CENTER || align == WidgetAnchor.BOTTOM_RIGHT) { top += super.getHeight() - totalheight; } // Move all children into the correct position for (Widget widget : visibleChildren) { int realtop = top + widget.getMarginTop(); int realleft = left + widget.getMarginLeft(); if (widget.getY() != realtop || widget.getX() != realleft) { widget.setY(realtop).setX(realleft); } if (type == ContainerType.VERTICAL) { top += widget.getHeight() + widget.getMarginTop() + widget.getMarginBottom(); } else if (type == ContainerType.HORIZONTAL) { left += widget.getWidth() + widget.getMarginLeft() + widget.getMarginRight(); } } recalculating = false; } needsLayout = false; return this; } @Override public void onTick() { if (needsLayout) { updateLayout(); } } @Override public int getMinWidth() { return Math.max(super.getMinWidth(), minWidthCalc); } @Override public int getMaxWidth() { return Math.min(super.getMaxWidth(), maxWidthCalc); } @Override public int getMinHeight() { return Math.max(super.getMinHeight(), minHeightCalc); } @Override public int getMaxHeight() { return Math.min(super.getMaxHeight(), maxHeightCalc); } @Override public Container updateSize() { if (!recalculating && !isFixed()) { recalculating = true; // Prevent us from getting into a loop due to both trickle down and push up int minwidth = 0, maxwidth = 0, minheight = 0, maxheight = 0, minhoriz, maxhoriz, minvert, maxvert; // Work out the minimum and maximum dimensions for the contents of this container for (Widget widget : children) { if (widget.isVisible()) { if (widget instanceof Container) { // Trickle down to children ((Container) widget).updateSize(); } minhoriz = maxhoriz = widget.getMarginLeft() + widget.getMarginRight(); minvert = maxvert = widget.getMarginTop() + widget.getMarginBottom(); if (widget.isFixed()) { minhoriz += widget.getWidth(); maxhoriz += widget.getWidth(); minvert += widget.getHeight(); maxvert += widget.getHeight(); } else { minhoriz += widget.getMinWidth(); maxhoriz += widget.getMaxWidth(); minvert += widget.getMinHeight(); maxvert += widget.getMaxHeight(); } if (type == ContainerType.HORIZONTAL) { minwidth += minhoriz; maxwidth += maxhoriz; } else { minwidth = Math.max(minwidth, minhoriz); if (type == ContainerType.OVERLAY) { maxwidth = Math.max(maxwidth, maxhoriz); } else { maxwidth = Math.min(maxwidth, maxhoriz); } } if (type == ContainerType.VERTICAL) { minheight += minvert; maxheight += maxvert; } else { minheight = Math.max(minheight, minvert); if (type == ContainerType.OVERLAY) { maxheight = Math.max(maxheight, maxvert); } else { maxheight = Math.min(maxheight, maxvert); } } } } minwidth = Math.min(minwidth, 427); maxwidth = Math.min(maxwidth == 0 ? 427 : maxwidth, 427); minheight = Math.min(minheight, 240); maxheight = Math.min(maxheight == 0 ? 240 : maxheight, 240); // Check if the dimensions have changed if (minwidth != minWidthCalc || maxwidth != maxWidthCalc || minheight != minHeightCalc || maxheight != maxHeightCalc) { minWidthCalc = minwidth; maxWidthCalc = maxwidth; minHeightCalc = minheight; maxHeightCalc = maxheight; deferLayout(); if (hasContainer()) { // Push up to parents getContainer().updateSize(); getContainer().deferLayout(); } } recalculating = false; } return this; } @Override public Container setAuto(boolean auto) { this.auto = auto; return this; } @Override public boolean isAuto() { return auto; } }