浏览代码

Merge pull request #159 from UnityTech/textselection

Textselection
/main
GitHub 6 年前
当前提交
1d3e556e
共有 4 个文件被更改,包括 140 次插入64 次删除
  1. 103
      Runtime/painting/image_cache.cs
  2. 16
      Runtime/painting/image_stream.cs
  3. 82
      Runtime/painting/text_painter.cs
  4. 3
      Samples/UIWidgetSample/AsScreenSample.cs

103
Runtime/painting/image_cache.cs


const int _kDefaultSize = 1000;
const int _kDefaultSizeBytes = 100 << 20; // 100 MiB
readonly Dictionary<object, ImageStreamCompleter> _pendingImages =
new Dictionary<object, ImageStreamCompleter>();
readonly Dictionary<object, _PendingImage> _pendingImages =
new Dictionary<object, _PendingImage>();
readonly Dictionary<object, _CachedImage> _cache = new Dictionary<object, _CachedImage>();
readonly LinkedList<object> _lruKeys = new LinkedList<object>();

public void clear() {
this._cache.Clear();
this._lruKeys.Clear();
this._pendingImages.Clear();
this._lruKeys.Clear();
if (this._pendingImages.TryGetValue(key, out var pendingImage)) {
pendingImage.removeListener();
this._pendingImages.Remove(key);
return true;
}
this._cache.Remove(image.node);
this._cache.Remove(key);
this._lruKeys.Remove(image.node);
return true;
}

public ImageStreamCompleter putIfAbsent(object key, Func<ImageStreamCompleter> loader) {
public ImageStreamCompleter putIfAbsent(object key, Func<ImageStreamCompleter> loader,
ImageErrorListener onError = null) {
if (this._pendingImages.TryGetValue(key, out var result)) {
ImageStreamCompleter result = null;
if (this._pendingImages.TryGetValue(key, out var pendingImage)) {
result = pendingImage.completer;
return result;
}

return image.completer;
}
result = loader();
try {
result = loader();
}
catch (Exception ex) {
if (onError != null) {
onError(ex);
}
else {
throw;
}
}
if (this._maximumSize > 0 && this._maximumSizeBytes > 0) {
D.assert(!this._pendingImages.ContainsKey(key));
this._pendingImages[key] = result;
void listener(ImageInfo info, bool syncCall) {
int imageSize = info?.image == null ? 0 : info.image.height * info.image.width * 4;
_CachedImage cachedImage = new _CachedImage(result, imageSize);
ImageListener listener = null;
listener = (info, syncCall) => {
result.removeListener(listener);
if (this.maximumSizeBytes > 0 && imageSize > this.maximumSizeBytes) {
this._maximumSizeBytes = imageSize + 1000;
}
this._currentSizeBytes += imageSize;
D.assert(this._pendingImages.ContainsKey(key));
if (this._pendingImages.TryGetValue(key, out var loadedPendingImage)) {
loadedPendingImage.removeListener();
int imageSize = info?.image == null ? 0 : info.image.width * info.image.height * 4;
_CachedImage cachedImage = new _CachedImage {
completer = result,
sizeBytes = imageSize,
};
// If the image is bigger than the maximum cache size, and the cache size
// is not zero, then increase the cache size to the size of the image plus
// some change.
if (this._maximumSizeBytes > 0 && imageSize > this._maximumSizeBytes) {
this._maximumSizeBytes = imageSize + 1000;
}
}
this._currentSizeBytes += imageSize;
D.assert(!this._cache.ContainsKey(key));
this._cache[key] = cachedImage;
cachedImage.node = this._lruKeys.AddLast(key);
this._checkCacheSize();
}
D.assert(!this._cache.ContainsKey(key));
this._cache[key] = cachedImage;
cachedImage.node = this._lruKeys.AddLast(key);
this._checkCacheSize();
};
if (this.maximumSize > 0 && this.maximumSizeBytes > 0) {
this._pendingImages[key] = new _PendingImage(result, listener);
result.addListener(listener);
}

}
class _CachedImage {
public _CachedImage(ImageStreamCompleter completer, int sizeBytes) {
this.completer = completer;
this.sizeBytes = sizeBytes;
}
}
class _PendingImage {
public _PendingImage(
ImageStreamCompleter completer,
ImageListener listener
) {
this.completer = completer;
this.listener = listener;
}
public readonly ImageStreamCompleter completer;
public readonly ImageListener listener;
public void removeListener() {
this.completer.removeListener(this.listener);
}
}
}

16
Runtime/painting/image_stream.cs


public ImageInfo currentImage;
public UIWidgetsErrorDetails currentError;
protected bool hasListeners {
get { return this._listeners.isNotEmpty(); }
}
public virtual void addListener(ImageListener listener, ImageErrorListener onError = null) {
this._listeners.Add(new _ImageListenerPair {listener = listener, errorListener = onError});

catch (Exception ex) {
UIWidgetsError.reportError(
new UIWidgetsErrorDetails(
context: "by an image error listener",
context: "when reporting an error to an image listener",
library: "image resource service",
exception: ex
)

}
void _handleAppFrame(TimeSpan timestamp) {
if (!this._hasActiveListeners) {
if (!this.hasListeners) {
return;
}

this._framesEmitted += 1;
}
bool _hasActiveListeners {
get { return this._listeners.isNotEmpty(); }
}
if (!this._hasActiveListeners && this._codec != null) {
if (!this.hasListeners && this._codec != null) {
this._decodeNextFrameAndSchedule();
}

public override void removeListener(ImageListener listener) {
base.removeListener(listener);
if (!this._hasActiveListeners) {
if (!this.hasListeners) {
this._timer?.cancel();
this._timer = null;
}

82
Runtime/painting/text_painter.cs


using System;
using System.Collections.Generic;
using System.Collections.Generic;
using Unity.UIWidgets.foundation;
using Unity.UIWidgets.service;
using Unity.UIWidgets.ui;

public TextSpan text {
get { return this._text; }
set {
if ((this._text == null && value == null) || (this._text != null && this.text.Equals(value))) {
if ((this._text == null && value == null) || (this._text != null && this.text.Equals(value))) {
return;
}

return Mathf.Ceil(layoutValue);
}
const int _zwjUtf16 = 0x200d;
var prevCodeUnit = this._text.codeUnitAt(offset - 1);
string flattenedText = this._text.toPlainText();
var prevCodeUnit = this._text.codeUnitAt(Mathf.Max(0, offset - 1));
var prevRuneOffset = _isUtf16Surrogate((int) prevCodeUnit) ? offset - 2 : offset - 1;
var boxes = this._paragraph.getRectsForRange(prevRuneOffset, offset);
if (boxes.Count == 0) {
return null;
bool needsSearch = _isUtf16Surrogate(prevCodeUnit.Value) || this._text.codeUnitAt(offset) == _zwjUtf16;
int graphemeClusterLength = needsSearch ? 2 : 1;
List<TextBox> boxes = null;
while ((boxes == null || boxes.isEmpty()) && flattenedText != null) {
int prevRuneOffset = offset - graphemeClusterLength;
boxes = this._paragraph.getRectsForRange(prevRuneOffset, offset);
if (boxes.isEmpty()) {
if (!needsSearch) {
break;
}
if (prevRuneOffset < -flattenedText.Length) {
break;
}
graphemeClusterLength *= 2;
continue;
}
TextBox box = boxes[0];
const int NEWLINE_CODE_UNIT = 10;
if (prevCodeUnit == NEWLINE_CODE_UNIT) {
return new Offset(this._emptyOffset.dx, box.bottom);
}
float caretEnd = box.end;
float dx = box.direction == TextDirection.rtl ? caretEnd - caretPrototype.width : caretEnd;
return new Offset(dx, box.top);
var box = boxes[0];
var caretEnd = box.end;
var dx = box.direction == TextDirection.rtl ? caretEnd : caretEnd - caretPrototype.width;
return new Offset(dx, box.top);
return null;
var nextCodeUnit = this._text.codeUnitAt(offset - 1);
string flattenedText = this._text.toPlainText();
var nextCodeUnit =
this._text.codeUnitAt(Mathf.Min(offset, flattenedText == null ? 0 : flattenedText.Length - 1));
var nextRuneOffset = _isUtf16Surrogate((int) nextCodeUnit) ? offset + 2 : offset + 1;
var boxes = this._paragraph.getRectsForRange(offset, nextRuneOffset);
if (boxes.Count == 0) {
return null;
bool needsSearch = _isUtf16Surrogate(nextCodeUnit.Value) || nextCodeUnit == _zwjUtf16;
int graphemeClusterLength = needsSearch ? 2 : 1;
List<TextBox> boxes = null;
while ((boxes == null || boxes.isEmpty()) && flattenedText != null) {
int nextRuneOffset = offset + graphemeClusterLength;
boxes = this._paragraph.getRectsForRange(offset, nextRuneOffset);
if (boxes.isEmpty()) {
if (!needsSearch) {
break;
}
if (nextRuneOffset >= flattenedText.Length << 1) {
break;
}
graphemeClusterLength *= 2;
continue;
}
TextBox box = boxes[boxes.Count - 1];
float caretStart = box.start;
float dx = box.direction == TextDirection.rtl ? caretStart - caretPrototype.width : caretStart;
return new Offset(dx, box.top);
var box = boxes[0];
var caretStart = box.start;
var dx = box.direction == TextDirection.rtl ? caretStart - caretPrototype.width : caretStart;
return new Offset(dx, box.top);
return null;
}
Offset _emptyOffset {

3
Samples/UIWidgetSample/AsScreenSample.cs


fontSize: 16
),
selectionColor: Color.fromARGB(255, 255, 0, 0),
cursorColor: Color.fromARGB(255, 0, 0, 0)
cursorColor: Color.fromARGB(255, 0, 0, 0),
backgroundCursorColor: Colors.blue
)
),
new Container(

正在加载...
取消
保存