PageRenderTime 148ms CodeModel.GetById 100ms app.highlight 23ms RepoModel.GetById 17ms app.codeStats 1ms

/Scenes/UserInterfaces/Controls/Panel.cs

#
C# | 613 lines | 391 code | 74 blank | 148 comment | 25 complexity | 7511b11eabd10194129000fd1e23522c MD5 | raw file
  1using System.Collections.Generic;
  2using Delta.Engine;
  3using Delta.InputSystem;
  4using Delta.Rendering.Basics.Fonts;
  5using Delta.Scenes.Enums;
  6using Delta.Scenes.UserInterfaces.Designs;
  7using Delta.Utilities;
  8using Delta.Utilities.Datatypes;
  9using Delta.Utilities.Datatypes.Advanced;
 10using NUnit.Framework;
 11
 12namespace Delta.Scenes.UserInterfaces.Controls
 13{
 14	/// <summary>
 15	/// Panel
 16	/// </summary>
 17	public class Panel : BaseControl
 18	{
 19		#region Padding (Public)
 20		/// <summary>
 21		/// Defines the padding for the inner content / controls of the panel.
 22		/// Note: NOT SUPPORTED YET
 23		/// </summary>
 24		/// <returns>Spacing</returns>
 25		public Margin Padding
 26		{
 27			get;
 28			set;
 29		}
 30		#endregion
 31
 32		#region Protected
 33
 34		#region FallbackDesign (Protected)
 35		/// <summary>
 36		/// Defines the theme which will be used if no "Theme" was set explicitely.
 37		/// </summary>
 38		protected override ControlDesign FallbackDesign
 39		{
 40			get
 41			{
 42				return Theme.Current.PanelDesign;
 43			} // get
 44		}
 45		#endregion
 46
 47		#endregion
 48
 49		#region Private
 50
 51		#region horizontalScrollbar (Private)
 52		private readonly Scrollbar horizontalScrollbar;
 53		#endregion
 54
 55		#region needCheckForScrollbars (Private)
 56		private bool needCheckForScrollbars;
 57		#endregion
 58
 59		#endregion
 60
 61		#region Constructors
 62		/// <summary>
 63		/// Create panel
 64		/// </summary>
 65		public Panel()
 66		{
 67			horizontalScrollbar = new Scrollbar(this);
 68			Add(horizontalScrollbar);
 69			horizontalScrollbar.State = ElementState.Invisible;
 70			needCheckForScrollbars = false;
 71		}
 72		#endregion
 73
 74		#region Dispose (Public)
 75		/// <summary>
 76		/// Disposes the whole control (inclusive children).
 77		/// </summary>
 78		public override void Dispose()
 79		{
 80			// At first we need to destroy all Children of us
 81			List<BaseControl> rememberToRemove = new List<BaseControl>();
 82			foreach (BaseControl child in Children)
 83			{
 84				rememberToRemove.Add(child);
 85			} // foreach
 86			foreach (BaseControl child in rememberToRemove)
 87			{
 88				child.Dispose();
 89			} // foreach
 90
 91			base.Dispose();
 92		}
 93		#endregion
 94
 95		#region Add (Public)
 96		/// <summary>
 97		/// Add
 98		/// </summary>
 99		/// <param name="newChild">New child</param>
100		public override bool Add(AlignableElement newChild)
101		{
102			bool wasControlAdded = base.Add(newChild);
103
104			// We need to recheck if the scrollbar are needed now if a new control
105			// was added because maybe it doesn't fit (fully) in the visible panel
106			// space anymore or if a scrollbar is already shown then the available
107			// "scroll space" needs to be updated anyway
108			if (wasControlAdded)
109			{
110				// Note:
111				// We only modify that flag for "successful actions" to avoid
112				// "delete" this flag in the case that series of controls would be
113				// added in one frame and the last one was a "bad" one
114				needCheckForScrollbars = true;
115			} // if
116
117			return wasControlAdded;
118		}
119		#endregion
120
121		#region Remove (Public)
122		/// <summary>
123		/// Remove
124		/// </summary>
125		/// <param name="childControl">Child control</param>
126		public override bool Remove(AlignableElement childControl)
127		{
128			bool wasControlRemoved = base.Remove(childControl);
129
130			// We need to recheck if the scrollbar are needed now if a control was
131			// removed again because if the scrollbar is shown already the current
132			// "scroll space" need to be updated or maybe the remaining control fit
133			// now (fully) in the visible panel space and the scrollbar is not needed
134			// anymore
135			if (wasControlRemoved)
136			{
137				// Note:
138				// We only modify that flag for "successful actions" to avoid
139				// "delete" this flag in the case that series of controls would be
140				// removed in one frame and the last one was a "bad" one
141				needCheckForScrollbars = true;
142			} // if
143
144			return wasControlRemoved;
145		}
146		#endregion
147
148		#region GetChildrenOfType (Public)
149		/// <summary>
150		/// Get children of type
151		/// Recursive search for all children of Type T in this panel and all 
152		/// subpanels.
153		/// </summary>
154		/// <returns>List</returns>
155		public List<T> GetChildrenOfType<T>() where T : class
156		{
157			List<T> foundChildren = new List<T>();
158			for (int i = 0; i < Children.Count; i++)
159			{
160				if (Children[i] as Panel != null)
161				{
162					foundChildren.AddRange((Children[i] as Panel).GetChildrenOfType<T>());
163				} // if
164				else if ((Children[i] as T) != null)
165				{
166					foundChildren.Add(Children[i] as T);
167				} // else if
168			} // for
169			return foundChildren;
170		}
171		#endregion
172
173		#region GetScrolledOffset (Public)
174		/// <summary>
175		/// Get scrolled offset
176		/// </summary>
177		public float GetScrolledOffset()
178		{
179			return horizontalScrollbar.CurrentScrollOffset;
180		}
181		#endregion
182
183		#region ScrollForeward (Public)
184		/// <summary>
185		/// Scroll foreward
186		/// </summary>
187		public void ScrollForeward()
188		{
189			//float horizontalContentWidth = GetContentWidth();
190
191			//if (horizontalContentWidth <= Size.Width)
192			//{
193			//  // It only make sense to scroll if the content area is bigger than the
194			//  // panel area
195			//  return;
196			//} // if
197
198			//float newScrollOffset = horizontalScrollbar.CurrentScrollOffset +
199			//  horizontalScrollbar.ScrollWidth;
200			//if ((newScrollOffset + Size.Width) < horizontalContentWidth)
201			//{
202			//  horizontalScrollbar.CurrentScrollOffset = newScrollOffset;
203			//} // if
204			//else
205			//{
206			//  horizontalScrollbar.CurrentScrollOffset = horizontalContentWidth -
207			//    horizontalScrollbar.ScrollWidth;
208			//} // else
209			horizontalScrollbar.ScrollForewards();
210		}
211		#endregion
212
213		#region Methods (Private)
214
215		#region GetAlignmentArea
216		/// <summary>
217		/// Get alignment area
218		/// </summary>
219		/// <param name="totalArea">Total area</param>
220		/// <param name="areaToAlign">Area to align</param>
221		/// <param name="wishedAlignment">Wished alignment</param>
222		internal static Rectangle GetAlignmentArea(Size totalArea, Size areaToAlign,
223			Alignment wishedAlignment) //LOOK ?:, float borderThickness = 0.0f)
224		{
225			Point areaPos = Point.Zero;
226			switch (wishedAlignment)
227			{
228				case Alignment.BottomLeft:
229					areaPos.Y = totalArea.Height - areaToAlign.Height;
230					break;
231
232				case Alignment.Bottom:
233					areaPos.X = totalArea.Width * 0.5f - areaToAlign.Width * 0.5f;
234					areaPos.Y = totalArea.Height - areaToAlign.Height;
235					break;
236
237				case Alignment.BottomRight:
238					areaPos.X = totalArea.Width - areaToAlign.Width;
239					areaPos.Y = totalArea.Height - areaToAlign.Height;
240					break;
241
242				default:
243					Log.Warning("Sorry, the Alignment '" + wishedAlignment +
244					            "' isn't supported yet.");
245					break;
246			} // switch
247
248			return new Rectangle(areaPos, areaToAlign);
249		}
250		#endregion
251
252		#region UpdateDrawData
253		/// <summary>
254		/// Updates all data of this element which are required for the following
255		/// draw call.
256		/// </summary>
257		/// <remarks>
258		/// This method will only be called if the element is in an enabled state.
259		/// </remarks>
260		protected override void UpdateDrawData()
261		{
262			base.UpdateDrawData();
263
264			//return;
265			if (needCheckForScrollbars)
266			{
267				horizontalScrollbar.State = (GetContentWidth() > Size.Width)
268				                            	? ElementState.Enabled
269				                            	: ElementState.Invisible;
270			} // if
271		}
272		#endregion
273
274		#region DrawData
275		/// <summary>
276		/// Draws all data of this Panel which needs to be visualized.
277		/// </summary>
278		/// <remarks>
279		/// This method will only be called if the Panel is in a visible state.
280		/// </remarks>
281		protected override void DrawData()
282		{
283			base.DrawData();
284
285			//horizontalScrollbar.Run();
286		}
287		#endregion
288
289		#region OnSizeChanging
290		/// <summary>
291		/// On size changing
292		/// </summary>
293		/// <param name="oldSize">Old size</param>
294		/// <returns>
295		/// 'True' if the new value can be used or 'false' if the change should be
296		/// aborted.
297		/// </returns>
298		protected override bool OnSizeChanging(Size oldSize)
299		{
300			if (base.OnSizeChanging(oldSize))
301			{
302				// Notify the scrollbar that the size has changed
303				horizontalScrollbar.LocalArea = GetAlignmentArea(Size,
304					new Size(Size.Width, horizontalScrollbar.Size.Height),
305					Alignment.Bottom);
306
307				return true;
308			} // if
309
310			return false;
311		}
312		#endregion
313
314		#region GetScrolledControls
315		/// <summary>
316		/// Get scrolled controls
317		/// </summary>
318		internal List<BaseControl> GetScrolledControls()
319		{
320			List<BaseControl> visibleControls = new List<BaseControl>();
321
322			List<BaseControl> leftPartialControls = new List<BaseControl>();
323			List<BaseControl> rightPartialControls = new List<BaseControl>();
324
325			Rectangle visibleScrollArea = horizontalScrollbar.VisibleScrollArea;
326			foreach (BaseControl element in Children)
327			{
328				Rectangle childLocalArea = element.LocalArea;
329
330				// Checking which control is fully visible in the current scrolled
331				// panel section
332				if (childLocalArea.Left >= visibleScrollArea.Left &&
333				    childLocalArea.Right <= visibleScrollArea.Right)
334				{
335					visibleControls.Add(element);
336				} // if
337
338					// but still check for the other controls that aren't fully visible
339					// because maybe some elements are at least partial visible
340
341					// -> at least partial visible on the left side
342				else if (childLocalArea.Left < visibleScrollArea.Left &&
343				         childLocalArea.Right > visibleScrollArea.Left)
344				{
345					leftPartialControls.Add(element);
346					// Test:
347					//visibleControls.Add(element);
348				} // else if
349
350					// -> at least partial visible on the right side
351				else if (childLocalArea.Left < visibleScrollArea.Right &&
352				         childLocalArea.Right > visibleScrollArea.Right)
353				{
354					rightPartialControls.Add(element);
355					// Test:
356					//visibleControls.Add(element);
357				} // else if
358			} // foreach
359
360			return visibleControls;
361		}
362		#endregion
363
364		#region GetContentWidth
365		/// <summary>
366		/// Get content width
367		/// </summary>
368		internal float GetContentWidth()
369		{
370			float horizontalContentWidth = 0.0f;
371			foreach (BaseControl element in Children)
372			{
373				if (element.LocalArea.Right > horizontalContentWidth)
374				{
375					horizontalContentWidth = element.LocalArea.Right;
376				} // if
377			} // foreach
378
379			return horizontalContentWidth;
380		}
381		#endregion
382
383		#endregion
384
385		/// <summary>
386		/// Tests for Panel controls
387		/// </summary>
388		[Category("Visual")]
389		internal class PanelTests
390		{
391			#region Helpers
392
393			#region DisplayTwoPanelsWithChildren
394			/// <summary>
395			/// This test shows two panel with several children, one where the
396			/// children completely fit in the panel and no scrollbar is necessary and
397			/// one panel where the children controls need more space than the panel
398			/// has and a scrollbar is required.
399			/// </summary>
400			public static void DisplayTwoPanelsWithChildren()
401			{
402				Screen testScene = new Screen();
403
404				Margin panelPadding = new Margin(0.01f);
405
406				Panel fittingPanel = new Panel
407				{
408					//LocalArea = Rectangle.FromCenter(0.35f, 0.4f, 0.5f, 0.2f),
409					LocalArea = new Rectangle(0.2f, 0.3f, 0.3f, 0.15f),
410					CustomDesign = new ControlDesign
411					{
412						Background = BaseTheme.GetUIMaterial(Color.Yellow),
413					},
414					Padding = panelPadding,
415				};
416				testScene.Add(fittingPanel);
417
418				Label singleItem = new Label
419				{
420					LocalArea = new Rectangle(panelPadding.Left, panelPadding.Top, 0.1f,
421						0.1f),
422					CustomDesign = new TextControlDesign
423					{
424						Background = BaseTheme.GetUIMaterial(Color.Green),
425						TextFont = new Font(Font.Default, Color.Brown),
426					},
427					Text = "Lonely Label",
428				};
429				fittingPanel.Add(singleItem);
430
431				Panel toSmallPanel = new Panel
432				{
433					//LocalArea = Rectangle.FromCenter(0.35f, 0.4f, 0.5f, 0.2f),
434					LocalArea = new Rectangle(0.2f, 0.5f, 0.3f, 0.15f),
435					CustomDesign = new ControlDesign
436					{
437						Background = BaseTheme.GetUIMaterial(Color.Yellow),
438					},
439					Padding = panelPadding,
440				};
441				testScene.Add(toSmallPanel);
442
443				BaseControl lastControl = null;
444				for (int index = 0; index < 5; index++)
445				{
446					Label panelItem = new Label
447					{
448						LocalArea = new Rectangle(panelPadding.Left, panelPadding.Top, 0.1f,
449							0.1f),
450						CustomDesign = new TextControlDesign
451						{
452							Background = BaseTheme.GetUIMaterial(Color.Green),
453							TextFont = new Font(Font.Default, Color.Brown),
454						},
455						Text = "Label " + (index + 1),
456					};
457
458					if (lastControl != null)
459					{
460						panelItem.LocalArea = new Rectangle(panelItem.LocalArea.Left +
461						                                    lastControl.LocalArea.Right,
462							panelItem.LocalArea.Top,
463							panelItem.LocalArea.Width, panelItem.LocalArea.Height);
464					} // if
465
466					toSmallPanel.Add(panelItem);
467					lastControl = panelItem;
468				} // for
469
470				// Open now the scene to "activate" for the test
471				testScene.Open();
472
473				//Font infoFont = Font.Default;
474				Application.Start(delegate
475				{
476					if (Input.Keyboard.SpaceReleased)
477					{
478						toSmallPanel.ScrollForeward();
479					} // if
480				});
481			}
482			#endregion
483
484			#endregion
485
486			#region DisplayPanelWithChildren (Static)
487			/// <summary>
488			/// Display panel with children
489			/// </summary>
490			[Test]
491			public static void DisplayPanelWithChildren()
492			{
493				Panel testPanel = new Panel
494				{
495					//LocalArea = Rectangle.FromCenter(0.35f, 0.4f, 0.5f, 0.2f),
496					LocalArea = new Rectangle(0.2f, 0.3f, 0.3f, 0.15f),
497					CustomDesign = new ControlDesign
498					{
499						Background = BaseTheme.GetUIMaterial(Color.Yellow),
500					},
501					//Padding = 
502				};
503
504				float leftBorder = 0.01f;
505				float topBorder = leftBorder;
506				BaseControl lastControl = null;
507				for (int index = 0; index < 7; index++)
508				{
509					Label panelItem = new Label
510					{
511						LocalArea = new Rectangle(leftBorder, topBorder, 0.1f, 0.1f),
512						CustomDesign = new TextControlDesign
513						{
514							Background = BaseTheme.GetUIMaterial(Color.Green),
515							TextFont = new Font(Font.Default, Color.Brown),
516						},
517						Text = "Label " + (index + 1),
518					};
519
520					if (lastControl != null)
521					{
522						panelItem.LocalArea = new Rectangle(panelItem.LocalArea.Left +
523						                                    lastControl.LocalArea.Right,
524							panelItem.LocalArea.Top,
525							panelItem.LocalArea.Width, panelItem.LocalArea.Height);
526					} // if
527
528					testPanel.Add(panelItem);
529					lastControl = panelItem;
530				} // for
531
532				Screen testScene = new Screen();
533				testScene.Add(testPanel);
534
535				// Open now the scene to "activate" for the test
536				testScene.Open();
537
538				//Font infoFont = Font.Default;
539				Application.Start(delegate
540				{
541					if (Input.Keyboard.SpaceReleased)
542					{
543						testPanel.ScrollForeward();
544					} // if
545				});
546			}
547			#endregion
548
549			#region DisplayPanelWithButtons (Static)
550			/// <summary>
551			/// Display panel with children
552			/// </summary>
553			[Test]
554			public static void DisplayPanelWithButtons()
555			{
556				Panel testPanel = new Panel
557				{
558					CustomDesign = new ControlDesign
559					{
560						Background = BaseTheme.GetUIMaterial(
561							new Color(1.0f, 1.0f, 1.0f, 0.5f))
562					},
563					//Size = new Size(0.18f, 0.1f),
564					//LocalPosition = new Point(0.5f, 0.5f),
565					LocalArea = new Rectangle(0.2f, 0.3f, 0.3f, 0.15f),
566				};
567
568				// Create add and remove emitter buttons with some basic props.
569				Button addEmitterButton = new Button
570				{
571					CustomDesign = new TextControlDesign
572					{
573						//Background = null,
574						Background = BaseTheme.GetUIMaterial(Color.Black),
575						TextFont = Font.Default,
576					},
577					State = ElementState.Enabled,
578					Text = "A Test Button",
579					LocalArea = new Rectangle(0.0f, 0.0f, 0.18f, 0.05f),
580				};
581
582				Button removeEmitterButton = new Button
583				{
584					CustomDesign = new TextControlDesign
585					{
586						Background = BaseTheme.GetUIMaterial(Color.Black),
587						TextFont = Font.Default,
588					},
589					Text = "TEST ABC",
590					LocalArea = new Rectangle(0.0f, addEmitterButton.Size.Height, 0.18f, 0.05f),
591				};
592
593				testPanel.Add(addEmitterButton);
594				testPanel.Add(removeEmitterButton);
595
596				testPanel.State = ElementState.Enabled;
597				Screen testScene = new Screen();
598
599				testScene.Add(testPanel);
600
601				// Open now the scene to "activate" for the test
602				testScene.Open();
603
604				//Font infoFont = Font.Default;
605				Application.Start(delegate
606				{
607				});
608			}
609			#endregion
610		}
611	}
612}
613