PageRenderTime 50ms CodeModel.GetById 9ms app.highlight 36ms RepoModel.GetById 1ms app.codeStats 0ms

/cui/window.d

http://github.com/wilkie/djehuty
D | 761 lines | 569 code | 135 blank | 57 comment | 138 complexity | f1516aa135c2e74fe817d33670045d6e MD5 | raw file
  1module cui.window;
  2
  3import cui.application;
  4import cui.widget;
  5import cui.container;
  6import cui.dialog;
  7
  8import djehuty;
  9
 10import resource.menu;
 11
 12import io.console;
 13
 14// Description: This class abstacts the console window and allows for high level console operations which are abstracted away as controls.  It is the Window class for the console world.
 15class CuiWindow : Responder {
 16	// Constructor
 17
 18	this() {
 19		this("CuiWindow");
 20	}
 21
 22	this(Color bgClr) {
 23		this(bgClr, "CuiWindow");
 24	}
 25
 26	this(Color bgClr, string name) {
 27		Console.clipRect(0,0,this.width, this.height);
 28		_bgClr = bgClr;
 29		_controlContainer = new CuiContainer(0, 0, this.width, this.height);
 30		_controlContainer.text = name.dup;
 31		_controlContainer._window = this;
 32		push(_controlContainer);
 33	}
 34
 35	this(string name) {
 36		Console.clipRect(0,0,this.width, this.height);
 37		_controlContainer = new CuiContainer(0, 0, this.width, this.height);
 38		_controlContainer.text = name.dup;
 39		_controlContainer._window = this;
 40		push(_controlContainer);
 41	}
 42
 43	// Events
 44
 45	void onInitialize() {
 46		// go through control list, init
 47
 48		Console.backcolor = _bgClr;
 49		Console.clear();
 50		Console.clipRect(0,0,this.width, this.height);
 51
 52		_controlContainer.onInit();
 53
 54		_inited = true;
 55		redraw();
 56
 57		_controlContainer.onGotFocus();
 58	}
 59
 60	void onUninitialize() {
 61	}
 62
 63	void onResize() {
 64	}
 65
 66	void redraw() {
 67		if (_inited == false) { return; }
 68
 69		Console.hideCaret();
 70
 71		Console.clipClear();
 72		_controlContainer.onDraw();
 73
 74		_drawMenu();
 75		Console.clipClear();
 76	}
 77
 78	void onKeyDown(Key key) {
 79		if (_focusedMenu !is null) {
 80			if (_focusedMenu == _selectedMenu) {
 81				// on the menu bar
 82
 83				if (key.code == Key.Down) {
 84					// expand menu
 85					if (_focusedMenu.length > 0) {
 86						_selectMenu(_focusedMenu);
 87					}
 88				}
 89				else if (key.code == Key.Up) {
 90					// leave menu
 91					_focusedMenu = null;
 92					_selectedMenu = null;
 93					_drawMenu();
 94
 95					// Focus on the current widget
 96					_controlContainer.onGotFocus();
 97				}
 98				else if (key.code == Key.Left) {
 99					if (_focusedMenuIndex != 0) {
100						_focusedMenuIndex--;
101						_selectedMenu = _menu[_focusedMenuIndex];
102						_focusedMenu = _selectedMenu;
103						_drawMenu();
104					}
105					else {
106						// leave menu
107						_focusedMenu = null;
108						_selectedMenu = null;
109						_drawMenu();
110
111						// Focus on the current widget
112						_controlContainer.onGotFocus();
113					}
114				}
115				else if (key.code == Key.Right) {
116					if ((_focusedMenuIndex + 1) < _menu.length) {
117						_focusedMenuIndex++;
118						_selectedMenu = _menu[_focusedMenuIndex];
119						_focusedMenu = _selectedMenu;
120						_drawMenu();
121					}
122				}
123				else if (key.code == Key.Return || key.code == Key.Space) {
124					// apply
125					if (_selectedMenu.length > 0) {
126						// it is a submenu
127						_selectMenu(_selectedMenu);
128					}
129					else {
130						// select the item
131						_cancelNextChar = true;
132						_focusedMenu = null;
133						Menu selected = _selectedMenu;
134						_selectedMenu = null;
135						_drawMenu();
136
137						onMenu(selected);
138
139						// Focus on the current widget
140						_controlContainer.onGotFocus();
141					}
142				}
143				else if (key.code >= Key.A || key.code <= Key.Z) {
144					// check for hint
145					char keychr = cast(char)(key.code - Key.A) + 'a';
146					foreach(mnu; _menu) {
147						if (mnu.hint == keychr) {
148							// Select this menu
149							_selectedMenu = mnu;
150
151							if (_selectedMenu.length > 0) {
152								// it is a submenu
153								_selectMenu(_selectedMenu);
154							}
155							else {
156								// select the item
157								_cancelNextChar = true;
158								_focusedMenu = null;
159								Menu selected = _selectedMenu;
160								_selectedMenu = null;
161								_drawMenu();
162		
163								onMenu(selected);
164		
165								// Focus on the current widget
166								_controlContainer.onGotFocus();
167							}
168
169							break;
170						}
171					}
172				}
173			}
174			else {
175				// within a submenu
176
177				if (key.code == Key.Down) {
178					size_t tmpIndex = _selectedMenuIndex;
179					tmpIndex++;
180					while (tmpIndex < _focusedMenu.length) {
181						if (_focusedMenu[tmpIndex].displayText().trim() != "") {
182							_selectedMenuIndex = tmpIndex;
183							_selectedMenu = _focusedMenu[_selectedMenuIndex];
184							_drawSubmenu();
185							return;
186						}
187						tmpIndex++;
188					}
189				}
190				else if (key.code == Key.Up) {
191					size_t tmpIndex = _selectedMenuIndex;
192					while (tmpIndex > 0) {
193						tmpIndex--;
194						if (_focusedMenu[tmpIndex].displayText().trim() != "") {
195							_selectedMenuIndex = tmpIndex;
196							_selectedMenu = _focusedMenu[_selectedMenuIndex];
197							_drawSubmenu();
198							return;
199						}
200					}
201					
202					if (_selectedMenuIndex > 0) {
203						_selectedMenuIndex--;
204						_selectedMenu = _focusedMenu[_selectedMenuIndex];
205						_drawSubmenu();
206					}
207				}
208				else if (key.code == Key.Left) {
209					// remove submenu
210					if (_selectedMenu is null) {
211					}
212					else {
213						_removeMenuContext();
214					}
215				}
216				else if (key.code == Key.Right) {
217					// add submenu
218					if (_selectedMenu.length > 0) {
219						_addSubmenu();
220						_drawSubmenu();
221					}
222				}
223				else if (key.code == Key.Return || key.code == Key.Space) {
224					// apply
225					if (_selectedMenu.length > 0) {
226						// it is a submenu
227						_addSubmenu();
228						_drawSubmenu();
229					}
230					else {
231						// select the item
232						_cancelNextChar = true;
233						_focusedMenu = null;
234						Menu selected = _selectedMenu;
235						_selectedMenu = null;
236						redraw();
237
238						onMenu(selected);
239
240						// Focus on the current widget
241						_controlContainer.onGotFocus();
242					}
243				}
244				else if (key.code >= Key.A && key.code <= Key.Z) {
245					char chr = cast(char)(key.code - Key.A) + 'a';
246					// Look for a hint
247					foreach(uint i, mnuItem; _focusedMenu) {
248						// Does the menu item have a hint?
249						if (mnuItem.hintPosition == -1) {
250							continue;
251						}
252
253						char hint = mnuItem.hint;
254
255						if (chr == hint) {
256							// This menu item is selected
257							_selectedMenuIndex = i;
258							_selectedMenu = mnuItem;
259							_drawSubmenu();
260							if (_selectedMenu.length > 0) {
261								// it is a submenu
262								_addSubmenu();
263								_drawSubmenu();
264							}
265							else {
266								// select the item
267								_cancelNextChar = true;
268								_focusedMenu = null;
269								Menu selected = _selectedMenu;
270								_selectedMenu = null;
271								redraw();
272		
273								onMenu(selected);
274
275								// Focus on the current widget
276								_controlContainer.onGotFocus();
277							}
278						}
279					}
280				}
281			}
282
283			// Do not pass the key off to a widget
284			return;
285		}
286		else if (_menu !is null) {
287			if (key.alt) {
288				uint chr = key.code - Key.A;
289				if (chr <= Key.Z) {
290					// compare to menu hints
291					foreach(mnuItem; _menu) {
292						// Does the menu item have a hint?
293						if (mnuItem.hintPosition == -1) {
294							continue;
295						}
296
297						auto hint = mnuItem.displayText().lowercase()[mnuItem.hintPosition];
298						uint chr2 = cast(uint)(hint - 'a');
299
300						if (chr == chr2) {
301							// This menu item is selected
302							_selectMenu(mnuItem);
303
304							// Do not pass the key off to a widget
305							return;
306						}
307					}
308				}
309			}
310		}
311		
312		_controlContainer.onKeyDown(key);
313	}
314
315	void onMenu(Menu mnu) {
316	}
317
318	void onKeyChar(dchar keyChar) {
319		if (_focusedMenu !is null || _cancelNextChar) {
320			_cancelNextChar = false;
321			return;
322		}
323		_controlContainer.onKeyChar(keyChar);
324	}
325
326	void onPrimaryMouseDown() {
327		_controlContainer.onPrimaryMouseDown();
328	}
329
330	void onPrimaryMouseUp() {
331		_controlContainer.onPrimaryMouseUp();
332	}
333
334	void onSecondaryMouseDown() {
335		_controlContainer.onSecondaryMouseDown();
336	}
337
338	void onSecondaryMouseUp() {
339		_controlContainer.onSecondaryMouseUp();
340	}
341
342	void onTertiaryMouseDown() {
343		_controlContainer.onTertiaryMouseDown();
344	}
345
346	void onTertiaryMouseUp() {
347		_controlContainer.onTertiaryMouseUp();
348	}
349
350	void onOtherMouseDown(uint button) {
351//		if (_focused_control !is null) {
352//		}
353	}
354
355	void onOtherMouseUp(uint button) {
356//		if (_focused_control !is null) {
357//		}
358	}
359
360	void onMouseWheelY(uint amount) {
361		_controlContainer.onMouseWheelY(amount);
362	}
363
364	void onMouseWheelX(uint amount) {
365		_controlContainer.onMouseWheelX(amount);
366	}
367
368	void onMouseMove() {
369		_controlContainer.onMouseMove();
370	}
371
372	void text(string value) {
373		_controlContainer.text(value);
374	}
375
376	string text() {
377		return _controlContainer.text;
378	}
379
380	uint width() {
381		return Console.width();
382	}
383
384	uint height() {
385		return Console.height();
386	}
387
388	// Methods
389
390	override void push(Dispatcher dsp) {
391		if (dsp is _controlContainer) {
392			super.push(dsp);
393		}
394		else if (cast(CuiWidget)dsp) {
395			_controlContainer.push(cast(CuiWidget)dsp);
396		}
397		else {
398			super.push(dsp);
399		}
400	}
401
402	Color backcolor() {
403		return _bgClr;
404	}
405
406	CuiApplication application() {
407		return cast(CuiApplication)this.responder;
408	}
409
410	bool isActive() {
411		return (application() !is null && application.window is this);
412	}
413
414	void menu(Menu mnu) {
415		_menu = mnu;
416		if (isActive) {
417			_drawMenu();
418		}
419		
420		// Turn off all rendering
421		Console.clipRect(0,0,this.width,this.height);
422
423		// Resize control container to take into account menubar
424		_controlContainer.resize(this.width, this.height-1);
425
426		// Move control container below the menu bar
427		_controlContainer.move(0,1);
428
429		// clear clipping region
430		Console.clipClear();
431		redraw();
432	}
433
434	Mouse mouseProps;
435
436private:
437
438	package final void _onResize() {
439		if (_menu) {
440			_controlContainer.resize(this.width, this.height - 1);
441		}
442		else {
443			_controlContainer.resize(this.width, this.height);
444		}
445		onResize();
446	}
447
448	void _drawSubmenu() {
449		MenuContext context = _menus[$-1];
450
451		Menu mnu = context.submenu;
452		uint x = context.region.left;
453		uint y = context.region.top;
454
455		uint maxLength = context.region.right - context.region.left;
456
457		foreach(subItem; mnu) {
458			Console.position(x, y);
459			if (subItem is _selectedMenu) {
460				Console.forecolor = Color.White;
461				Console.backcolor = Color.Blue;
462			}
463			else {
464				Console.forecolor = Color.Black;
465				Console.backcolor = Color.White;
466			}
467			Console.put(" ");
468			int padding = maxLength - subItem.displayText.length;
469
470			_drawMenuItem(subItem, false, maxLength - subItem.displayText.length);
471			if (subItem is _selectedMenu) {
472				Console.forecolor = Color.White;
473				Console.backcolor = Color.Blue;
474			}
475			else {
476				Console.forecolor = Color.Black;
477				Console.backcolor = Color.White;
478			}
479			Console.put(" ");
480			y++;
481		}
482	}
483
484	void _drawMenuItem(Menu mnuItem, bool drawHints = false, uint padding = 0) {
485		if (mnuItem is _selectedMenu) {
486			Console.forecolor = Color.Gray;
487			Console.backcolor = Color.Blue;
488		}
489		else {
490			Console.forecolor = Color.Black;
491			Console.backcolor = Color.Gray;
492		}
493
494		if (mnuItem.hintPosition >= 0) {
495			if (mnuItem.hintPosition > 0) {
496				Console.put(mnuItem.displayText[0..mnuItem.hintPosition]);
497			}
498
499			if (mnuItem !is _selectedMenu) {
500				if (drawHints) {
501					Console.forecolor = Color.Blue;
502				}
503				else {
504					Console.forecolor = Color.DarkBlue;
505				}
506			}
507
508			Console.put(mnuItem.displayText[mnuItem.hintPosition]);
509
510			if (mnuItem !is _selectedMenu) {
511				Console.forecolor = Color.Black;
512				Console.backcolor = Color.White;
513			}
514			if (mnuItem.hintPosition < mnuItem.displayText.length) {
515				Console.put(mnuItem.displayText[mnuItem.hintPosition + 1..mnuItem.displayText.length]);
516			}
517		}
518		else {
519			Console.put(mnuItem.displayText);
520		}
521
522		// Padding
523		for (uint i; i < padding; i++) {
524			Console.put(" ");
525		}
526
527		if (mnuItem is _selectedMenu) {
528			Console.forecolor = Color.Black;
529			Console.backcolor = Color.White;
530		}
531	}
532
533	void _drawMenu(bool drawHints = false) {
534		if (_menu is null) {
535			return;
536		}
537
538		uint curWidth = this.width;
539
540		Console.position(0,0);
541		Console.forecolor = Color.Black;
542		Console.backcolor = Color.White;
543
544		if (_menu.length > 0 && (_menu[0] is _selectedMenu)) {
545			Console.forecolor = Color.White;
546			Console.backcolor = Color.Blue;
547		}
548		else {
549			Console.forecolor = Color.White;
550			Console.backcolor = Color.Blue;
551		}
552		Console.put(" ");
553		curWidth--;
554
555		foreach(i, mnuItem; _menu) {
556			if (curWidth > mnuItem.displayText.length) {
557				_drawMenuItem(mnuItem, drawHints, 0);
558				if (mnuItem is _selectedMenu || (((i + 1) < _menu.length) && _menu[i+1] is _selectedMenu)) {
559					Console.forecolor = Color.White;
560					Console.backcolor = Color.Blue;
561					Console.put(" ");
562					Console.forecolor = Color.Black;
563					Console.backcolor = Color.White;
564				}
565				else {
566					Console.put(" ");
567				}
568				curWidth -= (mnuItem.displayText.length + 1);
569			}
570		}
571
572		bool drawCaption = false;
573		if (this.text !is null && curWidth >= (this.text.length + 1)) {
574			curWidth -= this.text.length + 1;
575			drawCaption = true;
576		}
577
578		if (curWidth > 0) {
579			for (; curWidth != 0; curWidth--) {
580				Console.put(" ");
581			}
582		}
583
584		if (drawCaption) {
585			Console.put(this.text, " ");
586		}
587	}
588
589	void _selectMenu(Menu mnu) {
590
591		// Switching focus to window
592		_controlContainer.onLostFocus();
593
594		// Do not show the cursor within a menu
595		Console.hideCaret();
596
597		// draw menu
598		if (_menu is null) {
599			return;
600		}
601
602		if (mnu.isChildOf(_menu)) {
603			// Find this menu is the root menu
604			_selectedMenu = mnu;
605			_selectedMenuIndex = 0;
606			_drawMenu();
607
608			if (mnu.length == 0) {
609				onMenu(mnu);
610				_selectedMenu = null;
611				_selectedMenuIndex = 0;
612				_drawMenu();
613				return;
614			}
615			else {
616				uint curPos = 1;
617
618				uint focusedIdx;
619
620				foreach(uint idx, mnuItem; _menu) {
621					if (mnuItem is mnu) {
622						focusedIdx = idx;
623						break;
624					}
625
626					curPos += mnuItem.displayText.length;
627					curPos ++;
628				}
629
630				_focusedMenuIndex = focusedIdx;
631				_focusedMenu = mnu;
632				_addSubmenuContext(curPos, 1, mnu);
633
634				if (mnu.length >  0) {
635					_selectedMenu = mnu[0];
636					_selectedMenuIndex = 0;
637				}
638				else {
639					_selectedMenu = null;
640				}
641
642				_drawSubmenu();
643			}
644		}
645	}
646	
647	void _addSubmenu() {
648		uint x;
649		uint y;
650
651		if (_menus.length == 0) {
652			return;
653		}
654
655		// This menu starts at the right of the current one + 2 (for the padding to either side)
656		x = _menus[$-1].region.right + 2;
657		y = _menus[$-1].region.top + _selectedMenuIndex;
658
659		_addSubmenuContext(x, y, _selectedMenu);
660		_focusedMenuIndex = _selectedMenuIndex;
661		_focusedMenu = _selectedMenu;
662
663		if (_focusedMenu.length >  0) {
664			_selectedMenu = _focusedMenu[0];
665			_selectedMenuIndex = 0;
666		}
667		else {
668			_selectedMenu = null;
669		}
670	}
671	
672	void _addSubmenuContext(uint x, uint y, Menu mnu) {
673		// get width of submenu
674		uint maxLength;
675		foreach(subItem; mnu) {
676			if (subItem.displayText.length > maxLength) {
677				maxLength = subItem.displayText.length;
678			}
679		}
680
681		MenuContext mnuContext;
682
683		mnuContext.submenu = mnu;
684
685		mnuContext.region.left = x;
686		mnuContext.region.right = x + maxLength;
687		mnuContext.region.top = y;
688		mnuContext.region.bottom = y + mnu.length;
689
690		mnuContext.oldSelectedMenu = _selectedMenu;
691		mnuContext.oldSelectedIndex = _selectedMenuIndex;
692		mnuContext.oldFocusedMenu = _focusedMenu;
693		mnuContext.oldFocusedIndex = _focusedMenuIndex;
694		
695		_menus ~= mnuContext;
696	}
697
698	void _removeMenuContext() {
699		if (_menus.length == 0) {
700			return;
701		}
702
703		MenuContext removed = _menus[$-1];
704		removed.region.right += 2;
705		_menus = _menus[0..$-1];
706
707		_selectedMenu = removed.oldSelectedMenu;
708		_selectedMenuIndex = removed.oldSelectedIndex;
709		_focusedMenu = removed.oldFocusedMenu;
710		_focusedMenuIndex = removed.oldFocusedIndex;
711
712		Console.clipClear();
713		Console.clipRect(0, 0, removed.region.left, this.height);
714		Console.clipRect(removed.region.left, 0, removed.region.right, removed.region.top);
715		Console.clipRect(removed.region.left, removed.region.bottom, removed.region.right, this.height);
716		Console.clipRect(removed.region.right, 0, this.width, this.height);
717
718		_controlContainer.onDraw();
719
720		Console.clipClear();
721
722		if (_menus.length > 0) {
723		//	_drawSubmenu();
724		}
725	}
726
727	Color _bgClr = Color.Black;
728
729	// head and tail of the control linked list
730	CuiWidget _firstControl;	//head
731	CuiWidget _lastControl;	//tail
732
733	int _numControls = 0;
734
735	// Current Menu
736	Menu _menu;
737
738	// In a menu?
739	Menu _focusedMenu;
740	uint _focusedMenuIndex;
741
742	struct MenuContext {
743		Menu submenu;
744		Menu oldSelectedMenu;
745		uint oldSelectedIndex;
746		Menu oldFocusedMenu;
747		uint oldFocusedIndex;
748		Rect region;
749	}
750	
751	Menu _selectedMenu;
752	uint _selectedMenuIndex;
753
754	CuiContainer _controlContainer;
755
756	MenuContext[] _menus;
757
758	bool _cancelNextChar;
759	
760	bool _inited;
761}