1 module pixelperfectengine.concrete.popup.popupmenu;
2 
3 import pixelperfectengine.concrete.popup.base;
4 
5 /**
6  * To create drop-down lists, menu bars, etc.
7  */
8 public class PopUpMenu : PopUpElement {
9 	protected int width, height, select;
10 	PopUpMenuElement[] elements;
11 	public EventDeleg onMenuSelect;
12 	/**
13 	 * Creates a single PopUpMenu.
14 	 * Params:
15 	 *   elements = entries for the menu.
16 	 *   source = source identifier.
17 	 *   onMenuSelect = event delegate called on item selection.
18 	 */
19 	public this(PopUpMenuElement[] elements, string source, EventDeleg onMenuSelect){
20 		this.elements = elements;
21 		this.source = source;
22 		//this. iconWidth = iconWidth;
23 		select = -1;
24 		this.onMenuSelect = onMenuSelect;
25 	}
26 	public override void draw(){
27 		StyleSheet ss = getStyleSheet();
28 		if(output is null){
29 			foreach(e; elements){
30 				int newwidth;// = e.text.getWidth();
31 				if(e.text !is null) newwidth = e.text.getWidth();
32 				if(e.secondaryText !is null) newwidth += e.secondaryText.getWidth() + ss.drawParameters["PopUpMenuMinTextSpace"];
33 				if(newwidth > width){
34 					width = newwidth;
35 				}
36 				if(e.text !is null) height += e.text.getHeight + (ss.drawParameters["PopUpMenuVertPadding"] * 2);
37 				else height += ss.drawParameters["PopUpMenuSeparatorSize"];
38 			}
39 			width += (ss.drawParameters["PopUpMenuHorizPadding"] * 2);
40 			height += ss.drawParameters["PopUpMenuVertPadding"] * 2;
41 			position = Box(0, 0, width - 1, height - 1);
42 			output = new BitmapDrawer(width, height);
43 		}
44 		Box position0 = Box(0, 0, width - 1, height - 1);
45 		output.drawFilledBox(position0, ss.getColor("window"));//output.drawFilledRectangle(0,width - 1,0,height - 1,ss.getColor("window"));
46 
47 		/* if(select > -1){
48 			int y0 = cast(int)((height / elements.length) * select);
49 			int y1 = cast(int)((height / elements.length) + y0);
50 			output.drawFilledBox(Box(1, y0 + 1, position0.width - 1, y1 - 1), ss.getColor("selection")); //output.drawFilledRectangle(1, width - 1, y0 + 1, y1 + 1, ss.getColor("selection"));
51 		} */
52 
53 
54 		int y = 1 + ss.drawParameters["PopUpMenuVertPadding"];
55 		foreach(size_t i, PopUpMenuElement e; elements){
56 			if (e.text !is null) {	//Draw normal menuelement
57 						
58 				const Box textPos = Box(ss.drawParameters["PopUpMenuHorizPadding"], y,
59 						position0.width - ss.drawParameters["PopUpMenuHorizPadding"], y + e.text.getHeight());
60 				if (select == i) {
61 					output.drawFilledBox(Box(textPos.left, textPos.top + 1, textPos.right, textPos.bottom - 1), 
62 							ss.getColor("selection"));
63 				}
64 				output.drawSingleLineText(textPos, e.text);
65 				if (e.secondaryText !is null) {
66 				
67 					const Box textPos0 = Box(ss.drawParameters["PopUpMenuHorizPadding"], y,
68 							position0.width - ss.drawParameters["PopUpMenuHorizPadding"], y + e.secondaryText.getHeight());
69 					output.drawSingleLineText(textPos0, e.secondaryText);
70 				}
71 				y += e.text.getHeight() + (ss.drawParameters["PopUpMenuVertPadding"] * 2);
72 			} else {				//Draw separator
73 				const int linePoint = ss.drawParameters["PopUpMenuSeparatorSize"] / 2 + 
74 						(ss.drawParameters["PopUpMenuSeparatorSize"] & 1);
75 				output.drawLine(Point(ss.drawParameters["PopUpMenuHorizPadding"], y + linePoint), 
76 						Point(position.width - ss.drawParameters["PopUpMenuHorizPadding"] - 1, y + linePoint),
77 						ss.getColor("windowinactive"));
78 				y += ss.drawParameters["PopUpMenuSeparatorSize"];
79 			}
80 		}
81 
82 		//output.drawRectangle(1,1,height-1,width-1,ss.getColor("windowascent"));
83 		/+output.drawLine(0,0,0,height-1,ss.getColor("windowascent"));
84 		output.drawLine(0,width-1,0,0,ss.getColor("windowascent"));
85 		output.drawLine(0,width-1,height-1,height-1,ss.getColor("windowdescent"));
86 		output.drawLine(width-1,width-1,0,height-1,ss.getColor("windowdescent"));+/
87 		with (output) {
88 			drawLine(position0.cornerUL, position0.cornerUR, ss.getColor("windowascent"));
89 			drawLine(position0.cornerUL, position0.cornerLL, ss.getColor("windowascent"));
90 			drawLine(position0.cornerLL, position0.cornerLR, ss.getColor("windowdescent"));
91 			drawLine(position0.cornerUR, position0.cornerLR, ss.getColor("windowdescent"));
92 		}
93 		if(onDraw !is null){
94 			onDraw();
95 		}
96 	}
97 	public override void passMCE(MouseEventCommons mec, MouseClickEvent mce) {
98 		if (mce.state) {
99 			mce.y -= position.top;
100 			/* mce.y /= height / elements.length;
101 			mce.y = mce.y >= elements.length ? cast(int)elements.length - 1 : mce.y; */
102 			int num, vPos = 1 + getStyleSheet().drawParameters["PopUpMenuVertPadding"];
103 			foreach (PopUpMenuElement key; elements) {
104 				if (key.text !is null) {
105 					vPos += key.text.getHeight() + getStyleSheet.drawParameters["PopUpMenuVertPadding"] * 2;
106 				} else {
107 					vPos += getStyleSheet.drawParameters["PopUpMenuSeparatorSize"];
108 				}
109 				if (vPos > mce.y) {
110 					break;
111 				} else {
112 					num++;
113 				}
114 			}
115 			if (num >= elements.length) return;
116 			if (elements[num].source == "\\submenu\\") {
117 				PopUpMenu m = new PopUpMenu(elements[mce.y].subElements, this.source, onMenuSelect);
118 				m.onMouseClick = onMouseClick;
119 				//parent.getAbsolutePosition()
120 				parent.addPopUpElement(m, position.left + width, position.top + vPos - text.getHeight);
121 				//parent.closePopUp(this);
122 			} else if (elements[num].text !is null) {
123 				//invokeActionEvent(new Event(elements[offsetY].source, source, null, null, null, offsetY, EventType.CLICK));
124 				if(onMenuSelect !is null)
125 					onMenuSelect(new MenuEvent(this, SourceType.PopUpElement, elements[num].text, num, elements[num].source));
126 				parent.endPopUpSession(this);
127 				//parent.closePopUp(this);
128 			}
129 		}
130 
131 	}
132 	public override void passMME(MouseEventCommons mec, MouseMotionEvent mme) {
133 		if (!position.isBetween(mme.x, mme.y)) {
134 			if(select != -1){
135 				select = -1;
136 			}
137 		} else {
138 			int num, vPos = 1 + getStyleSheet().drawParameters["PopUpMenuVertPadding"];
139 			mme.y -= position.top;
140 			foreach (PopUpMenuElement key; elements) {
141 				if (key.text !is null) {
142 					vPos += key.text.getHeight() + getStyleSheet.drawParameters["PopUpMenuVertPadding"] * 2;
143 				} else {
144 					vPos += getStyleSheet.drawParameters["PopUpMenuSeparatorSize"];
145 				}
146 				if (vPos > mme.y) { 
147 					select = num;
148 					break;
149 				}
150 				num++;
151 				
152 			}
153 		}
154 		draw();
155 	}
156 
157 }
158 /**
159 * Defines a single MenuElement, also can contain multiple subelements.
160 */
161 public class PopUpMenuElement {
162 	public string source;					///Source identifier.
163 	public Text text, secondaryText;		///Primary and secondary display texts
164 	//protected Bitmap8Bit icon;
165 	private PopUpMenuElement[] subElements;	///Any child element the menu may have
166 	//private ushort keymod;
167 	//private int keycode;
168 	//public int iconWidth;
169 
170 	/**
171 	 * Generates a menu element with the supplied parameters.
172 	 * Uses the default formatting to initialize texts.
173 	 * Params:
174 	 *   source = source identifier.
175 	 *   text = primary text, forced to be left justified.
176 	 *   secondaryText = secondary text, forced to be right justified.
177 	 *   subElements = any child elements for further submenus.
178 	 */
179 	public this(string source, dstring text, dstring secondaryText = "", PopUpMenuElement[] subElements = null) {
180 		StyleSheet ss = globalDefaultStyle;
181 		Text st;
182 		if (secondaryText.length) {
183 			st = new Text(secondaryText, ss.getChrFormatting("popUpMenuSecondary"));
184 		}
185 		this(source, new Text(text, ss.getChrFormatting("popUpMenu")), st, subElements);
186 	}
187 	/**
188 	 * Generates a menu element with the supplied parameters.
189 	 * Text formatting can be supplied with the objects.
190 	 * Params:
191 	 *   source = source identifier.
192 	 *   text = primary text, forced to be left justified.
193 	 *   secondaryText = secondary text, forced to be right justified.
194 	 *   subElements = any child elements for further submenus.
195 	 */
196 	public this(string source, Text text, Text secondaryText = null, PopUpMenuElement[] subElements = []) {
197 		this.source = source;
198 		this.text = text;
199 		this.secondaryText = secondaryText;
200 		this.subElements = subElements;
201 	}
202 	///Creates an empty separator element.
203 	public static PopUpMenuElement createSeparator() {
204 		Text nulltext = null;
205 		return new PopUpMenuElement("\\separator\\", nulltext, nulltext);
206 	}
207 	///DEPRECATED!
208 	///REMOVE BY VER 0.11!
209 	public deprecated Bitmap8Bit getIcon(){
210 		return text.icon;
211 	}
212 	///DEPRECATED!
213 	///REMOVE BY VER 0.11!
214 	public deprecated void setIcon(Bitmap8Bit icon){
215 		text.icon = icon;
216 	}
217 	///Returns all subelements of this menu element.
218 	public PopUpMenuElement[] getSubElements() {
219 		return subElements;
220 	}
221 	/**
222 	 * Assigns this current object's all subelements at once.
223 	 */
224 	public void loadSubElements(PopUpMenuElement[] e){
225 		subElements = e;
226 	}
227 	/**
228 	 * Gets the subelement at the given index.
229 	 */
230 	public PopUpMenuElement opIndex(size_t i){
231 		return subElements[i];
232 	}
233 	/**
234 	 * Sets the subelement at the given index.
235 	 */
236 	public PopUpMenuElement opIndexAssign(PopUpMenuElement value, size_t i){
237 		subElements[i] = value;
238 		return value;
239 	}
240 	/**
241 	 * Implements appending to the last position of the underlying array.
242 	 */
243 	public PopUpMenuElement opOpAssign(string op)(PopUpMenuElement value){
244 		static if(op == "~"){
245 			subElements ~= value;
246 			return value;
247 		}else static assert("Operator " ~ op ~ " not supported!");
248 	}
249 	///Returns the lenght of the underlying array.
250 	public size_t getLength(){
251 		return subElements.length;
252 	}
253 	///Sets the lenght of the underlying array.
254 	public void setLength(int l){
255 		subElements.length = l;
256 	}
257 
258 }