OSDN Git Service

選択領域の表示時の処理を最適化した
[fooeditengine/FooEditEngine.git] / WPF / FooEditEngine / WPF / WPFRender.cs
1 /*
2  * Copyright (C) 2013 FooProject
3  * * This program is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by
4  * the Free Software Foundation; either version 3 of the License, or (at your option) any later version.
5
6  * This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of 
7  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details.
8
9 You should have received a copy of the GNU General Public License along with this program. If not, see <http://www.gnu.org/licenses/>.
10  */
11 using System;
12 using System.Collections.Generic;
13 using System.Linq;
14 using System.Windows;
15 using System.Windows.Controls;
16 using System.Windows.Media;
17 using System.Windows.Media.TextFormatting;
18
19 namespace FooEditEngine.WPF
20 {
21     sealed class WPFRender : IPrintableTextRender, IDisposable
22     {
23         FontFamily _Font;
24         double _FontSize, _LineHeight;
25         DrawingContext Context;
26         int _TabWidthChar;
27         double TabWidth;
28         bool _RightToLeft;
29         System.Windows.Media.Color ForegroundColor, BackgroundColor, HilightColor, Keyword1Color, Keyword2Color, LiteralColor, UrlColor, ControlCharColor, CommentColor, InsertCaretColor, OverwriteCaretColor, LineMarkerColor;
30         BrushStockes Brushes = new BrushStockes();
31         PenStockes Pens = new PenStockes();
32         VisualHost host;
33         DrawingVisual visual;
34
35         public WPFRender(FontFamily font, double fontSize)
36         {
37             this.ChangedRenderResource += (s, e) => { };
38             this.ChangedRightToLeft += (s, e) => { };
39             this.FontFamily = font;
40             this.FontSize = fontSize;
41         }
42
43         public WPFRender(FooTextBox textbox, double width, double height,FrameworkElement image)
44         {
45             this.ChangedRenderResource += (s, e) => { };
46             this.ChangedRightToLeft += (s, e) => { };
47             
48             this.FontFamily = textbox.FontFamily;
49             this.FontSize = textbox.FontSize;
50             this.ForegroundColor = textbox.Foreground;
51             this.BackgroundColor = textbox.Background;
52             this.ControlCharColor = textbox.ControlChar;
53             this.HilightColor = textbox.Hilight;
54             this.CommentColor = textbox.Comment;
55             this.UrlColor = textbox.URL;
56             this.Keyword1Color = textbox.Keyword1;
57             this.Keyword2Color = textbox.Keyword2;
58             this.LiteralColor = textbox.Literal;
59             this.InsertCaretColor = textbox.InsertCaret;
60             this.OverwriteCaretColor = textbox.OverwriteCaret;
61             this.LineMarkerColor = textbox.LineMarker;
62
63             this.host = (VisualHost)image;
64         }
65
66         public event EventHandler ChangedRightToLeft;
67
68         public TextAntialiasMode TextAntialiasMode
69         {
70             get;
71             set;
72         }
73
74         public bool ShowFullSpace
75         {
76             get;
77             set;
78         }
79
80         public bool ShowHalfSpace
81         {
82             get;
83             set;
84         }
85
86         public bool ShowTab
87         {
88             get;
89             set;
90         }
91
92         public bool RightToLeft
93         {
94             get { return this._RightToLeft; }
95             set
96             {
97                 this._RightToLeft = value;
98                 this.ChangedRightToLeft(this, null);
99             }
100         }
101
102         public bool InsertMode
103         {
104             get;
105             set;
106         }
107
108         public Rectangle TextArea
109         {
110             get;
111             set;
112         }
113
114         public FontFamily FontFamily
115         {
116             get
117             {
118                 return this._Font;
119             }
120             set
121             {
122                 this._Font = value;
123                 this.ChangedRenderResource(this, new ChangedRenderRsourceEventArgs(ResourceType.Font));
124             }
125         }
126
127         public double FontSize
128         {
129             get
130             {
131                 return this._FontSize;
132             }
133             set
134             {
135                 this._FontSize = value;
136
137                 TextLayout layout = new TextLayout("0", this.FontFamily, this.FontSize, Brushes[this.Foreground], 0);
138                 this.LineNemberWidth = layout.Lines[0].Width * EditView.LineNumberLength;
139                 this._LineHeight = layout.Lines[0].Height;
140                 this.emSize = new Size(layout.Lines[0].Width, layout.Lines[0].Height);
141                 layout.Dispose();
142                 
143                 this.ChangedRenderResource(this, new ChangedRenderRsourceEventArgs(ResourceType.Font));
144             }
145         }
146
147         public double LineNemberWidth
148         {
149             get;
150             private set;
151         }
152
153         public double FoldingWidth
154         {
155             get
156             {
157                 return 0;
158             }
159         }
160
161         public int TabWidthChar
162         {
163             get
164             {
165                 return this._TabWidthChar;
166             }
167             set
168             {
169                 TextLayout layout = new TextLayout("a", this.FontFamily, this.FontSize, this.Brushes[this.ForegroundColor], 0);
170                 double width = layout.Lines[0].WidthIncludingTrailingWhitespace;
171                 layout.Dispose();
172                 this.TabWidth = width * value;
173                 this._TabWidthChar = value;
174             }
175         }
176
177         public Size emSize
178         {
179             get;
180             private set;
181         }
182
183         public System.Windows.Media.Color Foreground
184         {
185             get
186             {
187                 return this.ForegroundColor;
188             }
189             set
190             {
191                 this.ForegroundColor = value;
192                 this.ChangedRenderResource(this, new ChangedRenderRsourceEventArgs(ResourceType.Brush));
193             }
194         }
195
196         public System.Windows.Media.Color Background
197         {
198             get
199             {
200                 return this.BackgroundColor;
201             }
202             set
203             {
204                 this.BackgroundColor = value;
205                 this.ChangedRenderResource(this, new ChangedRenderRsourceEventArgs(ResourceType.Brush));
206             }
207         }
208
209         public System.Windows.Media.Color InsertCaret
210         {
211             get
212             {
213                 return this.InsertCaretColor;
214             }
215             set
216             {
217                 this.InsertCaretColor = value;
218             }
219         }
220
221         public System.Windows.Media.Color OverwriteCaret
222         {
223             get
224             {
225                 return this.OverwriteCaretColor;
226             }
227             set
228             {
229                 this.OverwriteCaretColor = value;
230             }
231         }
232
233         public System.Windows.Media.Color LineMarker
234         {
235             get
236             {
237                 return this.LineMarkerColor;
238             }
239             set
240             {
241                 this.LineMarkerColor = value;
242             }
243         }
244
245         public System.Windows.Media.Color ControlChar
246         {
247             get
248             {
249                 return this.ControlCharColor;
250             }
251             set
252             {
253                 this.ControlCharColor = value;
254                 this.ChangedRenderResource(this, new ChangedRenderRsourceEventArgs(ResourceType.Brush));
255             }
256         }
257
258         public System.Windows.Media.Color Url
259         {
260             get
261             {
262                 return this.UrlColor;
263             }
264             set
265             {
266                 this.UrlColor = value;
267                 this.ChangedRenderResource(this, new ChangedRenderRsourceEventArgs(ResourceType.Brush));
268             }
269         }
270
271         public System.Windows.Media.Color Hilight
272         {
273             get
274             {
275                 return this.HilightColor;
276             }
277             set
278             {
279                 this.HilightColor = value;
280                 this.ChangedRenderResource(this, new ChangedRenderRsourceEventArgs(ResourceType.Brush));
281             }
282         }
283
284         public System.Windows.Media.Color Comment
285         {
286             get
287             {
288                 return this.CommentColor;
289             }
290             set
291             {
292                 this.CommentColor = value;
293                 this.ChangedRenderResource(this, new ChangedRenderRsourceEventArgs(ResourceType.Brush));
294             }
295         }
296
297         public System.Windows.Media.Color Literal
298         {
299             get
300             {
301                 return this.LiteralColor;
302             }
303             set
304             {
305                 this.LiteralColor = value;
306                 this.ChangedRenderResource(this, new ChangedRenderRsourceEventArgs(ResourceType.Brush));
307             }
308         }
309
310         public System.Windows.Media.Color Keyword1
311         {
312             get
313             {
314                 return this.Keyword1Color;
315             }
316             set
317             {
318                 this.Keyword1Color = value;
319                 this.ChangedRenderResource(this, new ChangedRenderRsourceEventArgs(ResourceType.Brush));
320             }
321         }
322
323         public System.Windows.Media.Color Keyword2
324         {
325             get
326             {
327                 return this.Keyword2Color;
328             }
329             set
330             {
331                 this.Keyword2Color = value;
332                 this.ChangedRenderResource(this, new ChangedRenderRsourceEventArgs(ResourceType.Brush));
333             }
334         }
335
336         public float HeaderHeight
337         {
338             get { return (float)this._LineHeight; }
339         }
340
341         public float FooterHeight
342         {
343             get { return (float)this._LineHeight; }
344         }
345
346         public bool Printing
347         {
348             get;
349             set;
350         }
351
352         public bool ShowLineBreak
353         {
354             get;
355             set;
356         }
357
358         public event ChangedRenderResourceEventHandler ChangedRenderResource;
359
360         public void DrawCachedBitmap(Rectangle rect)
361         {
362             return;
363         }
364
365         public void CacheContent()
366         {
367             return;
368         }
369
370         public bool IsVaildCache()
371         {
372             return false;
373         }
374
375         public void SetDrawingContext(DrawingContext dc)
376         {
377             this.Context = dc;
378         }
379
380         public void BegineDraw()
381         {
382             this.visual = new DrawingVisual();
383             this.Context = this.visual.RenderOpen();
384         }
385
386         public void EndDraw()
387         {
388             this.Context.Close();
389             this.host.AddVisual(this.visual);
390         }
391
392         public bool Resize(double width, double height)
393         {
394             return true;
395         }
396
397         public void DrawString(string str, double x, double y, StringAlignment align, Size layoutRect, StringColorType colorType = StringColorType.Forground)
398         {
399             TextAlignment TextAlign = TextAlignment.Left;
400             switch (align)
401             {
402                 case StringAlignment.Left:
403                     TextAlign = TextAlignment.Left;
404                     break;
405                 case StringAlignment.Center:
406                     TextAlign = TextAlignment.Center;
407                     break;
408                 case StringAlignment.Right:
409                     TextAlign = TextAlignment.Right;
410                     break;
411             }
412             TextLayout layout = new TextLayout(str, this.FontFamily, this.FontSize, this.Brushes[this.ForegroundColor], layoutRect.Width, TextAlign);
413             layout.FlowDirection = this.RightToLeft ? FlowDirection.RightToLeft : FlowDirection.LeftToRight;
414             layout.Draw(this.Context, x, y);
415             layout.Dispose();
416         }
417
418         public void FillRectangle(Rectangle rect,FillRectType type)
419         {
420             if (this.Printing)
421                 return;
422             switch(type)
423             {
424                 case FillRectType.OverwriteCaret:
425                     this.Context.DrawRectangle(this.Brushes[this.OverwriteCaret], null, rect);
426                     break;
427                 case FillRectType.InsertCaret:
428                     this.Context.DrawRectangle(this.Brushes[this.InsertCaret], null, rect);
429                     break;
430                 case FillRectType.InsertPoint:
431                     break;
432                 case FillRectType.LineMarker:
433                     this.Context.DrawRectangle(this.Brushes[this.LineMarker], null, rect);
434                     break;
435             }
436         }
437
438         public void DrawFoldingMark(bool expand, double x, double y)
439         {
440             string str = expand ? "-" : "+";
441             TextLayout layout = new TextLayout(str, this.FontFamily, this.FontSize, this.Brushes[this.ForegroundColor], this.FoldingWidth);
442             layout.Draw(this.Context, x, y);
443         }
444
445         public void DrawLine(Point from, Point to)
446         {
447             Brush brush = this.Brushes[this.ForegroundColor];
448             Pen pen = this.Pens.Get(brush,HilightType.Sold);
449             this.Context.DrawLine(pen, from, to);
450         }
451
452         public void FillBackground(Rectangle rect)
453         {
454             if (this.Printing)
455                 return;
456             this.Context.DrawRectangle(this.Brushes[this.Background], null, rect);
457         }
458
459         public void DrawOneLine(Document doc, LineToIndexTable lti, int row, double x, double y)
460         {
461             TextLayout layout = (TextLayout)lti.GetLayout(row);
462
463             if (lti.GetLengthFromLineNumber(row) == 0)
464                 return;
465
466             if (this.Printing == false)
467             {
468                 int lineIndex = lti.GetIndexFromLineNumber(row);
469                 int lineLength = lti.GetLengthFromLineNumber(row);
470                 var SelectRanges = from s in doc.Selections.Get(lineIndex, lineLength)
471                                    let n = Util.ConvertAbsIndexToRelIndex(s, lineIndex, lineLength)
472                                    select n;
473
474                 foreach (Selection sel in SelectRanges)
475                 {
476                     if (sel.length == 0 || sel.start == -1)
477                         continue;
478
479                     foreach (TextBounds bound in layout.GetTextBounds(sel.start, sel.length))
480                     {
481                         Rect rect = new Rect(x, y, bound.Rectangle.Width, bound.Rectangle.Height);
482                         this.Context.DrawRectangle(this.Brushes[this.Hilight], null, rect);
483                     }
484                 }
485             }
486
487             layout.Draw(this.Context, x, y);
488         }
489
490         public void BeginClipRect(Rectangle rect)
491         {
492         }
493
494         public void EndClipRect()
495         {
496         }
497
498         public List<LineToIndexTableData> BreakLine(Document doc, LineToIndexTable layoutLineCollection, int startIndex, int endIndex, double wrapwidth)
499         {
500             List<LineToIndexTableData> output = new List<LineToIndexTableData>();
501
502             foreach (string str in doc.GetLines(startIndex, endIndex))
503             {
504                 TextLayout layout = new TextLayout(str, this.FontFamily, this.FontSize, this.Brushes[this.Foreground], wrapwidth);
505
506                 int i = startIndex;
507                 foreach (TextLine line in layout.Lines)
508                 {
509                     if (line.Length == 0 && line.NewlineLength == 0)
510                         continue;
511
512                     int length = line.Length;
513
514                     IList<TextSpan<TextRun>> runs = line.GetTextRunSpans();
515                     if (runs.Last().Value is TextEndOfParagraph)
516                         length--;
517
518                     bool lineend = false;
519                     if (line.NewlineLength > 0)
520                         lineend = true;
521
522                     output.Add(layoutLineCollection.CreateLineToIndexTableData(i, length, lineend, null));
523                     i += line.Length;
524                 }
525
526                 layout.Dispose();
527
528                 startIndex += str.Length;
529             }
530
531             if (output.Count > 0)
532                 output.Last().LineEnd = true;
533
534             return output;
535         }
536
537         public ITextLayout CreateLaytout(string str, SyntaxInfo[] syntaxCollection, IEnumerable<Marker> MarkerRanges, IEnumerable<Selection> SelectRanges)
538         {
539             TextLayout layout = new TextLayout(str,this.FontFamily,this.FontSize,Brushes[this.Foreground],this.TextArea.Width);
540             layout.TextWarpping = TextWrapping.NoWrap;
541             layout.FlowDirection = this.RightToLeft ? FlowDirection.RightToLeft : FlowDirection.LeftToRight;
542
543             if (syntaxCollection != null)
544             {
545                 foreach (SyntaxInfo s in syntaxCollection)
546                 {
547                     Brush brush = this.Brushes[this.Foreground];
548                     switch (s.type)
549                     {
550                         case TokenType.Comment:
551                             brush = this.Brushes[this.Comment];
552                             break;
553                         case TokenType.Keyword1:
554                             brush = this.Brushes[this.Keyword1];
555                             break;
556                         case TokenType.Keyword2:
557                             brush = this.Brushes[this.Keyword2];
558                             break;
559                         case TokenType.Literal:
560                             brush = this.Brushes[this.Literal];
561                             break;
562                     }
563                     TextEffect effect = new TextEffect(null, brush, null, s.index, s.length);
564                     effect.Freeze();
565                     layout.SetTextEffect(effect);
566                 }
567             }
568
569             if (MarkerRanges != null)
570             {
571                 foreach (Marker m in MarkerRanges)
572                 {
573                     if (m.start == -1 || m.length == 0)
574                         continue;
575
576                     Brush brush;
577                     if (m.hilight == HilightType.Url)
578                     {
579                         brush = this.Brushes[this.Url];
580                         TextEffect effect = new TextEffect(null, brush, null, m.start, m.length);
581                         effect.Freeze();
582                         layout.SetTextEffect(effect);
583                     }
584                     else
585                     {
586                         System.Windows.Media.Color color = new System.Windows.Media.Color();
587                         color.A = m.color.A;
588                         color.R = m.color.R;
589                         color.G = m.color.G;
590                         color.B = m.color.B;
591                         brush = this.Brushes[color];
592                     }
593
594                     Pen pen = this.Pens.Get(brush,m.hilight);
595
596                     TextDecorationCollection collection = new TextDecorationCollection();
597                     TextDecoration decoration = new TextDecoration();
598                     decoration.Pen = pen;
599                     decoration.Location = TextDecorationLocation.Underline;
600                     decoration.Freeze();
601                     collection.Add(decoration);
602
603                     if (m.hilight == HilightType.Squiggle)
604                         layout.SetSquilleLine(m.start, m.length, collection);
605                     else
606                         layout.SetTextDecoration(m.start, m.length, collection);
607                 }
608             }
609
610             return layout;
611         }
612
613         public void DrawGripper(Point p, double radius)
614         {
615             //タッチには対応していないので実装する必要はない
616             throw new NotImplementedException();
617         }
618
619         public void Dispose()
620         {
621             this.Pens = null;
622             this.Brushes = null;
623         }
624     }
625
626     class BrushStockes
627     {
628         ResourceManager<System.Windows.Media.Color, Brush> collection = new ResourceManager<System.Windows.Media.Color, Brush>();
629         public Brush this[System.Windows.Media.Color c]
630         {
631             get
632             {
633                 Brush brush;
634                 if (this.collection.TryGetValue(c, out brush))
635                     return brush;
636                 brush = new SolidColorBrush(c);
637                 brush.Freeze();
638                 this.collection.Add(c, brush);
639                 return brush;
640             }
641         }
642     }
643
644     class PenStockes
645     {
646         ResourceManager<Brush, ResourceManager<HilightType, Pen>> cache = new ResourceManager<Brush, ResourceManager<HilightType, Pen>>();
647         public Pen Get(Brush brush, HilightType type)
648         {
649             ResourceManager<HilightType, Pen> hilights;
650             Pen effect;
651             if (this.cache.TryGetValue(brush, out hilights))
652             {
653                 if (hilights.TryGetValue(type, out effect))
654                     return effect;
655                 effect = CreatePen(brush,type);
656                 hilights.Add(type, effect);
657                 return effect;
658             }
659             effect = CreatePen(brush, type);
660             hilights = new ResourceManager<HilightType, Pen>();
661             hilights.Add(type, effect);
662             this.cache.Add(brush, hilights);
663             return effect;
664         }
665         Pen CreatePen(Brush brush, HilightType type)
666         {
667             Pen pen = new Pen(brush, 1.0f);
668             switch (type)
669             {
670                 case HilightType.Sold:
671                 case HilightType.Url:
672                 case HilightType.Squiggle:
673                     pen.DashStyle = DashStyles.Solid;
674                     break;
675                 case HilightType.Dash:
676                     pen.DashStyle = DashStyles.Dash;
677                     break;
678                 case HilightType.DashDot:
679                     pen.DashStyle = DashStyles.DashDot;
680                     break;
681                 case HilightType.DashDotDot:
682                     pen.DashStyle = DashStyles.DashDotDot;
683                     break;
684                 case HilightType.Dot:
685                     pen.DashStyle = DashStyles.Dot;
686                     break;
687             }
688             pen.Freeze();
689             return pen;
690         }
691     }
692 }