1/*******************************************************************************
2 * Copyright 2011 See AUTHORS file.
3 *
4 * Licensed under the Apache License, Version 2.0 (the "License");
5 * you may not use this file except in compliance with the License.
6 * You may obtain a copy of the License at
7 *
8 *   http://www.apache.org/licenses/LICENSE-2.0
9 *
10 * Unless required by applicable law or agreed to in writing, software
11 * distributed under the License is distributed on an "AS IS" BASIS,
12 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 * See the License for the specific language governing permissions and
14 * limitations under the License.
15 ******************************************************************************/
16
17package com.badlogic.gdx.scenes.scene2d.ui;
18
19import com.badlogic.gdx.graphics.g2d.Batch;
20import com.badlogic.gdx.scenes.scene2d.Actor;
21import com.badlogic.gdx.scenes.scene2d.Group;
22import com.badlogic.gdx.scenes.scene2d.Stage;
23import com.badlogic.gdx.scenes.scene2d.Touchable;
24import com.badlogic.gdx.scenes.scene2d.utils.Layout;
25import com.badlogic.gdx.utils.SnapshotArray;
26
27/** A {@link Group} that participates in layout and provides a minimum, preferred, and maximum size.
28 * <p>
29 * The default preferred size of a widget group is 0 and this is almost always overridden by a subclass. The default minimum size
30 * returns the preferred size, so a subclass may choose to return 0 for minimum size if it wants to allow itself to be sized
31 * smaller than the preferred size. The default maximum size is 0, which means no maximum size.
32 * <p>
33 * See {@link Layout} for details on how a widget group should participate in layout. A widget group's mutator methods should call
34 * {@link #invalidate()} or {@link #invalidateHierarchy()} as needed. By default, invalidateHierarchy is called when child widgets
35 * are added and removed.
36 * @author Nathan Sweet */
37public class WidgetGroup extends Group implements Layout {
38	private boolean needsLayout = true;
39	private boolean fillParent;
40	private boolean layoutEnabled = true;
41
42	public WidgetGroup () {
43	}
44
45	/** Creates a new widget group containing the specified actors. */
46	public WidgetGroup (Actor... actors) {
47		for (Actor actor : actors)
48			addActor(actor);
49	}
50
51	public float getMinWidth () {
52		return getPrefWidth();
53	}
54
55	public float getMinHeight () {
56		return getPrefHeight();
57	}
58
59	public float getPrefWidth () {
60		return 0;
61	}
62
63	public float getPrefHeight () {
64		return 0;
65	}
66
67	public float getMaxWidth () {
68		return 0;
69	}
70
71	public float getMaxHeight () {
72		return 0;
73	}
74
75	public void setLayoutEnabled (boolean enabled) {
76		if (layoutEnabled == enabled) return;
77		layoutEnabled = enabled;
78		setLayoutEnabled(this, enabled);
79	}
80
81	private void setLayoutEnabled (Group parent, boolean enabled) {
82		SnapshotArray<Actor> children = parent.getChildren();
83		for (int i = 0, n = children.size; i < n; i++) {
84			Actor actor = children.get(i);
85			if (actor instanceof Layout)
86				((Layout)actor).setLayoutEnabled(enabled);
87			else if (actor instanceof Group) //
88				setLayoutEnabled((Group)actor, enabled);
89		}
90	}
91
92	public void validate () {
93		if (!layoutEnabled) return;
94
95		Group parent = getParent();
96		if (fillParent && parent != null) {
97			float parentWidth, parentHeight;
98			Stage stage = getStage();
99			if (stage != null && parent == stage.getRoot()) {
100				parentWidth = stage.getWidth();
101				parentHeight = stage.getHeight();
102			} else {
103				parentWidth = parent.getWidth();
104				parentHeight = parent.getHeight();
105			}
106			if (getWidth() != parentWidth || getHeight() != parentHeight) {
107				setWidth(parentWidth);
108				setHeight(parentHeight);
109				invalidate();
110			}
111		}
112
113		if (!needsLayout) return;
114		needsLayout = false;
115		layout();
116	}
117
118	/** Returns true if the widget's layout has been {@link #invalidate() invalidated}. */
119	public boolean needsLayout () {
120		return needsLayout;
121	}
122
123	public void invalidate () {
124		needsLayout = true;
125	}
126
127	public void invalidateHierarchy () {
128		invalidate();
129		Group parent = getParent();
130		if (parent instanceof Layout) ((Layout)parent).invalidateHierarchy();
131	}
132
133	protected void childrenChanged () {
134		invalidateHierarchy();
135	}
136
137	protected void sizeChanged () {
138		invalidate();
139	}
140
141	public void pack () {
142		setSize(getPrefWidth(), getPrefHeight());
143		validate();
144		// Some situations require another layout. Eg, a wrapped label doesn't know its pref height until it knows its width, so it
145		// calls invalidateHierarchy() in layout() if its pref height has changed.
146		if (needsLayout) {
147			setSize(getPrefWidth(), getPrefHeight());
148			validate();
149		}
150	}
151
152	public void setFillParent (boolean fillParent) {
153		this.fillParent = fillParent;
154	}
155
156	public void layout () {
157	}
158
159	/** If this method is overridden, the super method or {@link #validate()} should be called to ensure the widget group is laid
160	 * out. */
161	public void draw (Batch batch, float parentAlpha) {
162		validate();
163		super.draw(batch, parentAlpha);
164	}
165}
166