PageRenderTime 91ms CodeModel.GetById 28ms RepoModel.GetById 6ms app.codeStats 0ms

/Floe.UI/ChatBox/ChatPresenter_Rendering.cs

https://github.com/neem/Nilas
C# | 376 lines | 326 code | 50 blank | 0 comment | 61 complexity | 8e87d9805a063fbf525391348f739365 MD5 | raw file
  1. using System;
  2. using System.Collections.Generic;
  3. using System.Linq;
  4. using System.Windows;
  5. using System.Windows.Controls.Primitives;
  6. using System.Windows.Media;
  7. using System.Windows.Media.TextFormatting;
  8. namespace Floe.UI
  9. {
  10. public partial class ChatPresenter : ChatBoxBase, IScrollInfo
  11. {
  12. private const double SeparatorPadding = 6.0;
  13. private class Block
  14. {
  15. public ChatLine Source { get; set; }
  16. public Brush Foreground { get; set; }
  17. public string TimeString { get; set; }
  18. public string NickString { get; set; }
  19. public TextLine Time { get; set; }
  20. public TextLine Nick { get; set; }
  21. public TextLine[] Text { get; set; }
  22. public int CharStart { get; set; }
  23. public int CharEnd { get; set; }
  24. public double Y { get; set; }
  25. public double NickX { get; set; }
  26. public double TextX { get; set; }
  27. public double Height { get; set; }
  28. }
  29. private LinkedList<Block> _blocks = new LinkedList<Block>();
  30. private double _lineHeight;
  31. private LinkedListNode<Block> _bottomBlock;
  32. private Typeface Typeface
  33. {
  34. get
  35. {
  36. return new Typeface(this.FontFamily, this.FontStyle, this.FontWeight, this.FontStretch);
  37. }
  38. }
  39. private Color BackgroundColor
  40. {
  41. get
  42. {
  43. if (this.Background is SolidColorBrush)
  44. {
  45. return ((SolidColorBrush)this.Background).Color;
  46. }
  47. return Colors.Black;
  48. }
  49. }
  50. private string FormatNick(string nick)
  51. {
  52. if (!this.UseTabularView)
  53. {
  54. if (nick == null)
  55. {
  56. nick = "* ";
  57. }
  58. else
  59. {
  60. nick = string.Format("<{0}> ", nick);
  61. }
  62. }
  63. return nick ?? "*";
  64. }
  65. private string FormatTime(DateTime time)
  66. {
  67. return this.ShowTimestamp ? time.ToString(this.TimestampFormat + " ") : "";
  68. }
  69. private Brush GetNickColor(int hashCode)
  70. {
  71. var rand = new Random(hashCode);
  72. int rgb = 0;
  73. Color c = Colors.Black;
  74. do
  75. {
  76. rgb = rand.Next();
  77. for (int i = 0; i < this.NicknameColorSeed; i++)
  78. {
  79. rgb = rand.Next();
  80. }
  81. var bg = this.BackgroundColor;
  82. c = Color.FromRgb((byte)(rgb >> 16), (byte)(rgb >> 8), (byte)rgb);
  83. rgb = (int)Math.Abs(bg.R - c.R) + (int)Math.Abs(bg.G - c.G) + (int)Math.Abs(bg.B - c.B);
  84. }
  85. while (rgb < 125);
  86. return new SolidColorBrush(c);
  87. }
  88. private void FormatSingle(ChatLine source)
  89. {
  90. if (string.IsNullOrEmpty(source.Text))
  91. {
  92. return;
  93. }
  94. var b = new Block();
  95. b.Source = source;
  96. b.Foreground = this.Palette[b.Source.ColorKey];
  97. b.TimeString = this.FormatTime(b.Source.Time);
  98. b.NickString = this.FormatNick(b.Source.Nick);
  99. var formatter = new ChatFormatter(this.Typeface, this.FontSize, this.Foreground, this.Palette);
  100. b.Time = formatter.Format(b.TimeString, null, this.ViewportWidth, b.Foreground, this.Background,
  101. TextWrapping.NoWrap).FirstOrDefault();
  102. b.NickX = b.Time != null ? b.Time.WidthIncludingTrailingWhitespace : 0.0;
  103. var nickBrush = b.Foreground;
  104. if (this.ColorizeNicknames && b.Source.NickHashCode != 0)
  105. {
  106. nickBrush = this.GetNickColor(b.Source.NickHashCode);
  107. }
  108. b.Nick = formatter.Format(b.NickString, null, this.ViewportWidth - b.NickX, nickBrush, this.Background,
  109. TextWrapping.NoWrap).First();
  110. b.TextX = b.NickX + b.Nick.WidthIncludingTrailingWhitespace;
  111. if (this.UseTabularView)
  112. {
  113. if (b.TextX > this.ColumnWidth)
  114. {
  115. if (this.AutoSizeColumn)
  116. {
  117. this.ColumnWidth = b.TextX;
  118. _blocks.AddLast(b);
  119. this.FormatAll();
  120. return;
  121. }
  122. else
  123. {
  124. b.Nick = formatter.Format(b.NickString, null, this.ColumnWidth - (b.Time != null ? b.Time.Width : 0.0),
  125. nickBrush, this.Background, TextWrapping.NoWrap).First();
  126. }
  127. }
  128. b.TextX = this.ColumnWidth + SeparatorPadding * 2.0 + 1.0;
  129. b.NickX = this.ColumnWidth - b.Nick.WidthIncludingTrailingWhitespace;
  130. }
  131. var offset = _blocks.Last != null ? _blocks.Last.Value.CharEnd : 0;
  132. b.Text = formatter.Format(b.Source.Text, b.Source, this.ViewportWidth - b.TextX, b.Foreground,
  133. this.Background, TextWrapping.Wrap).ToArray();
  134. b.Height = b.Text.Sum((t) => t.Height);
  135. _lineHeight = b.Text[0].Height;
  136. _extentHeight = Math.Max(_extentHeight, _lineHeight);
  137. _extentHeight += b.Height;
  138. b.CharStart = offset;
  139. offset += b.TimeString.Length + b.NickString.Length + b.Source.Text.Length;
  140. b.CharEnd = offset;
  141. _blocks.AddLast(b);
  142. this.InvalidateVisual();
  143. if (_viewer != null)
  144. {
  145. _viewer.InvalidateScrollInfo();
  146. if (_isAutoScrolling)
  147. {
  148. this.ScrollToEnd();
  149. }
  150. }
  151. }
  152. private void FormatAll()
  153. {
  154. _extentHeight = 0.0;
  155. this.InvalidateVisual();
  156. if (_blocks.Count < 1 || this.ViewportWidth < 1.0)
  157. {
  158. return;
  159. }
  160. var formatter = new ChatFormatter(this.Typeface, this.FontSize, this.Foreground, this.Palette);
  161. _blocks.ForEach((b) =>
  162. {
  163. b.Foreground = this.Palette[b.Source.ColorKey];
  164. b.TimeString = this.FormatTime(b.Source.Time);
  165. b.NickString = this.FormatNick(b.Source.Nick);
  166. b.NickX = b.TextX = 0.0;
  167. b.Time = formatter.Format(b.TimeString, null, this.ViewportWidth, b.Foreground, this.Background,
  168. TextWrapping.NoWrap).FirstOrDefault();
  169. b.NickX = b.Time != null ? b.Time.WidthIncludingTrailingWhitespace : 0.0;
  170. this.ColumnWidth = Math.Max(this.ColumnWidth, b.NickX);
  171. });
  172. double nickX = _blocks.Max((b) => b.NickX);
  173. _blocks.ForEach((b) =>
  174. {
  175. var nickBrush = b.Foreground;
  176. if (this.ColorizeNicknames && b.Source.NickHashCode != 0)
  177. {
  178. nickBrush = this.GetNickColor(b.Source.NickHashCode);
  179. }
  180. b.Nick = formatter.Format(b.NickString, null,
  181. this.UseTabularView ? this.ColumnWidth - (b.Time != null ? b.Time.Width : 0.0) : this.ViewportWidth,
  182. nickBrush, this.Background, TextWrapping.NoWrap).First();
  183. if (this.UseTabularView)
  184. {
  185. b.NickX = nickX;
  186. }
  187. b.TextX = b.NickX + b.Nick.WidthIncludingTrailingWhitespace;
  188. });
  189. double textX = this.ColumnWidth + SeparatorPadding * 2.0 + 1.0;
  190. var offset = 0;
  191. _blocks.ForEach((b) =>
  192. {
  193. if (this.UseTabularView)
  194. {
  195. b.TextX = textX;
  196. b.NickX = Math.Max(b.Time != null ? b.Time.Width : 0.0, this.ColumnWidth - b.Nick.Width);
  197. }
  198. b.Text = formatter.Format(b.Source.Text, b.Source, this.ViewportWidth - b.TextX, b.Foreground,
  199. this.Background, TextWrapping.Wrap).ToArray();
  200. b.Height = b.Text.Sum((t) => t.Height);
  201. _extentHeight += b.Height;
  202. _lineHeight = b.Text[0].Height;
  203. b.CharStart = offset;
  204. offset += b.TimeString.Length + b.NickString.Length + b.Source.Text.Length;
  205. b.CharEnd = offset;
  206. });
  207. _extentHeight += _lineHeight;
  208. if (_viewer != null)
  209. {
  210. _viewer.InvalidateScrollInfo();
  211. if (_isAutoScrolling)
  212. {
  213. this.ScrollToEnd();
  214. }
  215. }
  216. }
  217. protected override void OnRender(DrawingContext dc)
  218. {
  219. base.OnRender(dc);
  220. var visual = PresentationSource.FromVisual(this);
  221. if (visual == null)
  222. {
  223. return;
  224. }
  225. var m = visual.CompositionTarget.TransformToDevice;
  226. var scaledPen = new Pen(this.DividerBrush, 1 / m.M11);
  227. double guidelineHeight = scaledPen.Thickness;
  228. double vPos = this.ActualHeight, height = 0.0, minHeight = this.ExtentHeight - (this.VerticalOffset + this.ActualHeight);
  229. _bottomBlock = null;
  230. var guidelines = new GuidelineSet();
  231. dc.DrawRectangle(Brushes.Transparent, null, new Rect(new Size(this.ViewportWidth, this.ActualHeight)));
  232. var node = _blocks.Last;
  233. do
  234. {
  235. if (node == null)
  236. {
  237. break;
  238. }
  239. var block = node.Value;
  240. block.Y = double.NaN;
  241. bool drawAny = false;
  242. if (block.Text == null || block.Text.Length < 1)
  243. {
  244. continue;
  245. }
  246. for (int j = block.Text.Length - 1; j >= 0; --j)
  247. {
  248. var line = block.Text[j];
  249. if ((height += line.Height) <= minHeight)
  250. {
  251. continue;
  252. }
  253. vPos -= line.Height;
  254. drawAny = true;
  255. }
  256. if (drawAny)
  257. {
  258. block.Y = vPos;
  259. if ((block.Source.Marker & ChatMarker.NewMarker) > 0)
  260. {
  261. var markerBrush = new LinearGradientBrush(this.NewMarkerColor,
  262. this.NewMarkerTransparentColor, 90.0);
  263. dc.DrawRectangle(markerBrush, null,
  264. new Rect(new Point(0.0, block.Y), new Size(this.ViewportWidth, _lineHeight * 5)));
  265. }
  266. if ((block.Source.Marker & ChatMarker.OldMarker) > 0)
  267. {
  268. var markerBrush = new LinearGradientBrush(this.OldMarkerTransparentColor,
  269. this.OldMarkerColor, 90.0);
  270. dc.DrawRectangle(markerBrush, null,
  271. new Rect(new Point(0.0, (block.Y + block.Height) - _lineHeight * 5),
  272. new Size(this.ViewportWidth, _lineHeight * 5)));
  273. }
  274. if (_bottomBlock == null)
  275. {
  276. _bottomBlock = node;
  277. }
  278. guidelines.GuidelinesY.Add(vPos + guidelineHeight);
  279. }
  280. }
  281. while (node.Previous != null && vPos >= -_lineHeight * 5.0 && (node = node.Previous) != null);
  282. dc.PushGuidelineSet(guidelines);
  283. if (this.UseTabularView)
  284. {
  285. double lineX = this.ColumnWidth + SeparatorPadding;
  286. dc.DrawLine(scaledPen, new Point(lineX, 0.0), new Point(lineX, this.ActualHeight));
  287. }
  288. if (_blocks.Count < 1)
  289. {
  290. return;
  291. }
  292. do
  293. {
  294. var block = node.Value;
  295. if (double.IsNaN(block.Y))
  296. {
  297. continue;
  298. }
  299. if ((block.Source.Marker & ChatMarker.Attention) > 0)
  300. {
  301. var markerBrush = new SolidColorBrush(this.AttentionColor);
  302. dc.DrawRectangle(markerBrush, null,
  303. new Rect(new Point(block.TextX, block.Y), new Size(this.ViewportWidth - block.TextX, block.Height)));
  304. }
  305. block.Nick.Draw(dc, new Point(block.NickX, block.Y), InvertAxes.None);
  306. if (block.Time != null)
  307. {
  308. block.Time.Draw(dc, new Point(0.0, block.Y), InvertAxes.None);
  309. }
  310. for (int k = 0; k < block.Text.Length; k++)
  311. {
  312. block.Text[k].Draw(dc, new Point(block.TextX, block.Y + k * _lineHeight), InvertAxes.None);
  313. }
  314. if (this.IsSelecting)
  315. {
  316. this.DrawSelectedLine(dc, block);
  317. }
  318. }
  319. while ((node = node.Next) != null);
  320. }
  321. protected override void OnRenderSizeChanged(SizeChangedInfo sizeInfo)
  322. {
  323. this.FormatAll();
  324. }
  325. }
  326. }