0
|
1 // Written in the D programming language
|
|
2 // www.digitalmars.com/d/
|
|
3
|
|
4 /*
|
|
5 * The contents of this file are subject to the Mozilla Public License Version
|
|
6 * 1.1 (the "License"); you may not use this file except in compliance with
|
|
7 * the License. You may obtain a copy of the License at
|
|
8 * http://www.mozilla.org/MPL/
|
|
9 *
|
|
10 * Software distributed under the License is distributed on an "AS IS" basis,
|
|
11 * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License
|
|
12 * for the specific language governing rights and limitations under the
|
|
13 * License.
|
|
14 *
|
|
15 * The Original Code is the Dynamin library.
|
|
16 *
|
|
17 * The Initial Developer of the Original Code is Jordan Miner.
|
|
18 * Portions created by the Initial Developer are Copyright (C) 2006-2009
|
|
19 * the Initial Developer. All Rights Reserved.
|
|
20 *
|
|
21 * Contributor(s):
|
|
22 * Jordan Miner <jminer7@gmail.com>
|
|
23 *
|
|
24 */
|
|
25
|
|
26 module dynamin.gui.container;
|
|
27
|
|
28 import dynamin.all_core;
|
|
29 import dynamin.all_painting;
|
|
30 import dynamin.all_gui;
|
|
31 import dynamin.gui.control;
|
|
32 import dynamin.gui.events;
|
|
33 import tango.io.Stdout;
|
|
34
|
|
35 alias List!(Control) ControlList;
|
|
36
|
|
37 ///
|
|
38 class Container : Control {
|
|
39 protected:
|
|
40 ControlList _children;
|
|
41 Size _minSize;
|
|
42 Size _maxSize;
|
|
43
|
|
44 override void whenResized(EventArgs e) {
|
|
45 layout();
|
|
46 }
|
|
47 public:
|
|
48 /// This event occurs after the control's minimum size has been changed.
|
|
49 Event!() minSizeChanged;
|
|
50 /// Override this method in a subclass to handle the minSizeChanged event.
|
|
51 protected void whenMinSizeChanged(EventArgs e) { }
|
|
52
|
|
53 /// This event occurs after the control's maximum size has been changed.
|
|
54 Event!() maxSizeChanged;
|
|
55 /// Override this method in a subclass to handle the maxSizeChanged event.
|
|
56 protected void whenMaxSizeChanged(EventArgs e) { }
|
|
57
|
|
58 this() {
|
|
59 minSizeChanged = new Event!()(&whenMinSizeChanged);
|
|
60 maxSizeChanged = new Event!()(&whenMaxSizeChanged);
|
|
61 _children = new ControlList();
|
|
62
|
|
63 elasticX = true;
|
|
64 elasticY = true;
|
|
65 }
|
|
66 override void dispatchPainting(PaintingEventArgs e) {
|
|
67 super.dispatchPainting(e);
|
|
68 foreach(c; _children) {
|
|
69 e.graphics.save();
|
|
70 e.graphics.translate(c.x, c.y);
|
|
71 c.setupGraphics(e.graphics);
|
|
72 c.painting(e);
|
|
73 e.graphics.restore();
|
|
74 }
|
|
75 }
|
|
76 // TODO: make these use common code...get rid of copy and paste
|
|
77 override void dispatchMouseDown(MouseEventArgs e) {
|
|
78 auto c = getChildAtPoint(e.location);
|
|
79 if(c && getCaptorControl() !is this) {
|
|
80 e.location = e.location - c.location;
|
|
81 c.mouseDown(e);
|
|
82 e.location = e.location + c.location;
|
|
83 } else {
|
|
84 super.dispatchMouseDown(e);
|
|
85 }
|
|
86 }
|
|
87 override void dispatchMouseUp(MouseEventArgs e) {
|
|
88 auto c = getChildAtPoint(e.location);
|
|
89 if(c && getCaptorControl() !is this) {
|
|
90 e.location = e.location - c.location;
|
|
91 c.mouseUp(e);
|
|
92 e.location = e.location + c.location;
|
|
93 } else {
|
|
94 super.dispatchMouseUp(e);
|
|
95 }
|
|
96 }
|
|
97 override void dispatchMouseMoved(MouseEventArgs e) {
|
|
98 auto c = getChildAtPoint(e.location);
|
|
99 if(c && getCaptorControl() !is this) {
|
|
100 e.location = e.location - c.location;
|
|
101 c.mouseMoved(e);
|
|
102 e.location = e.location + c.location;
|
|
103 } else {
|
|
104 super.dispatchMouseMoved(e);
|
|
105 }
|
|
106 }
|
|
107 override void dispatchMouseDragged(MouseEventArgs e) {
|
|
108 auto c = getChildAtPoint(e.location);
|
|
109 if(c && getCaptorControl() !is this) {
|
|
110 e.location = e.location - c.location;
|
|
111 c.mouseDragged(e);
|
|
112 e.location = e.location + c.location;
|
|
113 } else {
|
|
114 super.dispatchMouseDragged(e);
|
|
115 }
|
|
116 }
|
|
117 /**
|
|
118 * Gets the child control at the specified point. If there are
|
|
119 * multiple child controls at the point, the topmost control is returned.
|
|
120 * If there is no child control at the point, null is returned. The returned
|
|
121 * control, if any, is a direct child of this container.
|
|
122 */
|
|
123 Control getChildAtPoint(real[] pt) {
|
|
124 assert(pt.length == 2, "pt must be just an x and y");
|
|
125 return getChildAtPoint(Point(pt[0], pt[1]));
|
|
126 }
|
|
127 /// ditto
|
|
128 Control getChildAtPoint(real x, real y) {
|
|
129 return getChildAtPoint(Point(x, y));
|
|
130 }
|
|
131 /// ditto
|
|
132 Control getChildAtPoint(Point pt) {
|
|
133 for(int i = _children.count-1; i >= 0; --i) {
|
|
134 pt = pt - _children[i].location;
|
|
135 scope(exit) pt = pt + _children[i].location;
|
|
136 if(_children[i].contains(pt))
|
|
137 return _children[i];
|
|
138 }
|
|
139 return null;
|
|
140 }
|
|
141 /**
|
|
142 * Never returns null. If there is no descendant at the specified point,
|
|
143 * this container will be returned.
|
|
144 */
|
|
145 Control getDescendantAtPoint(real[] pt) {
|
|
146 assert(pt.length == 2, "pt must be just an x and y");
|
|
147 return getDescendantAtPoint(Point(pt[0], pt[1]));
|
|
148 }
|
|
149 /// ditto
|
|
150 Control getDescendantAtPoint(real x, real y) {
|
|
151 return getDescendantAtPoint(Point(x, y));
|
|
152 }
|
|
153 /// ditto
|
|
154 Control getDescendantAtPoint(Point pt) {
|
|
155 Container des = this;
|
|
156 while(true) {
|
|
157 auto child = des.getChildAtPoint(pt);
|
|
158 if(!child)
|
|
159 return des;
|
|
160 auto isContainer = cast(Container)child;
|
|
161 if(isContainer) {
|
|
162 des = isContainer;
|
|
163 pt = pt - des.location;
|
|
164 // loop around with this container
|
|
165 } else {
|
|
166 return child;
|
|
167 }
|
|
168 }
|
|
169 }
|
|
170 /**
|
|
171 * Gets or sets the minimum size of this window. A minimum width or
|
|
172 * height of 0 means that there is no minimum width or height.
|
|
173 * The default is Size(0, 0).
|
|
174 */
|
|
175 Size minSize() { return _minSize; }
|
|
176 /// ditto
|
|
177 void minSize(Size size) {
|
|
178 _minSize = size;
|
|
179 minSizeChanged(new EventArgs);
|
|
180 }
|
|
181 /// ditto
|
|
182 void minSize(real[] size) {
|
|
183 assert(size.length == 2, "size must be just a width and height");
|
|
184 minSize = Size(size[0], size[1]);
|
|
185 }
|
|
186 ///
|
|
187 real minWidth() { return _minSize.width; }
|
|
188 ///
|
|
189 real minHeight() { return _minSize.height; }
|
|
190 /**
|
|
191 * Gets or sets the maximum size of this window. A maximum width or
|
|
192 * height of 0 means that there is no maximum width or height.
|
|
193 * The default is Size(0, 0).
|
|
194 */
|
|
195 Size maxSize() { return _maxSize; }
|
|
196 /// ditto
|
|
197 void maxSize(Size size) {
|
|
198 _maxSize = size;
|
|
199 minSizeChanged(new EventArgs);
|
|
200 }
|
|
201 /// ditto
|
|
202 void maxSize(real[] size) {
|
|
203 assert(size.length == 2, "size must be just a width and height");
|
|
204 maxSize = Size(size[0], size[1]);
|
|
205 }
|
|
206 ///
|
|
207 real maxWidth() { return _maxSize.width; }
|
|
208 ///
|
|
209 real maxHeight() { return _maxSize.height; }
|
|
210 /**
|
|
211 * Causes this container to position its child controls. Called on every
|
|
212 * resize. Usually, this function will get each child's best size, and
|
|
213 * then set each child's location and height. The definition in Container
|
|
214 * is empty, as it is intended for subclasses to override.
|
|
215 */
|
|
216 void layout() {
|
|
217 }
|
|
218 protected void add(Control child) {
|
|
219 if(child.parent)
|
|
220 child.parent.remove(child);
|
|
221 _children.add(child);
|
|
222 child.parent = this;
|
|
223 repaint();
|
|
224 //ControlAdded(EventArgs e); // TODO: add event
|
|
225 }
|
|
226 protected void remove(Control child) {
|
|
227 _children.remove(child);
|
|
228 child.parent = null;
|
|
229 repaint();
|
|
230 //ControlRemoved(EventArgs e); // TODO: add event
|
|
231 }
|
|
232 protected int opApply(int delegate(inout Control item) dg) {
|
|
233 for(uint i = 0; i < _children.count; ++i) {
|
|
234 auto tmp = _children[i];
|
|
235 if(int result = dg(tmp))
|
|
236 return result;
|
|
237 }
|
|
238 return 0;
|
|
239 }
|
|
240 protected int opApply(int delegate(inout uint index, inout Control item) dg) {
|
|
241 for(uint i = 0; i < _children.count; ++i) {
|
|
242 auto tmp = _children[i];
|
|
243 if(int result = dg(i, tmp))
|
|
244 return result;
|
|
245 }
|
|
246 return 0;
|
|
247 }
|
|
248 }
|
|
249
|
|
250 // TODO: calling panel.children.add(button) will cause a crash
|
|
251 // because the button's parent is not set to the panel
|
|
252 // need to add a change handler on children?
|
|
253 class Panel : Container {
|
|
254 ControlList children() { return _children; }
|
|
255 void add(Control child) { super.add(child); };
|
|
256 void remove(Control child) { super.remove(child); };
|
|
257 int opApply(int delegate(inout Control item) dg) {
|
|
258 return super.opApply(dg);
|
|
259 }
|
|
260 int opApply(int delegate(inout uint index, inout Control item) dg) {
|
|
261 return super.opApply(dg);
|
|
262 }
|
|
263 // override protected void whenPainting() {
|
|
264 // }
|
|
265 }
|
|
266
|