2021-03-31 16:42:58 -05:00
|
|
|
// Decompiled with JetBrains decompiler
|
|
|
|
|
// Type: Microsoft.Iris.InputHandlers.TextEditingHandler
|
|
|
|
|
// Assembly: UIX, Version=4.8.0.0, Culture=neutral, PublicKeyToken=ddd0da4d3e678217
|
|
|
|
|
// MVID: A56C6C9D-B7F6-46A9-8BDE-B3D9B8D60B11
|
|
|
|
|
// Assembly location: C:\Program Files\Zune\UIX.dll
|
|
|
|
|
|
|
|
|
|
using Microsoft.Iris.Drawing;
|
|
|
|
|
using Microsoft.Iris.Input;
|
|
|
|
|
using Microsoft.Iris.Layout;
|
|
|
|
|
using Microsoft.Iris.Markup;
|
|
|
|
|
using Microsoft.Iris.ModelItems;
|
|
|
|
|
using Microsoft.Iris.OS;
|
|
|
|
|
using Microsoft.Iris.Render;
|
|
|
|
|
using Microsoft.Iris.RenderAPI;
|
|
|
|
|
using Microsoft.Iris.Session;
|
|
|
|
|
using Microsoft.Iris.UI;
|
|
|
|
|
using Microsoft.Iris.ViewItems;
|
|
|
|
|
using System;
|
|
|
|
|
|
|
|
|
|
namespace Microsoft.Iris.InputHandlers
|
|
|
|
|
{
|
|
|
|
|
internal class TextEditingHandler :
|
|
|
|
|
InputHandler,
|
|
|
|
|
IRichTextCallbacks,
|
|
|
|
|
ITextScrollModelCallback,
|
|
|
|
|
IImeCallbacks
|
|
|
|
|
{
|
|
|
|
|
private const uint WM_LBUTTONDOWN = 513;
|
|
|
|
|
private const uint WM_LBUTTONUP = 514;
|
|
|
|
|
private TextEditingHandler.TextEditingCommand _copyCommand;
|
|
|
|
|
private TextEditingHandler.TextEditingCommand _cutCommand;
|
|
|
|
|
private TextEditingHandler.TextEditingCommand _deleteCommand;
|
|
|
|
|
private TextEditingHandler.TextPasteCommand _pasteCommand;
|
|
|
|
|
private TextEditingHandler.TextEditingCommand _selectAllCommand;
|
|
|
|
|
private TextEditingHandler.TextEditingCommand _undoCommand;
|
|
|
|
|
private uint _bits;
|
|
|
|
|
private Range _selection;
|
|
|
|
|
private Point _inputOffset;
|
|
|
|
|
private int _savedMouseYPositionToWorkAroundRichEditBug;
|
|
|
|
|
private EditableTextData _editData;
|
|
|
|
|
private EventHandler _maxLengthChangedHandler;
|
|
|
|
|
private EventHandler _readOnlyChangedHandler;
|
|
|
|
|
private EventHandler _valueChangedHandler;
|
|
|
|
|
private EventHandler _activationStateHandler;
|
|
|
|
|
private Form _activationStateNotifier;
|
|
|
|
|
private Text _textDisplay;
|
|
|
|
|
private CaretInfo _caretInfo;
|
|
|
|
|
private RichText _editControl;
|
|
|
|
|
private CursorID _cursor;
|
|
|
|
|
private TextScrollModel.State _pendingHorizontalScrollState;
|
|
|
|
|
private TextScrollModel.State _pendingVerticalScrollState;
|
|
|
|
|
private TextScrollModel _horizontalScrollModel;
|
|
|
|
|
private TextScrollModel _verticalScrollModel;
|
|
|
|
|
private SimpleCallback _updateScrollbars;
|
|
|
|
|
private InputInfo _pendingPointerDown;
|
|
|
|
|
private Color _linkColor;
|
|
|
|
|
private string _linkClickedParameter;
|
|
|
|
|
private bool _InImeMode;
|
|
|
|
|
private uint _ImeCallbackToken;
|
|
|
|
|
|
|
|
|
|
public TextEditingHandler()
|
|
|
|
|
{
|
|
|
|
|
this._caretInfo = new CaretInfo();
|
2021-03-31 17:15:06 -05:00
|
|
|
this._editControl = new RichText(true, this);
|
2021-03-31 16:42:58 -05:00
|
|
|
this._maxLengthChangedHandler = new EventHandler(this.OnEditableTextMaxLengthChanged);
|
|
|
|
|
this._readOnlyChangedHandler = new EventHandler(this.OnEditableTextReadOnlyChanged);
|
|
|
|
|
this._valueChangedHandler = new EventHandler(this.OnEditableTextValueChanged);
|
|
|
|
|
this._activationStateHandler = new EventHandler(this.OnActivationChanged);
|
2021-03-31 17:20:20 -05:00
|
|
|
this.SetBit(Bits.AcceptsEnter);
|
2021-03-31 16:42:58 -05:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
protected override void OnDispose()
|
|
|
|
|
{
|
|
|
|
|
base.OnDispose();
|
|
|
|
|
this.RemoveEditableTextEventHandlers();
|
|
|
|
|
this.UnregisterImeMessageHandler();
|
|
|
|
|
if (this._activationStateNotifier != null)
|
|
|
|
|
this.UpdateActivationStateHandler(false);
|
2021-03-31 17:15:06 -05:00
|
|
|
this.HorizontalScrollModel = null;
|
|
|
|
|
this.VerticalScrollModel = null;
|
2021-03-31 16:42:58 -05:00
|
|
|
this._editControl.Dispose();
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
protected override void ConfigureInteractivity()
|
|
|
|
|
{
|
|
|
|
|
base.ConfigureInteractivity();
|
|
|
|
|
if (!this.HandleDirect)
|
|
|
|
|
return;
|
|
|
|
|
this.UI.MouseInteractive = true;
|
|
|
|
|
this.UI.KeyInteractive = true;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
public override void OnZoneAttached()
|
|
|
|
|
{
|
|
|
|
|
if (!this.UI.DirectKeyFocus)
|
|
|
|
|
return;
|
|
|
|
|
this.UpdateActivationStateHandler(true);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
private void OnActivationChanged(object sender, EventArgs ea)
|
|
|
|
|
{
|
|
|
|
|
this.WindowIsActivated = this._activationStateNotifier.ActivationState;
|
|
|
|
|
this.UpdateCaretVisibility();
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
public EditableTextData EditableTextData
|
|
|
|
|
{
|
|
|
|
|
get => this._editData;
|
|
|
|
|
set
|
|
|
|
|
{
|
|
|
|
|
if (this._editData == value)
|
|
|
|
|
return;
|
|
|
|
|
this.RemoveEditableTextEventHandlers();
|
|
|
|
|
this._editData = value;
|
|
|
|
|
if (this._editData != null)
|
|
|
|
|
{
|
|
|
|
|
this.AddEditableTextEventHandlers();
|
|
|
|
|
this.UpdateMaxLengthOnRichEdit();
|
|
|
|
|
this.UpdateReadOnlyOnRichEdit();
|
|
|
|
|
this.UpdateContentOnRichEdit();
|
|
|
|
|
}
|
|
|
|
|
this.FireThreadSafeNotification(NotificationID.EditableTextData);
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
private void RemoveEditableTextEventHandlers()
|
|
|
|
|
{
|
|
|
|
|
if (this._editData == null)
|
|
|
|
|
return;
|
|
|
|
|
this._editData.MaxLengthChanged -= this._maxLengthChangedHandler;
|
|
|
|
|
this._editData.ReadOnlyChanged -= this._readOnlyChangedHandler;
|
|
|
|
|
this._editData.ValueChanged -= this._valueChangedHandler;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
private void AddEditableTextEventHandlers()
|
|
|
|
|
{
|
|
|
|
|
this._editData.MaxLengthChanged += this._maxLengthChangedHandler;
|
|
|
|
|
this._editData.ReadOnlyChanged += this._readOnlyChangedHandler;
|
|
|
|
|
this._editData.ValueChanged += this._valueChangedHandler;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
private void UnregisterImeMessageHandler() => RendererApi.IFC(NativeApi.SpUnregisterImeCallbacks(this._ImeCallbackToken));
|
|
|
|
|
|
|
|
|
|
private void OnEditableTextMaxLengthChanged(object sender, EventArgs unused) => this.UpdateMaxLengthOnRichEdit();
|
|
|
|
|
|
|
|
|
|
private void OnEditableTextReadOnlyChanged(object sender, EventArgs unused) => this.UpdateReadOnlyOnRichEdit();
|
|
|
|
|
|
|
|
|
|
private void OnEditableTextValueChanged(object sender, EventArgs unused)
|
|
|
|
|
{
|
|
|
|
|
if (this.InsideValueChangeOnEditableTextData)
|
|
|
|
|
return;
|
|
|
|
|
this.UpdateContentOnRichEdit();
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
private void UpdateContentOnRichEdit()
|
|
|
|
|
{
|
|
|
|
|
if (this._editData == null)
|
|
|
|
|
return;
|
|
|
|
|
this.InsideContentChangeOnRichEdit = true;
|
|
|
|
|
this._editControl.Content = this._editData.Value;
|
|
|
|
|
if (this._textDisplay != null)
|
|
|
|
|
this._textDisplay.MarkScaleDirty();
|
|
|
|
|
this.InsideContentChangeOnRichEdit = false;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
private void UpdateReadOnlyOnRichEdit()
|
|
|
|
|
{
|
|
|
|
|
this._editControl.ReadOnly = this._editData.ReadOnly;
|
|
|
|
|
this.UpdateSelectionAndReadOnlyCommands();
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
private void UpdateMaxLengthOnRichEdit() => this._editControl.MaxLength = this._editData.MaxLength;
|
|
|
|
|
|
|
|
|
|
public Text TextDisplay
|
|
|
|
|
{
|
|
|
|
|
get => this._textDisplay;
|
|
|
|
|
set
|
|
|
|
|
{
|
|
|
|
|
if (this._textDisplay == value)
|
|
|
|
|
return;
|
|
|
|
|
LayoutCompleteEventHandler completeEventHandler = new LayoutCompleteEventHandler(this.TextLayoutComplete);
|
|
|
|
|
if (this._textDisplay != null)
|
|
|
|
|
{
|
2021-03-31 17:15:06 -05:00
|
|
|
this._textDisplay.ExternalRasterizer = null;
|
|
|
|
|
this._textDisplay.ExternalEditingHandler = null;
|
2021-03-31 16:42:58 -05:00
|
|
|
this._textDisplay.LayoutComplete -= completeEventHandler;
|
|
|
|
|
}
|
|
|
|
|
this._textDisplay = value;
|
|
|
|
|
if (this._textDisplay != null)
|
|
|
|
|
{
|
|
|
|
|
this._textDisplay.ExternalRasterizer = this._editControl;
|
|
|
|
|
this._textDisplay.ExternalEditingHandler = this;
|
|
|
|
|
this._textDisplay.OnDisplayedContentChange();
|
|
|
|
|
this._textDisplay.LayoutComplete += completeEventHandler;
|
|
|
|
|
this.InputOffsetDirty = true;
|
|
|
|
|
}
|
|
|
|
|
this.FireThreadSafeNotification(NotificationID.TextDisplay);
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
private void TextLayoutComplete(object sender) => this.InputOffsetDirty = true;
|
|
|
|
|
|
|
|
|
|
private void FireTypingInputRejectedEvent() => this.FireThreadSafeNotification(NotificationID.TypingInputRejected);
|
|
|
|
|
|
|
|
|
|
public bool DetectUrls
|
|
|
|
|
{
|
2021-03-31 17:20:20 -05:00
|
|
|
get => this.GetBit(Bits.DetectUrls);
|
2021-03-31 16:42:58 -05:00
|
|
|
set
|
|
|
|
|
{
|
2021-03-31 17:20:20 -05:00
|
|
|
if (!this.ChangeBit(Bits.DetectUrls, value))
|
2021-03-31 16:42:58 -05:00
|
|
|
return;
|
|
|
|
|
this._editControl.DetectUrls = value;
|
|
|
|
|
this.FireThreadSafeNotification(NotificationID.DetectUrls);
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
public Color LinkColor
|
|
|
|
|
{
|
|
|
|
|
get => this._linkColor;
|
|
|
|
|
set
|
|
|
|
|
{
|
|
|
|
|
if (!(this._linkColor != value))
|
|
|
|
|
return;
|
|
|
|
|
this._linkColor = value;
|
|
|
|
|
this.FireThreadSafeNotification(NotificationID.LinkColor);
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
private void FireLinkClicked() => this.FireThreadSafeNotification(NotificationID.LinkClicked);
|
|
|
|
|
|
|
|
|
|
public string LinkClickedParameter
|
|
|
|
|
{
|
|
|
|
|
get => this._linkClickedParameter;
|
|
|
|
|
private set
|
|
|
|
|
{
|
|
|
|
|
if (!(this._linkClickedParameter != value))
|
|
|
|
|
return;
|
|
|
|
|
this._linkClickedParameter = value;
|
|
|
|
|
this.FireThreadSafeNotification(NotificationID.LinkClickedParameter);
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
HRESULT IRichTextCallbacks.SetCursor(EditCursorType type)
|
|
|
|
|
{
|
|
|
|
|
CursorID cursorId = CursorID.NotSpecified;
|
|
|
|
|
switch (type)
|
|
|
|
|
{
|
|
|
|
|
case EditCursorType.IBeam:
|
|
|
|
|
cursorId = CursorID.IBeam;
|
|
|
|
|
break;
|
|
|
|
|
}
|
|
|
|
|
if (cursorId != this._cursor)
|
|
|
|
|
{
|
|
|
|
|
this._cursor = cursorId;
|
|
|
|
|
this.UpdateCursor();
|
|
|
|
|
}
|
|
|
|
|
return new HRESULT(0);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
internal override CursorID GetCursor() => this._cursor;
|
|
|
|
|
|
|
|
|
|
private bool IgnoreReadOnlyChar(char character) => character == '\r' || character == '\t';
|
|
|
|
|
|
|
|
|
|
protected override void OnKeyDown(UIClass ui, KeyStateInfo info)
|
|
|
|
|
{
|
|
|
|
|
bool flag = this.ForwardKeyStateChange(info);
|
|
|
|
|
switch (info.Key)
|
|
|
|
|
{
|
|
|
|
|
case Keys.Back:
|
|
|
|
|
if (this._editData.ReadOnly)
|
|
|
|
|
this.FireTypingInputRejectedEvent();
|
|
|
|
|
info.MarkHandled();
|
|
|
|
|
break;
|
|
|
|
|
case Keys.Tab:
|
|
|
|
|
if (flag)
|
|
|
|
|
info.MarkHandled();
|
|
|
|
|
this.HandledTabKeyDown = info.Handled;
|
|
|
|
|
break;
|
|
|
|
|
case Keys.Enter:
|
|
|
|
|
if (!info.Handled && this.AcceptsEnter && (info.Modifiers == InputModifiers.None && !this._editData.ReadOnly))
|
|
|
|
|
{
|
|
|
|
|
this._editData.Submit();
|
|
|
|
|
info.MarkHandled();
|
|
|
|
|
}
|
|
|
|
|
this.HandledEnterKeyDown = info.Handled;
|
|
|
|
|
break;
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
protected override void OnKeyCharacter(UIClass ui, KeyCharacterInfo info)
|
|
|
|
|
{
|
|
|
|
|
if (this.ForwardKeyCharacter(info) && !info.Handled && (this._editData.ReadOnly && !this.IgnoreReadOnlyChar(info.Character)))
|
|
|
|
|
this.FireTypingInputRejectedEvent();
|
|
|
|
|
switch (info.Character)
|
|
|
|
|
{
|
|
|
|
|
case '\b':
|
|
|
|
|
info.MarkHandled();
|
|
|
|
|
break;
|
|
|
|
|
case '\t':
|
|
|
|
|
if (!this.HandledTabKeyDown)
|
|
|
|
|
break;
|
|
|
|
|
info.MarkHandled();
|
|
|
|
|
break;
|
|
|
|
|
case '\r':
|
|
|
|
|
if (!this.HandledEnterKeyDown)
|
|
|
|
|
break;
|
|
|
|
|
info.MarkHandled();
|
|
|
|
|
break;
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
protected override void OnKeyUp(UIClass ui, KeyStateInfo info)
|
|
|
|
|
{
|
|
|
|
|
this.ForwardKeyStateChange(info);
|
|
|
|
|
switch (info.Key)
|
|
|
|
|
{
|
|
|
|
|
case Keys.Back:
|
|
|
|
|
info.MarkHandled();
|
|
|
|
|
break;
|
|
|
|
|
case Keys.Tab:
|
|
|
|
|
if (!this.HandledTabKeyDown)
|
|
|
|
|
break;
|
|
|
|
|
info.MarkHandled();
|
|
|
|
|
this.HandledTabKeyDown = false;
|
|
|
|
|
break;
|
|
|
|
|
case Keys.Enter:
|
|
|
|
|
if (!this.HandledEnterKeyDown)
|
|
|
|
|
break;
|
|
|
|
|
info.MarkHandled();
|
|
|
|
|
this.HandledEnterKeyDown = false;
|
|
|
|
|
break;
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
private bool ForwardKeyToRichEdit(KeyStateInfo info)
|
|
|
|
|
{
|
|
|
|
|
bool flag = true;
|
|
|
|
|
switch (info.Key)
|
|
|
|
|
{
|
|
|
|
|
case Keys.Tab:
|
|
|
|
|
flag = info.Action != KeyAction.Down ? this.HandledTabKeyDown : this.AcceptsTab;
|
|
|
|
|
break;
|
|
|
|
|
case Keys.Enter:
|
|
|
|
|
flag = info.Action != KeyAction.Down ? this.HandledEnterKeyDown : this.AcceptsEnter;
|
|
|
|
|
break;
|
|
|
|
|
}
|
|
|
|
|
return flag;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
private bool ForwardKeyCharacterToRichEdit(KeyCharacterInfo info)
|
|
|
|
|
{
|
|
|
|
|
bool flag = true;
|
|
|
|
|
switch (info.Character)
|
|
|
|
|
{
|
|
|
|
|
case '\t':
|
|
|
|
|
flag = this.HandledTabKeyDown;
|
|
|
|
|
break;
|
|
|
|
|
case '\r':
|
|
|
|
|
flag = this.HandledEnterKeyDown;
|
|
|
|
|
break;
|
|
|
|
|
}
|
|
|
|
|
return flag;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
private bool ForwardKeyStateChange(KeyStateInfo info)
|
|
|
|
|
{
|
|
|
|
|
if (!this.ForwardKeyToRichEdit(info))
|
|
|
|
|
return false;
|
|
|
|
|
if (this._editControl.ForwardKeyStateNotification(info.NativeMessageID, (int)info.Key, info.ScanCode, (int)info.RepeatCount, (uint)info.Modifiers, info.KeyboardFlags))
|
|
|
|
|
info.MarkHandled();
|
|
|
|
|
return true;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
private bool ForwardKeyCharacter(KeyCharacterInfo info)
|
|
|
|
|
{
|
|
|
|
|
if (!this.ForwardKeyCharacterToRichEdit(info))
|
|
|
|
|
return false;
|
2021-03-31 17:15:06 -05:00
|
|
|
if (this._editControl.ForwardKeyCharacterNotification(info.NativeMessageID, info.Character, info.ScanCode, (int)info.RepeatCount, (uint)info.Modifiers, info.KeyboardFlags))
|
2021-03-31 16:42:58 -05:00
|
|
|
info.MarkHandled();
|
|
|
|
|
return true;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
protected override void OnGainKeyFocus(UIClass ui, KeyFocusInfo info)
|
|
|
|
|
{
|
2021-03-31 17:15:06 -05:00
|
|
|
RendererApi.IFC(NativeApi.SpRegisterImeCallbacks(this, out this._ImeCallbackToken));
|
2021-03-31 16:42:58 -05:00
|
|
|
this._editControl.NotifyOfFocusChange(true);
|
|
|
|
|
if (this.Overtype)
|
|
|
|
|
this.SelectAll();
|
|
|
|
|
this.DeliverPendingPointerDown();
|
|
|
|
|
if (this.UI.IsZoned)
|
|
|
|
|
this.UpdateActivationStateHandler(true);
|
|
|
|
|
if (!this._textDisplay.UsePasswordMask && !this._textDisplay.DisableIme)
|
|
|
|
|
return;
|
|
|
|
|
RendererApi.IFC(NativeApi.SpPostDeferredImeMessage(1032U, new UIntPtr(this._ImeCallbackToken), UIntPtr.Zero));
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
protected override void OnLoseKeyFocus(UIClass ui, KeyFocusInfo info)
|
|
|
|
|
{
|
|
|
|
|
this._editControl.NotifyOfFocusChange(false);
|
|
|
|
|
this.HandledEnterKeyDown = false;
|
|
|
|
|
this.HandledTabKeyDown = false;
|
|
|
|
|
if (this.UI.IsZoned)
|
|
|
|
|
this.UpdateActivationStateHandler(false);
|
|
|
|
|
this.ClearPendingPointerDown();
|
|
|
|
|
RendererApi.IFC(NativeApi.SpUnregisterImeCallbacks(this._ImeCallbackToken));
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
protected override void OnLoseMouseFocus(UIClass ui, MouseFocusInfo info)
|
|
|
|
|
{
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
private void UpdateActivationStateHandler(bool add)
|
|
|
|
|
{
|
|
|
|
|
if (add)
|
|
|
|
|
{
|
2021-03-31 17:15:06 -05:00
|
|
|
this._activationStateNotifier = UI.Zone.Form;
|
2021-03-31 16:42:58 -05:00
|
|
|
this._activationStateNotifier.ActivationChange += this._activationStateHandler;
|
2021-03-31 17:15:06 -05:00
|
|
|
this.OnActivationChanged(null, EventArgs.Empty);
|
2021-03-31 16:42:58 -05:00
|
|
|
}
|
|
|
|
|
else
|
|
|
|
|
{
|
|
|
|
|
this._activationStateNotifier.ActivationChange -= this._activationStateHandler;
|
2021-03-31 17:15:06 -05:00
|
|
|
this._activationStateNotifier = null;
|
2021-03-31 16:42:58 -05:00
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
2021-03-31 17:15:06 -05:00
|
|
|
protected override void OnMouseDoubleClick(UIClass ui, MouseButtonInfo info) => this.ForwardMouseInput(info);
|
2021-03-31 16:42:58 -05:00
|
|
|
|
2021-03-31 17:15:06 -05:00
|
|
|
protected override void OnMouseMove(UIClass ui, MouseMoveInfo info) => this.ForwardMouseInput(info);
|
2021-03-31 16:42:58 -05:00
|
|
|
|
|
|
|
|
protected override void OnMousePrimaryDown(UIClass ui, MouseButtonInfo info)
|
|
|
|
|
{
|
2021-03-31 17:15:06 -05:00
|
|
|
this._pendingPointerDown = info;
|
2021-03-31 16:42:58 -05:00
|
|
|
info.Lock();
|
|
|
|
|
if (!this.UI.DirectKeyFocus && this.HandlerStage == InputHandlerStage.Direct && this.UI.KeyFocusOnMouseDown)
|
|
|
|
|
return;
|
|
|
|
|
this.DeliverPendingPointerDown();
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
private void DeliverPendingPointerDown()
|
|
|
|
|
{
|
|
|
|
|
if (this._pendingPointerDown == null)
|
|
|
|
|
return;
|
|
|
|
|
this.MousePrimaryDown = true;
|
|
|
|
|
MouseButtonInfo pendingPointerDown = (MouseButtonInfo)this._pendingPointerDown;
|
|
|
|
|
this._savedMouseYPositionToWorkAroundRichEditBug = pendingPointerDown.Y;
|
2021-03-31 17:15:06 -05:00
|
|
|
this.ForwardMouseInput(pendingPointerDown);
|
2021-03-31 16:42:58 -05:00
|
|
|
this.ClearPendingPointerDown();
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
private void ClearPendingPointerDown()
|
|
|
|
|
{
|
|
|
|
|
if (this._pendingPointerDown == null)
|
|
|
|
|
return;
|
|
|
|
|
this._pendingPointerDown.Unlock();
|
2021-03-31 17:15:06 -05:00
|
|
|
this._pendingPointerDown = null;
|
2021-03-31 16:42:58 -05:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
protected override void OnMousePrimaryUp(UIClass ui, MouseButtonInfo info)
|
|
|
|
|
{
|
|
|
|
|
this.MousePrimaryDown = false;
|
2021-03-31 17:15:06 -05:00
|
|
|
this.ForwardMouseInput(info);
|
2021-03-31 16:42:58 -05:00
|
|
|
}
|
|
|
|
|
|
2021-03-31 17:15:06 -05:00
|
|
|
protected override void OnMouseSecondaryDown(UIClass ui, MouseButtonInfo info) => this.ForwardMouseInput(info);
|
2021-03-31 16:42:58 -05:00
|
|
|
|
2021-03-31 17:15:06 -05:00
|
|
|
protected override void OnMouseSecondaryUp(UIClass ui, MouseButtonInfo info) => this.ForwardMouseInput(info);
|
2021-03-31 16:42:58 -05:00
|
|
|
|
|
|
|
|
protected override void OnMouseWheel(UIClass ui, MouseWheelInfo info)
|
|
|
|
|
{
|
|
|
|
|
if (!this._textDisplay.WordWrap)
|
|
|
|
|
return;
|
2021-03-31 17:15:06 -05:00
|
|
|
this.ForwardMouseInput(info);
|
2021-03-31 16:42:58 -05:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
private bool ForwardMouseInput(
|
|
|
|
|
uint nativeMessageID,
|
|
|
|
|
InputModifiers modifiers,
|
|
|
|
|
MouseButtons button,
|
|
|
|
|
int inputX,
|
|
|
|
|
int inputY,
|
|
|
|
|
int wheelDelta)
|
|
|
|
|
{
|
|
|
|
|
if (this.InputOffsetDirty)
|
|
|
|
|
{
|
|
|
|
|
Vector3 parentOffsetPxlVector;
|
2021-03-31 17:15:06 -05:00
|
|
|
ViewItem.GetAccumulatedOffsetAndScale(_textDisplay, UI.RootItem, out parentOffsetPxlVector, out Vector3 _);
|
|
|
|
|
this._inputOffset = new Point((int)Math.Floor(parentOffsetPxlVector.X), (int)Math.Floor(parentOffsetPxlVector.Y));
|
2021-03-31 16:42:58 -05:00
|
|
|
this.InputOffsetDirty = false;
|
|
|
|
|
}
|
|
|
|
|
int x = inputX - this._inputOffset.X;
|
|
|
|
|
int y = inputY - this._inputOffset.Y;
|
|
|
|
|
if (this.MousePrimaryDown && this._textDisplay != null && !this._textDisplay.WordWrap)
|
|
|
|
|
y = this._savedMouseYPositionToWorkAroundRichEditBug - this._inputOffset.Y;
|
|
|
|
|
return this._editControl.ForwardMouseInput(nativeMessageID, (uint)modifiers, (int)button, x, y, wheelDelta);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
private void ForwardMouseInput(MouseActionInfo info)
|
|
|
|
|
{
|
|
|
|
|
if (!this.ForwardMouseInput(info.NativeMessageID, info.Modifiers, info.Button, info.X, info.Y, info.WheelDelta))
|
|
|
|
|
return;
|
|
|
|
|
info.MarkHandled();
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
public bool Overtype
|
|
|
|
|
{
|
2021-03-31 17:20:20 -05:00
|
|
|
get => this.GetBit(Bits.Overtype);
|
2021-03-31 16:42:58 -05:00
|
|
|
set
|
|
|
|
|
{
|
2021-03-31 17:20:20 -05:00
|
|
|
if (!this.ChangeBit(Bits.Overtype, value))
|
2021-03-31 16:42:58 -05:00
|
|
|
return;
|
|
|
|
|
this.FireThreadSafeNotification(NotificationID.Overtype);
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
public bool AcceptsTab
|
|
|
|
|
{
|
2021-03-31 17:20:20 -05:00
|
|
|
get => this.GetBit(Bits.AcceptsTab);
|
2021-03-31 16:42:58 -05:00
|
|
|
set
|
|
|
|
|
{
|
2021-03-31 17:20:20 -05:00
|
|
|
if (!this.ChangeBit(Bits.AcceptsTab, value))
|
2021-03-31 16:42:58 -05:00
|
|
|
return;
|
|
|
|
|
this.FireThreadSafeNotification(NotificationID.AcceptsTab);
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
public bool AcceptsEnter
|
|
|
|
|
{
|
2021-03-31 17:20:20 -05:00
|
|
|
get => this.GetBit(Bits.AcceptsEnter);
|
2021-03-31 16:42:58 -05:00
|
|
|
set
|
|
|
|
|
{
|
2021-03-31 17:20:20 -05:00
|
|
|
if (!this.ChangeBit(Bits.AcceptsEnter, value))
|
2021-03-31 16:42:58 -05:00
|
|
|
return;
|
|
|
|
|
this.FireThreadSafeNotification(NotificationID.AcceptsEnter);
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
public bool WordWrap
|
|
|
|
|
{
|
|
|
|
|
set
|
|
|
|
|
{
|
2021-03-31 17:20:20 -05:00
|
|
|
if (!this.ChangeBit(Bits.WordWrap, value))
|
2021-03-31 16:42:58 -05:00
|
|
|
return;
|
|
|
|
|
this._editControl.SetWordWrap(value);
|
|
|
|
|
if (!value)
|
|
|
|
|
return;
|
|
|
|
|
this.UpdateContentOnRichEdit();
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
public CaretInfo CaretInfo => this._caretInfo;
|
|
|
|
|
|
|
|
|
|
private void CreateCommands()
|
|
|
|
|
{
|
2021-03-31 17:20:20 -05:00
|
|
|
if (this.GetBit(Bits.CommandsCreated))
|
2021-03-31 16:42:58 -05:00
|
|
|
return;
|
2021-03-31 17:20:20 -05:00
|
|
|
this.SetBit(Bits.CommandsCreated);
|
2021-03-31 16:42:58 -05:00
|
|
|
this._undoCommand = new TextEditingHandler.TextEditingCommand(new SimpleCallback(this._editControl.Undo));
|
|
|
|
|
this._cutCommand = new TextEditingHandler.TextEditingCommand(new SimpleCallback(this._editControl.Cut));
|
|
|
|
|
this._copyCommand = new TextEditingHandler.TextEditingCommand(new SimpleCallback(this._editControl.Copy));
|
|
|
|
|
this._pasteCommand = new TextEditingHandler.TextPasteCommand(new SimpleCallback(this._editControl.Paste));
|
|
|
|
|
this._deleteCommand = new TextEditingHandler.TextEditingCommand(new SimpleCallback(this._editControl.Delete));
|
|
|
|
|
this._selectAllCommand = new TextEditingHandler.TextEditingCommand(new SimpleCallback(this.SelectAll));
|
|
|
|
|
this.UpdateSelectionAndReadOnlyCommands();
|
|
|
|
|
this.UpdateTextBasedCommandAvailability();
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
public IUICommand UndoCommand
|
|
|
|
|
{
|
|
|
|
|
get
|
|
|
|
|
{
|
|
|
|
|
this.CreateCommands();
|
2021-03-31 17:15:06 -05:00
|
|
|
return _undoCommand;
|
2021-03-31 16:42:58 -05:00
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
public void Undo() => this._editControl.Undo();
|
|
|
|
|
|
|
|
|
|
public IUICommand CutCommand
|
|
|
|
|
{
|
|
|
|
|
get
|
|
|
|
|
{
|
|
|
|
|
this.CreateCommands();
|
2021-03-31 17:15:06 -05:00
|
|
|
return _cutCommand;
|
2021-03-31 16:42:58 -05:00
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
public void Cut() => this._editControl.Cut();
|
|
|
|
|
|
|
|
|
|
public IUICommand CopyCommand
|
|
|
|
|
{
|
|
|
|
|
get
|
|
|
|
|
{
|
|
|
|
|
this.CreateCommands();
|
2021-03-31 17:15:06 -05:00
|
|
|
return _copyCommand;
|
2021-03-31 16:42:58 -05:00
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
public void Copy() => this._editControl.Copy();
|
|
|
|
|
|
|
|
|
|
public IUICommand PasteCommand
|
|
|
|
|
{
|
|
|
|
|
get
|
|
|
|
|
{
|
|
|
|
|
this.CreateCommands();
|
2021-03-31 17:15:06 -05:00
|
|
|
return _pasteCommand;
|
2021-03-31 16:42:58 -05:00
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
public void Paste() => this._editControl.Paste();
|
|
|
|
|
|
|
|
|
|
public IUICommand DeleteCommand
|
|
|
|
|
{
|
|
|
|
|
get
|
|
|
|
|
{
|
|
|
|
|
this.CreateCommands();
|
2021-03-31 17:15:06 -05:00
|
|
|
return _deleteCommand;
|
2021-03-31 16:42:58 -05:00
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
public void Delete() => this._editControl.Delete();
|
|
|
|
|
|
|
|
|
|
public IUICommand SelectAllCommand
|
|
|
|
|
{
|
|
|
|
|
get
|
|
|
|
|
{
|
|
|
|
|
this.CreateCommands();
|
2021-03-31 17:15:06 -05:00
|
|
|
return _selectAllCommand;
|
2021-03-31 16:42:58 -05:00
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
public void SelectAll()
|
|
|
|
|
{
|
2021-03-31 17:15:06 -05:00
|
|
|
string str = this._editData != null ? this._editData.Value : null;
|
2021-03-31 16:42:58 -05:00
|
|
|
if (string.IsNullOrEmpty(str))
|
|
|
|
|
return;
|
|
|
|
|
this.SelectionRange = new Range(0, str.Length);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
public Range SelectionRange
|
|
|
|
|
{
|
|
|
|
|
get => this._selection;
|
|
|
|
|
set
|
|
|
|
|
{
|
|
|
|
|
if (this._selection.IsEqual(value))
|
|
|
|
|
return;
|
|
|
|
|
this._editControl.SetSelectionRange(value.Begin, value.End);
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
private void UpdateTextBasedCommandAvailability()
|
|
|
|
|
{
|
|
|
|
|
this._undoCommand.Available = this._editControl.CanUndo;
|
|
|
|
|
this._selectAllCommand.Available = this._editData != null && !string.IsNullOrEmpty(this._editData.Value);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
private void UpdateSelectionAndReadOnlyCommands()
|
|
|
|
|
{
|
2021-03-31 17:20:20 -05:00
|
|
|
if (!this.GetBit(Bits.CommandsCreated))
|
2021-03-31 16:42:58 -05:00
|
|
|
return;
|
|
|
|
|
bool flag1 = !this._selection.IsEmpty;
|
|
|
|
|
bool flag2 = this._editData == null || this._editData.ReadOnly;
|
|
|
|
|
this._cutCommand.Available = flag1 && !flag2;
|
|
|
|
|
this._copyCommand.Available = flag1;
|
|
|
|
|
this._deleteCommand.Available = flag1 && !flag2;
|
|
|
|
|
this._pasteCommand.TextIsReadOnly = flag2;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
HRESULT IRichTextCallbacks.TextChanged()
|
|
|
|
|
{
|
|
|
|
|
if (!Application.IsApplicationThread)
|
|
|
|
|
{
|
2021-03-31 17:20:20 -05:00
|
|
|
Application.DeferredInvoke(args => ((IRichTextCallbacks)args).TextChanged(), this, DeferredInvokePriority.Normal);
|
2021-03-31 16:42:58 -05:00
|
|
|
return new HRESULT(0);
|
|
|
|
|
}
|
2021-03-31 17:20:20 -05:00
|
|
|
if (this.GetBit(Bits.CommandsCreated))
|
2021-03-31 16:42:58 -05:00
|
|
|
this.UpdateTextBasedCommandAvailability();
|
|
|
|
|
if (this._editData != null && !this.InsideContentChangeOnRichEdit)
|
|
|
|
|
{
|
|
|
|
|
this.InsideValueChangeOnEditableTextData = true;
|
|
|
|
|
this._editData.Value = this._editControl.SimpleContent;
|
|
|
|
|
this.InsideValueChangeOnEditableTextData = false;
|
|
|
|
|
}
|
|
|
|
|
return new HRESULT(0);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
HRESULT IRichTextCallbacks.InvalidateContent()
|
|
|
|
|
{
|
|
|
|
|
if (!Application.IsApplicationThread)
|
|
|
|
|
{
|
2021-03-31 17:20:20 -05:00
|
|
|
Application.DeferredInvoke(args => ((IRichTextCallbacks)args).InvalidateContent(), this, DeferredInvokePriority.Normal);
|
2021-03-31 16:42:58 -05:00
|
|
|
return new HRESULT(0);
|
|
|
|
|
}
|
|
|
|
|
if (this._textDisplay != null)
|
|
|
|
|
this._textDisplay.OnDisplayedContentChange();
|
|
|
|
|
this.RefreshCaretPosition();
|
|
|
|
|
return new HRESULT(0);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
HRESULT IRichTextCallbacks.SelectionChanged(
|
|
|
|
|
int selectionStart,
|
|
|
|
|
int selectionEnd)
|
|
|
|
|
{
|
|
|
|
|
this._selection = new Range(selectionStart, selectionEnd);
|
|
|
|
|
this.FireThreadSafeNotification(NotificationID.SelectionRange);
|
|
|
|
|
this.UpdateSelectionAndReadOnlyCommands();
|
|
|
|
|
return new HRESULT(0);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
HRESULT IRichTextCallbacks.MaxLengthExceeded()
|
|
|
|
|
{
|
|
|
|
|
this.FireTypingInputRejectedEvent();
|
|
|
|
|
return new HRESULT(0);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
HRESULT IRichTextCallbacks.SetTimer(uint id, uint timeout)
|
|
|
|
|
{
|
|
|
|
|
this._editControl.SetTimer(id, timeout);
|
|
|
|
|
return new HRESULT(0);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
HRESULT IRichTextCallbacks.KillTimer(uint id)
|
|
|
|
|
{
|
|
|
|
|
this._editControl.KillTimer(id);
|
|
|
|
|
return new HRESULT(0);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
HRESULT IRichTextCallbacks.CreateCaret(int width, int height)
|
|
|
|
|
{
|
|
|
|
|
this._caretInfo.CreateCaret(new Size(width, height));
|
|
|
|
|
return new HRESULT(0);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
HRESULT IRichTextCallbacks.SetCaretPos(int x, int y)
|
|
|
|
|
{
|
|
|
|
|
this._caretInfo.SetCaretPosition(new Point(x, y));
|
|
|
|
|
return new HRESULT(0);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
HRESULT IRichTextCallbacks.ShowCaret(bool visible)
|
|
|
|
|
{
|
|
|
|
|
this.RichEditCaretVisible = visible;
|
|
|
|
|
this.UpdateCaretVisibility();
|
|
|
|
|
return new HRESULT(0);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
HRESULT IRichTextCallbacks.ClientToWindow(Point pt, out Point ppt)
|
|
|
|
|
{
|
|
|
|
|
ppt = this._textDisplay == null ? new Point(0, 0) : this._textDisplay.ClientToWindow(pt);
|
|
|
|
|
return new HRESULT(0);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
HRESULT IRichTextCallbacks.ClientToScreen(Point pt, out Point ppt)
|
|
|
|
|
{
|
|
|
|
|
ppt = this._textDisplay.ClientToScreen(pt);
|
|
|
|
|
return new HRESULT(0);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
HRESULT IRichTextCallbacks.LinkClicked(int start, int end)
|
|
|
|
|
{
|
|
|
|
|
string str = this._editData != null ? this._editData.Value : string.Empty;
|
|
|
|
|
if (string.IsNullOrEmpty(str))
|
|
|
|
|
{
|
|
|
|
|
this.LinkClickedParameter = string.Empty;
|
|
|
|
|
}
|
|
|
|
|
else
|
|
|
|
|
{
|
|
|
|
|
end = end >= 0 ? Math.Min(str.Length, end) : str.Length;
|
|
|
|
|
start = Math.Min(start, end);
|
|
|
|
|
this.LinkClickedParameter = str.Substring(start, end - start);
|
|
|
|
|
}
|
|
|
|
|
this.FireLinkClicked();
|
|
|
|
|
return new HRESULT(0);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
HRESULT IImeCallbacks.OnImeMessageReceived(
|
|
|
|
|
uint message,
|
|
|
|
|
UIntPtr wParam,
|
|
|
|
|
UIntPtr lParam)
|
|
|
|
|
{
|
|
|
|
|
switch (message)
|
|
|
|
|
{
|
|
|
|
|
case 269:
|
|
|
|
|
this.InImeCompositionMode = true;
|
|
|
|
|
break;
|
|
|
|
|
case 270:
|
|
|
|
|
this.InImeCompositionMode = false;
|
|
|
|
|
break;
|
|
|
|
|
}
|
|
|
|
|
return this._editControl.ForwardImeMessage(message, wParam, lParam);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
public bool InImeCompositionMode
|
|
|
|
|
{
|
|
|
|
|
get => this._InImeMode;
|
|
|
|
|
set
|
|
|
|
|
{
|
|
|
|
|
if (this._InImeMode == value)
|
|
|
|
|
return;
|
|
|
|
|
this.CaretInfo.IgnoreIdealWidth = value;
|
|
|
|
|
this._InImeMode = value;
|
|
|
|
|
this.FireThreadSafeNotification(NotificationID.InImeCompositionMode);
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
HRESULT IRichTextCallbacks.SetScrollRange(
|
|
|
|
|
int whichBarInt,
|
|
|
|
|
int minPosition,
|
|
|
|
|
int extent,
|
|
|
|
|
int viewExtent,
|
|
|
|
|
int scrollPosition)
|
|
|
|
|
{
|
|
|
|
|
if (whichBarInt == 1 && this._verticalScrollModel != null)
|
|
|
|
|
{
|
|
|
|
|
this.SetScrollRange(ref this._pendingVerticalScrollState, minPosition, extent, viewExtent, scrollPosition);
|
|
|
|
|
this.ScheduleScrollbarUpdate(true);
|
|
|
|
|
}
|
|
|
|
|
else if (this._horizontalScrollModel != null)
|
|
|
|
|
{
|
|
|
|
|
this.SetScrollRange(ref this._pendingHorizontalScrollState, minPosition, extent, viewExtent, scrollPosition);
|
|
|
|
|
this.ScheduleScrollbarUpdate(false);
|
|
|
|
|
}
|
|
|
|
|
return new HRESULT(0);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
private void SetScrollRange(
|
|
|
|
|
ref TextScrollModel.State state,
|
|
|
|
|
int minPosition,
|
|
|
|
|
int extent,
|
|
|
|
|
int viewExtent,
|
|
|
|
|
int scrollAmount)
|
|
|
|
|
{
|
|
|
|
|
state.Min = minPosition;
|
|
|
|
|
state.Extent = extent;
|
|
|
|
|
state.ViewExtent = viewExtent;
|
|
|
|
|
state.ScrollAmount = scrollAmount;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
private bool IsVerticalScrollbar(ScrollbarType whichBar) => whichBar == ScrollbarType.Vertical || whichBar == ScrollbarType.Both;
|
|
|
|
|
|
|
|
|
|
private bool IsHorizontalScrollbar(ScrollbarType whichBar) => whichBar == ScrollbarType.Horizontal || whichBar == ScrollbarType.Both;
|
|
|
|
|
|
|
|
|
|
HRESULT IRichTextCallbacks.EnableScrollbar(
|
|
|
|
|
int whichBarInt,
|
|
|
|
|
ScrollbarEnableFlags flags)
|
|
|
|
|
{
|
|
|
|
|
ScrollbarType whichBar = (ScrollbarType)whichBarInt;
|
|
|
|
|
if (this.IsVerticalScrollbar(whichBar) && this._verticalScrollModel != null)
|
|
|
|
|
{
|
|
|
|
|
this.EnableScrollbar(ref this._pendingVerticalScrollState, flags);
|
|
|
|
|
this.ScheduleScrollbarUpdate(true);
|
|
|
|
|
}
|
|
|
|
|
if (this.IsHorizontalScrollbar(whichBar) && this._horizontalScrollModel != null)
|
|
|
|
|
{
|
|
|
|
|
this.EnableScrollbar(ref this._pendingHorizontalScrollState, flags);
|
|
|
|
|
this.ScheduleScrollbarUpdate(false);
|
|
|
|
|
}
|
|
|
|
|
return new HRESULT(0);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
private void EnableScrollbar(ref TextScrollModel.State state, ScrollbarEnableFlags flags)
|
|
|
|
|
{
|
|
|
|
|
switch (flags)
|
|
|
|
|
{
|
|
|
|
|
case ScrollbarEnableFlags.EnableBoth:
|
|
|
|
|
state.CanScrollUp = true;
|
|
|
|
|
state.CanScrollDown = true;
|
|
|
|
|
break;
|
|
|
|
|
case ScrollbarEnableFlags.DisableNear:
|
|
|
|
|
state.CanScrollUp = false;
|
|
|
|
|
state.CanScrollDown = true;
|
|
|
|
|
break;
|
|
|
|
|
case ScrollbarEnableFlags.DisableFar:
|
|
|
|
|
state.CanScrollUp = true;
|
|
|
|
|
state.CanScrollDown = false;
|
|
|
|
|
break;
|
|
|
|
|
case ScrollbarEnableFlags.DisableBoth:
|
|
|
|
|
state.CanScrollUp = false;
|
|
|
|
|
state.CanScrollDown = false;
|
|
|
|
|
break;
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
private void ScheduleScrollbarUpdate(bool vertical)
|
|
|
|
|
{
|
2021-03-31 17:20:20 -05:00
|
|
|
bool flag = !this.GetBit(Bits.PendingVerticalScrollbarUpdate) && !this.GetBit(Bits.PendingHorizontalScrollbarUpdate);
|
2021-03-31 16:42:58 -05:00
|
|
|
if (vertical)
|
2021-03-31 17:20:20 -05:00
|
|
|
this.SetBit(Bits.PendingVerticalScrollbarUpdate);
|
2021-03-31 16:42:58 -05:00
|
|
|
else
|
2021-03-31 17:20:20 -05:00
|
|
|
this.SetBit(Bits.PendingHorizontalScrollbarUpdate);
|
2021-03-31 16:42:58 -05:00
|
|
|
if (!flag)
|
|
|
|
|
return;
|
|
|
|
|
if (this._updateScrollbars == null)
|
|
|
|
|
this._updateScrollbars = new SimpleCallback(this.UpdateScrollbars);
|
|
|
|
|
DeferredCall.Post(DispatchPriority.Normal, this._updateScrollbars);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
private void UpdateScrollbars()
|
|
|
|
|
{
|
2021-03-31 17:20:20 -05:00
|
|
|
if (this.GetBit(Bits.PendingVerticalScrollbarUpdate))
|
2021-03-31 16:42:58 -05:00
|
|
|
{
|
2021-03-31 17:20:20 -05:00
|
|
|
this.ClearBit(Bits.PendingVerticalScrollbarUpdate);
|
2021-03-31 16:42:58 -05:00
|
|
|
this._verticalScrollModel.UpdateState(this._pendingVerticalScrollState);
|
|
|
|
|
this._pendingVerticalScrollState = new TextScrollModel.State();
|
|
|
|
|
}
|
2021-03-31 17:20:20 -05:00
|
|
|
if (!this.GetBit(Bits.PendingHorizontalScrollbarUpdate))
|
2021-03-31 16:42:58 -05:00
|
|
|
return;
|
2021-03-31 17:20:20 -05:00
|
|
|
this.ClearBit(Bits.PendingHorizontalScrollbarUpdate);
|
2021-03-31 16:42:58 -05:00
|
|
|
this._horizontalScrollModel.UpdateState(this._pendingHorizontalScrollState);
|
|
|
|
|
this._pendingHorizontalScrollState = new TextScrollModel.State();
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
public TextScrollModel HorizontalScrollModel
|
|
|
|
|
{
|
|
|
|
|
get
|
|
|
|
|
{
|
|
|
|
|
if (this._horizontalScrollModel == null)
|
|
|
|
|
this.StoreScrollModel(ref this._horizontalScrollModel, new TextScrollModel());
|
|
|
|
|
return this._horizontalScrollModel;
|
|
|
|
|
}
|
|
|
|
|
set
|
|
|
|
|
{
|
|
|
|
|
if (this._horizontalScrollModel == value)
|
|
|
|
|
return;
|
|
|
|
|
this.StoreScrollModel(ref this._horizontalScrollModel, value);
|
|
|
|
|
this.FireThreadSafeNotification(NotificationID.HorizontalScrollModel);
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
public TextScrollModel VerticalScrollModel
|
|
|
|
|
{
|
|
|
|
|
get
|
|
|
|
|
{
|
|
|
|
|
if (this._verticalScrollModel == null)
|
|
|
|
|
this.StoreScrollModel(ref this._verticalScrollModel, new TextScrollModel());
|
|
|
|
|
return this._verticalScrollModel;
|
|
|
|
|
}
|
|
|
|
|
set
|
|
|
|
|
{
|
|
|
|
|
if (this._verticalScrollModel == value)
|
|
|
|
|
return;
|
|
|
|
|
this.StoreScrollModel(ref this._verticalScrollModel, value);
|
|
|
|
|
this.FireThreadSafeNotification(NotificationID.VerticalScrollModel);
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
private void StoreScrollModel(ref TextScrollModel storage, TextScrollModel newDude)
|
|
|
|
|
{
|
|
|
|
|
if (storage != null)
|
|
|
|
|
storage.DetachCallbacks();
|
|
|
|
|
storage = newDude;
|
|
|
|
|
if (storage != null)
|
2021-03-31 17:15:06 -05:00
|
|
|
storage.AttachCallbacks(this);
|
2021-03-31 16:42:58 -05:00
|
|
|
this._editControl.SetScrollbars(this._horizontalScrollModel != null, this._verticalScrollModel != null);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
private ScrollbarType WhichScrollbar(TextScrollModel who) => who != this._verticalScrollModel ? ScrollbarType.Horizontal : ScrollbarType.Vertical;
|
|
|
|
|
|
|
|
|
|
void ITextScrollModelCallback.ScrollUp(TextScrollModel who) => this._editControl.ScrollUp(this.WhichScrollbar(who));
|
|
|
|
|
|
|
|
|
|
void ITextScrollModelCallback.ScrollDown(TextScrollModel who) => this._editControl.ScrollDown(this.WhichScrollbar(who));
|
|
|
|
|
|
|
|
|
|
void ITextScrollModelCallback.PageUp(TextScrollModel who) => this._editControl.PageUp(this.WhichScrollbar(who));
|
|
|
|
|
|
|
|
|
|
void ITextScrollModelCallback.PageDown(TextScrollModel who) => this._editControl.PageDown(this.WhichScrollbar(who));
|
|
|
|
|
|
|
|
|
|
void ITextScrollModelCallback.ScrollToPosition(
|
|
|
|
|
TextScrollModel who,
|
|
|
|
|
int whereTo)
|
|
|
|
|
{
|
|
|
|
|
this._editControl.ScrollToPosition(this.WhichScrollbar(who), whereTo);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
private void UpdateCaretVisibility() => this.CaretInfo.SetVisible(this.RichEditCaretVisible && this.WindowIsActivated);
|
|
|
|
|
|
|
|
|
|
private void RefreshCaretPosition()
|
|
|
|
|
{
|
|
|
|
|
if (!this.CaretInfo.Visible || this.MousePrimaryDown || this._editControl == null)
|
|
|
|
|
return;
|
|
|
|
|
this._editControl.NotifyOfFocusChange(true);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
private bool HandledTabKeyDown
|
|
|
|
|
{
|
2021-03-31 17:20:20 -05:00
|
|
|
get => this.GetBit(Bits.HandledTabKeyDown);
|
|
|
|
|
set => this.SetBit(Bits.HandledTabKeyDown, value);
|
2021-03-31 16:42:58 -05:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
private bool HandledEnterKeyDown
|
|
|
|
|
{
|
2021-03-31 17:20:20 -05:00
|
|
|
get => this.GetBit(Bits.HandledEnterKeyDown);
|
|
|
|
|
set => this.SetBit(Bits.HandledEnterKeyDown, value);
|
2021-03-31 16:42:58 -05:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
private bool InputOffsetDirty
|
|
|
|
|
{
|
2021-03-31 17:20:20 -05:00
|
|
|
get => this.GetBit(Bits.InputOffsetDirty);
|
|
|
|
|
set => this.SetBit(Bits.InputOffsetDirty, value);
|
2021-03-31 16:42:58 -05:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
private bool MousePrimaryDown
|
|
|
|
|
{
|
2021-03-31 17:20:20 -05:00
|
|
|
get => this.GetBit(Bits.MousePrimaryDown);
|
|
|
|
|
set => this.SetBit(Bits.MousePrimaryDown, value);
|
2021-03-31 16:42:58 -05:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
private bool InsideValueChangeOnEditableTextData
|
|
|
|
|
{
|
2021-03-31 17:20:20 -05:00
|
|
|
get => this.GetBit(Bits.InsideValueChangeOnEditableTextData);
|
|
|
|
|
set => this.SetBit(Bits.InsideValueChangeOnEditableTextData, value);
|
2021-03-31 16:42:58 -05:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
private bool InsideContentChangeOnRichEdit
|
|
|
|
|
{
|
2021-03-31 17:20:20 -05:00
|
|
|
get => this.GetBit(Bits.InsideContentChangeOnRichEdit);
|
|
|
|
|
set => this.SetBit(Bits.InsideContentChangeOnRichEdit, value);
|
2021-03-31 16:42:58 -05:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
private bool RichEditCaretVisible
|
|
|
|
|
{
|
2021-03-31 17:20:20 -05:00
|
|
|
get => this.GetBit(Bits.RichEditCaretVisible);
|
|
|
|
|
set => this.SetBit(Bits.RichEditCaretVisible, value);
|
2021-03-31 16:42:58 -05:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
private bool WindowIsActivated
|
|
|
|
|
{
|
2021-03-31 17:20:20 -05:00
|
|
|
get => this.GetBit(Bits.WindowIsActivated);
|
|
|
|
|
set => this.SetBit(Bits.WindowIsActivated, value);
|
2021-03-31 16:42:58 -05:00
|
|
|
}
|
|
|
|
|
|
2021-03-31 17:15:06 -05:00
|
|
|
private bool GetBit(TextEditingHandler.Bits lookupBit) => ((TextEditingHandler.Bits)this._bits & lookupBit) != 0;
|
2021-03-31 16:42:58 -05:00
|
|
|
|
|
|
|
|
private void SetBit(TextEditingHandler.Bits changeBit)
|
|
|
|
|
{
|
|
|
|
|
TextEditingHandler textEditingHandler = this;
|
|
|
|
|
textEditingHandler._bits = (uint)((TextEditingHandler.Bits)textEditingHandler._bits | changeBit);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
private void SetBit(TextEditingHandler.Bits changeBit, bool value) => this._bits = value ? (uint)((TextEditingHandler.Bits)this._bits | changeBit) : (uint)((TextEditingHandler.Bits)this._bits & ~changeBit);
|
|
|
|
|
|
|
|
|
|
private void ClearBit(TextEditingHandler.Bits changeBit)
|
|
|
|
|
{
|
|
|
|
|
TextEditingHandler textEditingHandler = this;
|
|
|
|
|
textEditingHandler._bits = (uint)((TextEditingHandler.Bits)textEditingHandler._bits & ~changeBit);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
private bool ChangeBit(TextEditingHandler.Bits changeBit, bool value)
|
|
|
|
|
{
|
|
|
|
|
uint num = value ? (uint)((TextEditingHandler.Bits)this._bits | changeBit) : (uint)((TextEditingHandler.Bits)this._bits & ~changeBit);
|
|
|
|
|
bool flag = (int)num != (int)this._bits;
|
|
|
|
|
if (flag)
|
|
|
|
|
this._bits = num;
|
|
|
|
|
return flag;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
private enum Bits
|
|
|
|
|
{
|
|
|
|
|
AcceptsTab = 1,
|
|
|
|
|
AcceptsEnter = 2,
|
|
|
|
|
HandledTabKeyDown = 4,
|
|
|
|
|
HandledEnterKeyDown = 16, // 0x00000010
|
|
|
|
|
InputOffsetDirty = 32, // 0x00000020
|
|
|
|
|
MousePrimaryDown = 64, // 0x00000040
|
|
|
|
|
Overtype = 128, // 0x00000080
|
|
|
|
|
InsideValueChangeOnEditableTextData = 256, // 0x00000100
|
|
|
|
|
InsideContentChangeOnRichEdit = 512, // 0x00000200
|
|
|
|
|
RichEditCaretVisible = 1024, // 0x00000400
|
|
|
|
|
WindowIsActivated = 2048, // 0x00000800
|
|
|
|
|
WordWrap = 4096, // 0x00001000
|
|
|
|
|
CommandsCreated = 8192, // 0x00002000
|
|
|
|
|
PendingHorizontalScrollbarUpdate = 16384, // 0x00004000
|
|
|
|
|
PendingVerticalScrollbarUpdate = 32768, // 0x00008000
|
|
|
|
|
DetectUrls = 65536, // 0x00010000
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
private class TextEditingCommand : UICommand
|
|
|
|
|
{
|
|
|
|
|
private SimpleCallback _onInvoked;
|
|
|
|
|
|
|
|
|
|
public TextEditingCommand(SimpleCallback onInvoked) => this._onInvoked = onInvoked;
|
|
|
|
|
|
|
|
|
|
protected override void OnInvoked()
|
|
|
|
|
{
|
|
|
|
|
base.OnInvoked();
|
|
|
|
|
this._onInvoked();
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
private class TextPasteCommand : TextEditingHandler.TextEditingCommand, IUICommand
|
|
|
|
|
{
|
|
|
|
|
private bool _isReadOnly;
|
|
|
|
|
|
|
|
|
|
public TextPasteCommand(SimpleCallback onInvoked)
|
|
|
|
|
: base(onInvoked)
|
|
|
|
|
{
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
bool IUICommand.Available
|
|
|
|
|
{
|
|
|
|
|
get => Clipboard.ContainsText() && !this._isReadOnly;
|
|
|
|
|
set
|
|
|
|
|
{
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
public bool TextIsReadOnly
|
|
|
|
|
{
|
|
|
|
|
get => this._isReadOnly;
|
|
|
|
|
set => this._isReadOnly = value;
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|