OSDN Git Service

ファイル読み込み後の再描写はすぐ行わなくても構わない
[fooeditengine/FooEditEngine.git] / UWP / FooEditEngine.UWP / FooTextBox.cs
index c4172c1..71da92f 100644 (file)
@@ -55,16 +55,20 @@ namespace FooEditEngine.UWP
 #endif
         bool nowCaretMove = false;
         bool nowCompstion = false;
+        bool requestSizeChange = false;
         Document _Document;
         DispatcherTimer timer = new DispatcherTimer();
 
+        const int Interval = 32;
+        const int IntervalWhenLostFocus = 160;
+
         /// <summary>
         /// コンストラクター
         /// </summary>
         public FooTextBox()
         {
             this.DefaultStyleKey = typeof(FooTextBox);
-
+            
             this.rectangle = new Windows.UI.Xaml.Shapes.Rectangle();
             this.rectangle.Margin = this.Padding;
 #if !DUMMY_RENDER
@@ -75,7 +79,7 @@ namespace FooEditEngine.UWP
 
             this.Document = new Document();
 
-            this.View = new EditView(this.Document, this.Render, new Padding(5, Gripper.HitAreaWidth, Gripper.HitAreaWidth / 2, Gripper.HitAreaWidth));
+            this.View = new EditView(this.Document, this.Render, new Padding(5, 5, Gripper.HitAreaWidth / 2, Gripper.HitAreaWidth));
             this.View.SrcChanged += View_SrcChanged;
             this.View.InsertMode = this.InsertMode;
             this.Document.DrawLineNumber = this.DrawLineNumber;
@@ -104,10 +108,13 @@ namespace FooEditEngine.UWP
             this.gestureRecongnizer.ManipulationUpdated += gestureRecongnizer_ManipulationUpdated;
             this.gestureRecongnizer.ManipulationCompleted += gestureRecongnizer_ManipulationCompleted;
 
-            this.timer.Interval = new TimeSpan(0, 0, 0, 0, 500);
+            this.timer.Interval = new TimeSpan(0, 0, 0, 0, Interval);
             this.timer.Tick += this.timer_Tick;
             this.timer.Start();
 
+            this.GettingFocus += FooTextBox_GettingFocus;
+            this.LosingFocus += FooTextBox_LosingFocus;
+
             this.SizeChanged += FooTextBox_SizeChanged;
 
             this.Loaded += FooTextBox_Loaded;
@@ -214,7 +221,13 @@ namespace FooEditEngine.UWP
             var dataPackageView = Clipboard.GetContent();
             if (dataPackageView.Contains(StandardDataFormats.Text))
             {
-                this._Controller.SelectedText = await dataPackageView.GetTextAsync();
+                try
+                {
+                    this._Controller.SelectedText = await dataPackageView.GetTextAsync();
+                }catch(Exception e)
+                {
+                    System.Diagnostics.Debug.WriteLine("past error:" + e.Message);
+                }
             }
         }
 
@@ -301,9 +314,12 @@ namespace FooEditEngine.UWP
         /// <summary>
         /// 再描写する
         /// </summary>
-        public void Refresh()
+        public void Refresh(bool immidately=true)
         {
-            this.Refresh(this.View.PageBound);
+            if(immidately)
+                this.Refresh(this.View.PageBound);
+            else
+                this.Document.RequestRedraw();
         }
 
         /// <summary>
@@ -311,7 +327,7 @@ namespace FooEditEngine.UWP
         /// </summary>
         public void PerfomLayouts()
         {
-            this.View.PerfomLayouts();
+            this.Document.PerformLayout();
         }
 
         /// <summary>
@@ -331,23 +347,29 @@ namespace FooEditEngine.UWP
         /// <returns>Taskオブジェクト</returns>
         public async Task LoadFileAsync(System.IO.StreamReader sr, System.Threading.CancellationTokenSource token)
         {
-            this.IsEnabled = false;
-            this.View.LayoutLines.IsFrozneDirtyFlag = true;
-            WinFileReader fs = new WinFileReader(sr);
-            await this.Document.LoadAsync(fs, token);
-            this.View.LayoutLines.IsFrozneDirtyFlag = false;
+            await this.Document.LoadAsync(sr, token);
+        }
 
-            CoreTextRange modified_range = new CoreTextRange();
-            modified_range.StartCaretPosition = 0;
-            modified_range.EndCaretPosition = 0;
-            //キャレット位置はロード前と同じにしないと違和感を感じる
-            if(this.textEditContext != null)
-                this.textEditContext.NotifyTextChanged(modified_range, this.Document.Length, modified_range);
+        private void Document_LoadProgress(object sender, ProgressEventArgs e)
+        {
+            if(e.state == ProgressState.Start)
+            {
+                this.IsEnabled = false;
+            }
+            else if(e.state == ProgressState.Complete)
+            {
+                CoreTextRange modified_range = new CoreTextRange();
+                modified_range.StartCaretPosition = 0;
+                modified_range.EndCaretPosition = 0;
+                //キャレット位置はロード前と同じにしないと違和感を感じる
+                if (this.textEditContext != null)
+                    this.textEditContext.NotifyTextChanged(modified_range, this.Document.Length, modified_range);
 
-            if (this.verticalScrollBar != null)
-                this.verticalScrollBar.Maximum = this.View.LayoutLines.Count;
-            this.View.CalculateLineCountOnScreen();
-            this.IsEnabled = true;
+                if (this.verticalScrollBar != null)
+                    this.verticalScrollBar.Maximum = this.View.LayoutLines.Count;
+                this.IsEnabled = true;
+                this.Refresh(false);
+            }
         }
 
         /// <summary>
@@ -358,8 +380,7 @@ namespace FooEditEngine.UWP
         /// <returns>Taskオブジェクト</returns>
         public async Task SaveFile(System.IO.StreamWriter sw, System.Threading.CancellationTokenSource token)
         {
-            WinFileWriter fs = new WinFileWriter(sw);
-            await this.Document.SaveAsync(fs, token);
+            await this.Document.SaveAsync(sw, token);
         }
 
 #region command
@@ -423,12 +444,23 @@ namespace FooEditEngine.UWP
             }
         }
 
-            /// <inheritdoc/>
-        protected override async void OnGotFocus(RoutedEventArgs e)
+        private void FooTextBox_LosingFocus(UIElement sender, LosingFocusEventArgs args)
         {
-            base.OnGotFocus(e);
+            this.RemoveTextContext();
+            
+            if (this.textServiceManager != null)
+            {
+                this.textServiceManager.InputLanguageChanged -= TextServiceManager_InputLanguageChanged;
+                this.textServiceManager = null;
+            }
+
+            System.Diagnostics.Debug.WriteLine("losing focus");
+        }
 
-            if(this.textServiceManager == null)
+        private async void FooTextBox_GettingFocus(UIElement sender, GettingFocusEventArgs args)
+        {
+            System.Diagnostics.Debug.WriteLine("getting focus");
+            if (this.textServiceManager == null)
             {
                 await Task.Delay(500);
                 this.textServiceManager = CoreTextServicesManager.GetForCurrentView();
@@ -436,27 +468,35 @@ namespace FooEditEngine.UWP
             }
 
             this.CreateTextContext();
+        }
+
+        /// <inheritdoc/>
+        protected override void OnGotFocus(RoutedEventArgs e)
+        {
+            base.OnGotFocus(e);
+
+            System.Diagnostics.Debug.WriteLine("got focus");
 
             this.View.IsFocused = true;
-            this.Refresh();
+            this.timer.Interval = new TimeSpan(0, 0, 0, 0, Interval);
+            this.Refresh(false);
         }
 
         private void TextServiceManager_InputLanguageChanged(CoreTextServicesManager sender, object args)
         {
             System.Diagnostics.Debug.WriteLine("input language changed input script:"+  this.textServiceManager.InputLanguage.Script);
-            this.RemoveTextContext();
-            this.CreateTextContext();
         }
 
-        /// <inheritdoc/>
+       /// <inheritdoc/>
         protected override void OnLostFocus(RoutedEventArgs e)
         {
             base.OnLostFocus(e);
 
-            this.RemoveTextContext();
-
+            System.Diagnostics.Debug.WriteLine("lost focus");
+            
             this.View.IsFocused = false;
-            this.Refresh();
+            this.Refresh(false);
+            this.timer.Interval = new TimeSpan(0, 0, 0, 0, IntervalWhenLostFocus);
         }
 
         private void CreateTextContext()
@@ -464,6 +504,7 @@ namespace FooEditEngine.UWP
             if(this.textServiceManager != null)
             {
                 this.textEditContext = this.textServiceManager.CreateEditContext();
+                this.textEditContext.InputScope = CoreTextInputScope.Text;
                 this.textEditContext.CompositionStarted += TextEditContext_CompositionStarted;
                 this.textEditContext.CompositionCompleted += TextEditContext_CompositionCompleted;
                 this.textEditContext.LayoutRequested += TextEditContext_LayoutRequested;
@@ -482,6 +523,7 @@ namespace FooEditEngine.UWP
         {
             if(this.textEditContext != null)
             {
+                this.textEditContext.NotifyFocusLeave();
                 this.textEditContext.CompositionStarted -= TextEditContext_CompositionStarted;
                 this.textEditContext.CompositionCompleted -= TextEditContext_CompositionCompleted;
                 this.textEditContext.LayoutRequested -= TextEditContext_LayoutRequested;
@@ -492,7 +534,6 @@ namespace FooEditEngine.UWP
                 this.textEditContext.FormatUpdating -= TextEditContext_FormatUpdating;
                 this.textEditContext.FocusRemoved -= TextEditContext_FocusRemoved;
                 this.textEditContext.NotifyFocusLeaveCompleted -= TextEditContext_NotifyFocusLeaveCompleted;
-                this.textEditContext.NotifyFocusLeave();
                 this.textEditContext = null;
             }
         }
@@ -503,6 +544,11 @@ namespace FooEditEngine.UWP
             bool isControlPressed = this.IsModiferKeyPressed(VirtualKey.Control);
             bool isShiftPressed = this.IsModiferKeyPressed(VirtualKey.Shift);
             bool isMovedCaret = false;
+
+            var autocomplete = this.Document.AutoComplete as AutoCompleteBox;
+            if (autocomplete != null && autocomplete.ProcessKeyDown(this, e, isControlPressed, isShiftPressed))
+                return;
+
             switch (e.Key)
             {
                 case VirtualKey.Up:
@@ -658,6 +704,7 @@ namespace FooEditEngine.UWP
         /// <inheritdoc/>
         protected override void OnPointerPressed(PointerRoutedEventArgs e)
         {
+            System.Diagnostics.Debug.WriteLine("pointer pressed");
             this.CapturePointer(e.Pointer);
             this.gestureRecongnizer.ProcessDownEvent(e.GetCurrentPoint(this));
             e.Handled = true;
@@ -666,7 +713,15 @@ namespace FooEditEngine.UWP
         /// <inheritdoc/>
         protected override void OnPointerMoved(PointerRoutedEventArgs e)
         {
-            this.gestureRecongnizer.ProcessMoveEvents(e.GetIntermediatePoints(this));
+            System.Diagnostics.Debug.WriteLine("pointer moved");
+            try
+            {
+                this.gestureRecongnizer.ProcessMoveEvents(e.GetIntermediatePoints(this));
+            }catch(System.Runtime.InteropServices.COMException ex)
+            {
+                //ピンチズームでこの例外が発生するが、回避できない
+                System.Diagnostics.Debug.WriteLine("expection:" + ex);
+            }
             e.Handled = true;
 
             if (e.Pointer.PointerDeviceType == PointerDeviceType.Mouse)
@@ -690,6 +745,7 @@ namespace FooEditEngine.UWP
         /// <inheritdoc/>
         protected override void OnPointerReleased(PointerRoutedEventArgs e)
         {
+            System.Diagnostics.Debug.WriteLine("pointer released");
             this.gestureRecongnizer.ProcessUpEvent(e.GetCurrentPoint(this));
             e.Handled = true;
         }
@@ -697,6 +753,7 @@ namespace FooEditEngine.UWP
         /// <inheritdoc/>
         protected override void OnPointerCanceled(PointerRoutedEventArgs e)
         {
+            System.Diagnostics.Debug.WriteLine("pointer canceled");
             this.gestureRecongnizer.CompleteGesture();
             e.Handled = true;
         }
@@ -704,6 +761,7 @@ namespace FooEditEngine.UWP
         /// <inheritdoc/>
         protected override void OnPointerWheelChanged(PointerRoutedEventArgs e)
         {
+            System.Diagnostics.Debug.WriteLine("pointer wheelchanged");
             bool shift = (e.KeyModifiers & Windows.System.VirtualKeyModifiers.Shift) ==
                 Windows.System.VirtualKeyModifiers.Shift;
             bool ctrl = (e.KeyModifiers & Windows.System.VirtualKeyModifiers.Control) ==
@@ -776,14 +834,16 @@ namespace FooEditEngine.UWP
             }
 
             int start = req.Range.StartCaretPosition;
-            int length = req.Range.EndCaretPosition - req.Range.StartCaretPosition;
-            if (length > this.Document.Length)
-                length = this.Document.Length;
+            int end = req.Range.EndCaretPosition;
+            if (end > this.Document.Length)
+                end = this.Document.Length;
+
+            int length = end - start;
 
             System.Diagnostics.Debug.WriteLine("req text start:{0} length:{1}", start, length);
 
             //キャレット位置も含むので+1する必要はない
-            req.Text = this.Document.ToString(start, length);
+            req.Text = this.Document.ToString(start,length);
         }
 
         private void TextEditContext_LayoutRequested(CoreTextEditContext sender, CoreTextLayoutRequestedEventArgs args)
@@ -812,7 +872,7 @@ namespace FooEditEngine.UWP
                 args.Request.LayoutBounds.TextBounds = new Rect(
                     screenStartPos.X,
                     screenStartPos.Y,
-                    screenEndPos.X - screenStartPos.X,
+                    Math.Max(0,screenEndPos.X - screenStartPos.X),  //折り返されている場合、負になることがある
                     screenEndPos.Y - screenStartPos.Y);
             }
 
@@ -949,16 +1009,6 @@ namespace FooEditEngine.UWP
 
         void gestureRecongnizer_ManipulationUpdated(GestureRecognizer sender, ManipulationUpdatedEventArgs e)
         {
-            if (this._Controller.MoveCaretAndGripper(e.Position, this.hittedGripper))
-            {
-#if ENABLE_AUTMATION
-                if (this.peer != null)
-                    this.peer.OnNotifyCaretChanged();
-#endif
-                this.Refresh();                
-                return;
-            }
-
             if (e.Delta.Scale < 1)
             {
                 double newSize = this.Render.FontSize - 1;
@@ -969,7 +1019,7 @@ namespace FooEditEngine.UWP
                 SetValue(MagnificationPowerPropertyKey, this.Render.FontSize / this.FontSize);
                 return;
             }
-            
+
             if (e.Delta.Scale > 1)
             {
                 double newSize = this.Render.FontSize + 1;
@@ -980,6 +1030,16 @@ namespace FooEditEngine.UWP
                 SetValue(MagnificationPowerPropertyKey, this.Render.FontSize / this.FontSize);
                 return;
             }
+
+            if (this._Controller.MoveCaretAndGripper(e.Position, this.hittedGripper))
+            {
+#if ENABLE_AUTMATION
+                if (this.peer != null)
+                    this.peer.OnNotifyCaretChanged();
+#endif
+                this.Refresh();                
+                return;
+            }
             
             Point translation = e.Delta.Translation;
 
@@ -1048,22 +1108,36 @@ namespace FooEditEngine.UWP
                             this._Controller.RectSelection = true;
                         }));
                     }
-                    await ContextMenu.ShowAsync(e.Position);
+                    var windowStartPos = Util.GetPointInWindow(e.Position, this);
+                    await ContextMenu.ShowAsync(windowStartPos);
                 }
             }
         }
 
+        long lastDouleTapTick;
+        const long allowTripleTapTimeSpan = 500;
         void gestureRecongnizer_Tapped(GestureRecognizer sender, TappedEventArgs e)
         {
             bool touched = e.PointerDeviceType == PointerDeviceType.Touch;
             this.Document.SelectGrippers.BottomLeft.Enabled = false;
             this.Document.SelectGrippers.BottomRight.Enabled = touched;
             this.JumpCaret(e.Position);
-            if (e.TapCount == 2)
+            if(e.TapCount == 1 && System.DateTime.Now.Ticks - lastDouleTapTick < allowTripleTapTimeSpan * 10000)    //トリプルタップ
             {
+                //タッチスクリーンで行選択した場合、アンカーインデックスを単語の先頭にしないとバグる
                 this.Document.SelectGrippers.BottomLeft.Enabled = touched;
-                //タッチスクリーンでダブルタップした場合、アンカーインデックスを単語の先頭にしないとバグる
-                this.Document.SelectWord(this.Controller.SelectionStart, touched);
+                this.Document.SelectLine(this.Controller.SelectionStart, touched);
+                this.Refresh();
+            }
+            else  if(e.TapCount == 2)   //ダブルタップ
+            {
+                //タッチスクリーンで単語選択した場合、アンカーインデックスを単語の先頭にしないとバグる
+                this.Document.SelectGrippers.BottomLeft.Enabled = touched;
+                if (e.Position.X < this.Render.TextArea.X)
+                    this.Document.SelectLine(this.Controller.SelectionStart, touched);
+                else
+                    this.Document.SelectWord(this.Controller.SelectionStart, touched);
+                this.lastDouleTapTick = System.DateTime.Now.Ticks;
                 this.Refresh();
             }
         }
@@ -1109,7 +1183,7 @@ namespace FooEditEngine.UWP
             else
                 return;
             TextPoint tp = this.View.GetTextPointFromPostion(p, searchRange);
-            this._Controller.MoveCaretAndSelect(tp);
+            this._Controller.MoveCaretAndSelect(tp, this.IsModiferKeyPressed(VirtualKey.LeftControl));
 #if ENABLE_AUTMATION
             if (this.peer != null)
                 this.peer.OnNotifyCaretChanged();
@@ -1128,6 +1202,8 @@ namespace FooEditEngine.UWP
                 return;
 
             this.Render.DrawContent(this.View, this.IsEnabled, updateRect);
+
+            this.Document.IsRequestRedraw = false;
         }
 
 
@@ -1172,11 +1248,8 @@ namespace FooEditEngine.UWP
 
         void FooTextBox_SizeChanged(object sender, SizeChangedEventArgs e)
         {
-            if (this.Resize(this.rectangle.ActualWidth, this.rectangle.ActualHeight))
-            {
-                this.Refresh();
-                return;
-            }
+            //LostFocusやGotFocusなどと競合するとDirect2Dでエラーが起きるので、timer_tickイベントでサイズ変更を行うことにした
+            this.requestSizeChange = true;
         }
 
         void horizontalScrollBar_Scroll(object sender, ScrollEventArgs e)
@@ -1245,8 +1318,21 @@ namespace FooEditEngine.UWP
 
         void timer_Tick(object sender, object e)
         {
-            if (this.View.LayoutLines.HilightAll() || this.View.LayoutLines.GenerateFolding())
+            this.timer.Stop();
+            if(this.requestSizeChange)
+            {
+                if (this.Resize(this.rectangle.ActualWidth, this.rectangle.ActualHeight))
+                {
+                    //普通に再描写するとちらつく
+                    this.Refresh(this.View.PageBound);
+                }
+                this.requestSizeChange = false;
+            }
+            else if (this.View.LayoutLines.HilightAll() || this.View.LayoutLines.GenerateFolding() || this.Document.IsRequestRedraw)
+            {
                 this.Refresh(this.View.PageBound);
+            }
+            this.timer.Start();
         }
 
         private void SetDocument(Document value)
@@ -1260,6 +1346,13 @@ namespace FooEditEngine.UWP
             {
                 old_doc.Update -= new DocumentUpdateEventHandler(Document_Update);
                 this._Document.SelectionChanged -= Controller_SelectionChanged;
+                this._Document.LoadProgress -= Document_LoadProgress;
+                this._Document.AutoCompleteChanged -= _Document_AutoCompleteChanged;
+                if (this._Document.AutoComplete != null)
+                {
+                    this._Document.AutoComplete.GetPostion = null;
+                    this._Document.AutoComplete = null;
+                }
 
                 //NotifyTextChanged()を呼び出すと落ちるのでTextConextをごっそり作り替える
                 this.RemoveTextContext();
@@ -1270,6 +1363,10 @@ namespace FooEditEngine.UWP
             this._Document = value;
             this._Document.LayoutLines.Render = this.Render;
             this._Document.Update += new DocumentUpdateEventHandler(Document_Update);
+            this._Document.LoadProgress += Document_LoadProgress;
+            this._Document.AutoCompleteChanged += _Document_AutoCompleteChanged;
+            if (this._Document.AutoComplete != null && this._Document.AutoComplete.GetPostion == null)
+                this._Document_AutoCompleteChanged(this._Document, null);
             //初期化が終わっていればすべて存在する
             if (this.Controller != null && this.View != null)
             {
@@ -1302,6 +1399,24 @@ namespace FooEditEngine.UWP
             this._Document.SelectionChanged += Controller_SelectionChanged;
         }
 
+        private void _Document_AutoCompleteChanged(object sender, EventArgs e)
+        {
+            Document doc = (Document)sender;
+            doc.AutoComplete.GetPostion = (tp, e_doc) =>
+            {
+                var p = this.View.GetPostionFromTextPoint(tp);
+                int height = (int)e_doc.LayoutLines.GetLayout(e_doc.CaretPostion.row).Height;
+
+                if (p.Y + AutoCompleteBox.CompleteListBoxHeight + height > e_doc.LayoutLines.Render.TextArea.Height)
+                    p.Y -= AutoCompleteBox.CompleteListBoxHeight;
+                else
+                    p.Y += height;
+                //AutoCompleteBox内ではCanvasで位置を指定しているので変換する必要がある
+                var pointInWindow = Util.GetPointInWindow(p, this);
+                return pointInWindow;
+            };
+        }
+
         /// <inheritdoc/>
         public static void OnPropertyChanged(object sender, DependencyPropertyChangedEventArgs e)
         {
@@ -1364,6 +1479,8 @@ namespace FooEditEngine.UWP
                 source.Render.FontSize = source.FontSize;
             if (e.Property.Equals(ForegroundProperty))
                 source.Render.Foreground = D2DRenderBase.ToColor4(source.Foreground);
+            if (e.Property.Equals(HilightForegroundProperty))
+                source.Render.HilightForeground = D2DRenderBase.ToColor4(source.HilightForeground);
             if (e.Property.Equals(BackgroundProperty))
                 source.Render.Background = D2DRenderBase.ToColor4(source.Background);
             if (e.Property.Equals(ControlCharProperty))
@@ -1670,6 +1787,21 @@ namespace FooEditEngine.UWP
             DependencyProperty.Register("Foreground", typeof(Windows.UI.Color), typeof(FooTextBox), new PropertyMetadata(Colors.Black, OnPropertyChanged));
 
         /// <summary>
+        /// 選択時の文字色を表す。これは依存プロパティです
+        /// </summary>
+        public Windows.UI.Color HilightForeground
+        {
+            get { return (Windows.UI.Color)GetValue(HilightForegroundProperty); }
+            set { SetValue(HilightForegroundProperty, value); }
+        }
+
+        /// <summary>
+        /// HilightForegroundForegroundの依存プロパティを表す
+        /// </summary>
+        public static readonly DependencyProperty HilightForegroundProperty =
+            DependencyProperty.Register("HilightForeground", typeof(Windows.UI.Color), typeof(FooTextBox), new PropertyMetadata(Colors.White, OnPropertyChanged));
+
+        /// <summary>
         /// 背景色を表す。これは依存プロパティです
         /// </summary>
         public new Windows.UI.Color Background
@@ -1712,7 +1844,7 @@ namespace FooEditEngine.UWP
         /// Hilightの依存プロパティを表す
         /// </summary>
         public static readonly DependencyProperty HilightProperty =
-            DependencyProperty.Register("Hilight", typeof(Windows.UI.Color), typeof(FooTextBox), new PropertyMetadata(Colors.DeepSkyBlue, OnPropertyChanged));
+            DependencyProperty.Register("Hilight", typeof(Windows.UI.Color), typeof(FooTextBox), new PropertyMetadata(Colors.DodgerBlue, OnPropertyChanged));
 
         /// <summary>
         /// キーワード1の文字色を表す。これは依存プロパティです