1 module pixelperfectengine.concrete.elements.panel;
2 
3 import pixelperfectengine.concrete.elements.base;
4 
5 import pixelperfectengine.system.etc : clamp, cmpObjPtr;
6 
7 import collections.linkedlist;
8 
9 
10 /**
11  * Panel for grouping elements.
12  * Does not directly handle elements, instead relies on blitter transparency. However, can handle
13  * the state of the elements.
14  */
15 public class Panel : WindowElement, ElementContainer {
16 	alias WESet = LinkedList!(WindowElement, false, "a is b");
17 	protected WESet subElems;			///Contains all elements within the panel
18 	protected sizediff_t focusedElem;				///Index of the currently focused element
19 	/**
20 	 * Default CTOR
21 	 */
22 	public this(Text text, string source, Coordinate coordinates) {
23 		position = coordinates;
24 		this.text = text;
25 		this.source = source;
26 	}
27 	///Ditto
28 	public this(dstring text, string source, Coordinate position) {
29 		this(new Text(text, getStyleSheet().getChrFormatting("panel")), source, position);
30 	}
31 	public override ElementState state(ElementState state) @property {
32 		ElementState subst = state == ElementState.Enabled ? ElementState.Enabled : ElementState.DisabledWOGray;
33 		foreach (WindowElement key; subElems) {
34 			key.state = subst;
35 		}
36 		return super.state(state);
37 	}
38 	public override void draw() {
39 		foreach (key; subElems) {
40 			key.draw;
41 		}
42 		StyleSheet ss = getStyleSheet();
43 		const int textLength = text.getWidth();
44 		Box offsetEdge = position;// - (text.font.size / 2);
45 		offsetEdge.top = offsetEdge.top + (text.font.size / 2);
46 		const Box textPos = Box(offsetEdge.left + (text.font.size / 2), position.top,
47 				offsetEdge.left + textLength + (text.font.size / 2), position.top + text.font.size);
48 		with (parent) {
49 			drawLine(offsetEdge.cornerUL, Point(offsetEdge.left + (text.font.size / 2), offsetEdge.top), 
50 					ss.getColor("windowascent"));
51 			drawLine(Point(offsetEdge.left + textLength + (text.font.size / 2), offsetEdge.top), offsetEdge.cornerUR, 
52 					ss.getColor("windowascent"));
53 			drawLine(offsetEdge.cornerUL, offsetEdge.cornerLL, ss.getColor("windowascent"));
54 			drawLine(offsetEdge.cornerLL, offsetEdge.cornerLR, ss.getColor("windowascent"));
55 			drawLine(offsetEdge.cornerUR, offsetEdge.cornerLR, ss.getColor("windowascent"));
56 			drawTextSL(textPos, text, Point(0, 0));
57 		}
58 		if (super.state == ElementState.Disabled) {
59 			parent.bitBLTPattern(position, ss.getImage("ElementDisabledPtrn"));
60 		}
61 		if (onDraw !is null) {
62 			onDraw();
63 		}
64 	}
65 	/**
66 	 * Adds an element to the panel
67 	 */
68 	public void addElement(WindowElement we) {
69 		we.setParent(this);
70 		subElems.put(we);
71 		we.draw();
72 	}
73 
74 	public Coordinate getAbsolutePosition(WindowElement sender) {
75 		return parent.getAbsolutePosition(sender); // TODO: implement
76 	}
77 	
78 	/**
79 	 * Gives focus to the element if applicable
80 	 */
81 	public void requestFocus(WindowElement sender) {
82 		subElems[focusedElem].focusTaken();
83 		focusedElem = subElems.which(sender);
84 		subElems[focusedElem].focusGiven();
85 	}
86 	///Called when an object receives focus.
87 	public override void focusGiven() {
88 		if(subElems.length) subElems[focusedElem].focusGiven();
89 		flags |= IS_FOCUSED;
90 		draw;
91 	}
92 	///Called when an object loses focus.
93 	public override void focusTaken() {
94 		subElems[focusedElem].focusTaken();
95 		flags &= ~IS_FOCUSED;
96 		draw;
97 	}
98 	///Cycles the focus on a single element.
99 	///Returns -1 if end is reached, or the number of remaining elements that
100 	///are cycleable in the direction.
101 	public override int cycleFocus(int direction) {
102 		if (focusedElem + direction < 0 || focusedElem + direction >= subElems.length) {
103 			return -1;
104 		} else {
105 			if (subElems.length) {
106 				const int result = subElems[focusedElem].cycleFocus(direction);
107 				if (result == -1) {
108 					subElems[focusedElem].focusTaken();
109 					focusedElem += direction;
110 					subElems[focusedElem].focusGiven();
111 				}
112 			}
113 			return direction > 1 ? cast(int)(subElems.length - focusedElem) : cast(int)focusedElem;
114 		}
115 	}
116 	
117 	public void drawLine(Point from, Point to, ubyte color) @trusted {
118 		parent.drawLine(from, to, color);
119 	}
120 	
121 	public void drawLinePattern(Point from, Point to, ubyte[] pattern) @trusted {
122 		parent.drawLinePattern(from, to, pattern);
123 	}
124 	
125 	public void drawBox(Coordinate target, ubyte color) @trusted {
126 		parent.drawBox(target, color);
127 	}
128 	
129 	public void drawBoxPattern(Coordinate target, ubyte[] pattern) @trusted {
130 		parent.drawBoxPattern(target, pattern);
131 	}
132 	
133 	public void drawFilledBox(Coordinate target, ubyte color) @trusted {
134 		parent.drawFilledBox(target, color);
135 	}
136 	
137 	public void bitBLT(Point target, ABitmap source) @trusted {
138 		parent.bitBLT(target, source);
139 	}
140 	
141 	public void bitBLT(Point target, ABitmap source, Coordinate slice) @trusted {
142 		parent.bitBLT(target, source, slice);
143 	}
144 	
145 	public void bitBLTPattern(Coordinate target, ABitmap pattern) @trusted {
146 		parent.bitBLTPattern(target, pattern);
147 	}
148 	
149 	public void xorBitBLT(Coordinate target, ABitmap pattern) @trusted {
150 		parent.xorBitBLT(target, pattern);
151 	}
152 	
153 	public void xorBitBLT(Coordinate target, ubyte color) @trusted {
154 		parent.xorBitBLT(target, color);
155 	}
156 	
157 	public void fill(Point target, ubyte color, ubyte background = 0) @trusted {
158 		parent.fill(target, color, background);
159 	}
160 	
161 	public void drawTextSL(Coordinate target, Text text, Point offset) @trusted {
162 		parent.drawTextSL(target, text, offset);
163 	}
164 	
165 	public void drawTextML(Coordinate target, Text text, Point offset) @trusted {
166 		parent.drawTextML(target, text, offset);
167 	}
168 	
169 	public void clearArea(Coordinate target) @trusted {
170 		parent.clearArea(target);
171 	}
172 	/**
173 	 * Sets the cursor to the given type on request.
174 	 */
175 	public void requestCursor(CursorType type) {
176 		parent.requestCursor(type);
177 	}
178 	/**
179 	 * Puts a PopUpElement on the GUI.
180 	 */
181 	public void addPopUpElement(PopUpElement p) {
182 		parent.addPopUpElement(p);
183 	}
184 	/**
185 	 * Puts a PopUpElement on the GUI at the given position.
186 	 */
187 	public void addPopUpElement(PopUpElement p, int x, int y) {
188 		parent.addPopUpElement(p, x, y);
189 	}
190 	/** 
191 	 * Ends the popup session and closes all popups.
192 	 */
193 	public void endPopUpSession(PopUpElement p) {
194 		parent.endPopUpSession(p);
195 	}
196 	/**
197 	 * Closes a single popup element.
198 	 */
199 	public void closePopUp(PopUpElement p) {
200 		parent.closePopUp(p);
201 	}
202 
203 	public override void passMCE(MouseEventCommons mec, MouseClickEvent mce) {
204 		foreach (WindowElement we; subElems) {
205 			if (we.getPosition.isBetween(mce.x, mce.y)) {
206 				we.passMCE(mec, mce);
207 				focusedElem = subElems.which(we);
208 				break;
209 			}
210 		}
211 		super.passMCE(mec, mce);
212 	}
213 	public override void passMME(MouseEventCommons mec, MouseMotionEvent mme) {
214 		if (focusedElem != -1) {
215 			subElems[focusedElem].passMME(mec, mme);
216 		}
217 		super.passMME(mec, mme);
218 	}
219 	public override void passMWE(MouseEventCommons mec, MouseWheelEvent mwe) {
220 		foreach (WindowElement we; subElems) {
221 			if (we.getPosition.isBetween(lastMousePosition.x, lastMousePosition.y)) {
222 				we.passMWE(mec, mwe);
223 				break;
224 			}
225 		}
226 		super.passMWE(mec, mwe);
227 	}
228 }