CSharpGL(26)在opengl中实现控件布局/渲染文字
如图所示,可以将文字、坐标轴固定在窗口的一角。
下载
CSharpGL已在GitHub开源,欢迎对OpenGL有兴趣的同学加入(https://github.com/bitzhuwei/CSharpGL)
UI控件布局关键点
ILayout
类似Winform控件那样,控件的位置、大小由其Anchor等属性决定。窗口大小改变时,控件的位置、大小会随之改变。
所以模仿Control类,直接使用Anchor作为UIRenderer的接口。
1 /// <summary> 2 /// Supports layout UI element in OpenGL canvas. 3 /// 实现在OpenGL窗口中的UI布局 4 /// </summary> 5 public interface ILayout : ITreeNode<UIRenderer> 6 { 7 //event EventHandler afterLayout; 8 9 /// <summary> 10 /// the edges of the <see cref="GLCanvas"/> to which a UI’s rect is bound and determines how it is resized with its parent. 11 /// <para>something like AnchorStyles.Left | AnchorStyles.Bottom.</para> 12 /// </summary> 13 System.Windows.Forms.AnchorStyles Anchor { get; set; } 14 15 /// <summary> 16 /// Gets or sets the space between viewport and SimpleRect. 17 /// </summary> 18 System.Windows.Forms.Padding Margin { get; set; } 19 20 /// <summary> 21 /// 相对于Parent左下角的位置(Left Down location) 22 /// </summary> 23 System.Drawing.Point Location { get; set; } 24 25 /// <summary> 26 /// Stores width when <see cref="Anchor"/>.Left & <see cref="Anchor"/>.Right is <see cref="Anchor"/>.None. 27 /// <para> and height when <see cref="Anchor"/>.Top & <see cref="Anchor"/>.Bottom is <see cref="Anchor"/>.None.</para> 28 /// </summary> 29 System.Drawing.Size Size { get; set; } 30 31 /// <summary> 32 /// 33 /// </summary> 34 System.Drawing.Size ParentLastSize { get; set; } 35 36 /// <summary> 37 /// 38 /// </summary> 39 int zNear { get; set; } 40 41 /// <summary> 42 /// 43 /// </summary> 44 int zFar { get; set; } 45 46 }
实现在OpenGL窗口中的UI布局
有了数据结构,就可以实现窗口中的UI布局了。当窗口大小改变时,调用下面的函数。
1 /// <summary> 2 /// layout controls in OpenGL canvas. 3 /// <para>This coordinate system is as below.</para> 4 /// <para> /\ y</para> 5 /// <para> |</para> 6 /// <para> |</para> 7 /// <para> |</para> 8 /// <para> |</para> 9 /// <para> |</para> 10 /// <para> |----------------->x</para> 11 /// <para>(0, 0)</para> 12 /// </summary> 13 /// <param name="uiRenderer"></param> 14 internal static void Layout(this ILayout uiRenderer) 15 { 16 ILayout parent = uiRenderer.Parent; 17 if (parent != null) 18 { 19 uiRenderer.Self.DoBeforeLayout(); 20 NonRootNodeLayout(uiRenderer, parent); 21 uiRenderer.Self.DoAfterLayout(); 22 } 23 24 foreach (var item in uiRenderer.Children) 25 { 26 item.Layout(); 27 } 28 29 if (parent != null) 30 { 31 uiRenderer.ParentLastSize = parent.Size; 32 } 33 } 34 35 /// <summary> 36 /// leftRightAnchor = (AnchorStyles.Left | AnchorStyles.Right); 37 /// </summary> 38 private const AnchorStyles leftRightAnchor = (AnchorStyles.Left | AnchorStyles.Right); 39 40 /// <summary> 41 /// topBottomAnchor = (AnchorStyles.Top | AnchorStyles.Bottom); 42 /// </summary> 43 private const AnchorStyles topBottomAnchor = (AnchorStyles.Top | AnchorStyles.Bottom); 44 45 /// <summary> 46 /// Gets <paramref name="currentNode"/>'s location and size according to its state and parent's information. 47 /// </summary> 48 /// <param name="currentNode"></param> 49 /// <param name="parent"></param> 50 private static void NonRootNodeLayout(ILayout currentNode, ILayout parent) 51 { 52 int x, y, width, height; 53 if ((currentNode.Anchor & leftRightAnchor) == leftRightAnchor) 54 { 55 width = parent.Size.Width - currentNode.Margin.Left - currentNode.Margin.Right; 56 //width = currentNode.Size.Width + (parent.Size.Width - currentNode.ParentLastSize.Width); 57 if (width < 0) { width = 0; } 58 } 59 else 60 { 61 width = currentNode.Size.Width; 62 } 63 64 if ((currentNode.Anchor & topBottomAnchor) == topBottomAnchor) 65 { 66 height = parent.Size.Height - currentNode.Margin.Top - currentNode.Margin.Bottom; 67 //height = currentNode.Size.Height + (parent.Size.Height - currentNode.ParentLastSize.Height); 68 if (height < 0) { height = 0; } 69 } 70 else 71 { 72 height = currentNode.Size.Height; 73 } 74 75 if ((currentNode.Anchor & leftRightAnchor) == AnchorStyles.None) 76 { 77 x = (int)( 78 (parent.Size.Width - width) 79 * ((double)currentNode.Margin.Left / (double)(currentNode.Margin.Left + currentNode.Margin.Right))); 80 } 81 else if ((currentNode.Anchor & leftRightAnchor) == AnchorStyles.Left) 82 { 83 x = parent.Location.X + currentNode.Margin.Left; 84 } 85 else if ((currentNode.Anchor & leftRightAnchor) == AnchorStyles.Right) 86 { 87 x = parent.Location.X + parent.Size.Width - currentNode.Margin.Right - width; 88 } 89 else if ((currentNode.Anchor & leftRightAnchor) == leftRightAnchor) 90 { 91 x = parent.Location.X + currentNode.Margin.Left; 92 } 93 else 94 { throw new Exception("uiRenderer should not happen!"); } 95 96 if ((currentNode.Anchor & topBottomAnchor) == AnchorStyles.None) 97 { 98 y = (int)( 99 (parent.Size.Height - height) 100 * ((double)currentNode.Margin.Bottom / (double)(currentNode.Margin.Bottom + currentNode.Margin.Top))); 101 } 102 else if ((currentNode.Anchor & topBottomAnchor) == AnchorStyles.Bottom) 103 { 104 //y = currentNode.Margin.Bottom; 105 y = parent.Location.Y + currentNode.Margin.Bottom; 106 } 107 else if ((currentNode.Anchor & topBottomAnchor) == AnchorStyles.Top) 108 { 109 //y = parent.Size.Height - height - currentNode.Margin.Top; 110 y = parent.Location.Y + parent.Size.Height - currentNode.Margin.Top - height; 111 } 112 else if ((currentNode.Anchor & topBottomAnchor) == topBottomAnchor) 113 { 114 //y = currentNode.Margin.Top + parent.Location.Y; 115 y = parent.Location.Y + currentNode.Margin.Bottom; 116 } 117 else 118 { throw new Exception("This should not happen!"); } 119 120 currentNode.Location = new System.Drawing.Point(x, y); 121 currentNode.Size = new Size(width, height); 122 }