浏览代码

Merge pull request #59 from fzhangtj/text_edit

fix text edit: copy/paste/select keyboard shortcut
/main
GitHub 6 年前
当前提交
d065a8f2
共有 5 个文件被更改,包括 134 次插入45 次删除
  1. 44
      Runtime/rendering/editable.cs
  2. 40
      Runtime/service/keyboard.cs
  3. 50
      Runtime/service/raw_keyboard.cs
  4. 9
      Runtime/service/text_input.cs
  5. 36
      Runtime/ui/txt/paragraph.cs

44
Runtime/rendering/editable.cs


bool vKey = pressedKeyCode == KeyCode.V;
bool cKey = pressedKeyCode == KeyCode.C;
bool del = pressedKeyCode == KeyCode.Delete;
bool backDel = pressedKeyCode == KeyCode.Backspace;
if (keyEvent is RawKeyCommandEvent) { // editor case
this._handleShortcuts(((RawKeyCommandEvent)keyEvent).command);
return;
}
if ((ctrl || (isMac && cmd)) && (xKey || vKey || cKey || aKey)) { // runtime case
if (xKey) {
this._handleShortcuts(KeyCommand.Cut);
} else if (aKey) {
this._handleShortcuts(KeyCommand.SelectAll);
} else if (vKey) {
this._handleShortcuts(KeyCommand.Paste);
} else if (cKey) {
this._handleShortcuts(KeyCommand.Copy);
}
return;
}
if (arrow) {
int newOffset = this._extentOffset;

newOffset = this._handleShift(rightArrow, leftArrow, shift, newOffset);
this._extentOffset = newOffset;
} else if ((ctrl || (isMac && cmd)) && (xKey || vKey || cKey || aKey)) {
this._handleShortcuts(pressedKeyCode);
if (del || backDel) {
this._handleDelete(backDel);
if (del) {
this._handleDelete();
}
}

return newOffset;
}
void _handleShortcuts(KeyCode pressedKeyCode) {
switch (pressedKeyCode) {
case KeyCode.C:
void _handleShortcuts(KeyCommand cmd) {
switch (cmd) {
case KeyCommand.Copy:
case KeyCode.X:
case KeyCommand.Cut:
if (!this.selection.isCollapsed) {
Clipboard.setData(
new ClipboardData(text: this.selection.textInside(this.text.text)));

);
}
break;
case KeyCode.V:
case KeyCommand.Paste:
TextEditingValue value = this.textSelectionDelegate.textEditingValue;
Clipboard.getData(Clipboard.kTextPlain).Then(data => {
if (data != null) {

});
break;
case KeyCode.A:
case KeyCommand.SelectAll:
this._baseOffset = 0;
this._extentOffset = this.textSelectionDelegate.textEditingValue.text.Length;
this.onSelectionChanged(

}
}
void _handleDelete(bool backDel) {
void _handleDelete() {
if (backDel && selection.isCollapsed) {
if (selection.start <= 0) {
return;
}
selection = TextSelection.collapsed(selection.start - 1, selection.affinity);
}
if (selection.textAfter(this.text.text).isNotEmpty()) {
this.textSelectionDelegate.textEditingValue = new TextEditingValue(
text: selection.textBefore(this.text.text)

40
Runtime/service/keyboard.cs


namespace Unity.UIWidgets.service {
interface KeyboardDelegate: IDisposable {
void show();
void hide();

class DefaultKeyboardDelegate : KeyboardDelegate, TextInputOnGUIListener {
int _client;
string _lastCompositionString;
TextEditingValue _value;
public void show() {

if (this._client == 0) {
return;
}
var oldValue = this._value;
if (currentEvent.keyCode == KeyCode.Return) {
Window.instance.run(() => { TextInput._performAction(this._client, TextInputAction.newline); });
}
if (currentEvent.character != '\0') {
if (currentEvent.keyCode == KeyCode.Backspace) {
if (this._value.selection.isValid) {
this._value = this._value.deleteSelection(true);
}
} else if (currentEvent.character != '\0') {
this._value = this._value.clearCompose();
this._value = this._value.clearCompose();
if (ch == '\n') {
Window.instance.run(() => { TextInput._performAction(this._client, TextInputAction.newline); });
}
}
Window.instance.run(() => { TextInput._updateEditingState(this._client, this._value); });
}
} else if (!string.IsNullOrEmpty(Input.compositionString)) {
this._value = this._value.compose(Input.compositionString);
if (!string.IsNullOrEmpty(Input.compositionString) &&
this._lastCompositionString != Input.compositionString) {
this._value = this._value.compose(Input.compositionString);
if (this._value != oldValue) {
}
this._lastCompositionString = Input.compositionString;
}
}
public void Dispose() {

50
Runtime/service/raw_keyboard.cs


return;
}
var keyboardEvent = RawKeyEvent.fromEvent(evt);
var keyboardEvent = RawKeyEvent.processGUIEvent(evt);
foreach (var listener in new List<ValueChanged<RawKeyEvent>>(this._listeners)) {
if (this._listeners.Contains(listener)) {
listener(keyboardEvent);

this.data = data;
}
public static RawKeyEvent fromEvent(Event evt) {
public static RawKeyEvent processGUIEvent(Event evt) {
if (evt.type == EventType.ValidateCommand) {
var cmd = toKeyCommand(evt.commandName);
if (cmd != null) {
evt.Use();
return null;
}
} else if (evt.type == EventType.ExecuteCommand) { // Validate/ExecuteCommand is editor only
var cmd = toKeyCommand(evt.commandName);
if (cmd != null) {
return new RawKeyCommandEvent(new RawKeyEventData(evt), cmd.Value);
}
}
if (evt.type == EventType.KeyDown) {
return new RawKeyDownEvent(new RawKeyEventData(evt));
} else if (evt.type == EventType.KeyUp) {

}
public readonly RawKeyEventData data;
static KeyCommand? toKeyCommand(string commandName) {
switch (commandName) {
case "Paste":
return KeyCommand.Paste;
case "Copy":
return KeyCommand.Copy;
case "SelectAll":
return KeyCommand.SelectAll;
case "Cut":
return KeyCommand.Cut;
}
return null;
}
}
public class RawKeyDownEvent: RawKeyEvent {

}
}
public class RawKeyCommandEvent : RawKeyEvent {
public readonly KeyCommand command;
public RawKeyCommandEvent(RawKeyEventData data, KeyCommand command) : base(data) {
this.command = command;
}
}
public enum KeyCommand {
Copy,
Cut,
Paste,
SelectAll,
}
public class RawKeyEventData {
public readonly Event unityEvent;

9
Runtime/service/text_input.cs


return this.copyWith(
text: this.text.Substring(0, this.selection.start - 1) + this.selection.textAfter(this.text),
selection: TextSelection.collapsed(this.selection.start - 1));
selection: TextSelection.collapsed(this.selection.start - 1),
composing: TextRange.empty);
}
if (this.selection.start >= this.text.Length) {

return this.copyWith(text: this.text.Substring(0, this.selection.start) +
this.text.Substring(this.selection.start + 1));
this.text.Substring(this.selection.start + 1),
composing: TextRange.empty);
return this.copyWith(text: newText, selection: TextSelection.collapsed(this.selection.start));
return this.copyWith(text: newText, selection: TextSelection.collapsed(this.selection.start),
composing: TextRange.empty);
}
}

36
Runtime/ui/txt/paragraph.cs


List<PaintRecord> paintRecords = new List<PaintRecord>();
for (int i = 0; i < lineRuns.Count; ++i) {
var run = lineRuns[i];
string text = this._text;
layout.doLayout(runXOffset, this._text, textStart, textCount, run.style);
string ellipsis = this._paragraphStyle.ellipsis;
if (!string.IsNullOrEmpty(ellipsis) && !this._width.isInfinite() && !lineRange.hardBreak
&& i == lineRuns.Count - 1 && (lineNumber == lineLimit - 1 || this._paragraphStyle.maxLines == null)) {
float ellipsisWidth = Layout.measureText(runXOffset, ellipsis, 0,
ellipsis.Length, run.style, null, 0, this._tabStops);
List<float> textAdvances = new List<float>(textCount);
for (int index = 0; index < textCount;++index) {
textAdvances.Add(0);
}
float textWidth = Layout.measureText(runXOffset, this._text, textStart, textCount,
run.style, textAdvances, 0, this._tabStops);
int truncateCount = 0;
while (truncateCount < textCount &&
runXOffset + textWidth + ellipsisWidth > this._width) {
textWidth -= textAdvances[textCount - truncateCount - 1];
truncateCount++;
}
var ellipsizedText = this._text.Substring(textStart, textCount - truncateCount) + ellipsis;
textStart = 0;
textCount = ellipsizedText.Length;
text = ellipsizedText;
if (this._paragraphStyle.maxLines == null) {
lineLimit = lineNumber + 1;
this._didExceedMaxLines = true;
}
}
layout.doLayout(runXOffset, text, textStart, textCount, run.style);
if (layout.nGlyphs() == 0) {
continue;
}

builder.allocRunPos(run.style, this._text, textStart, textCount);
builder.allocRunPos(run.style, text, textStart, textCount);
builder.setBounds(layout.getBounds());
glyphPositions.Clear();

正在加载...
取消
保存