Vincent Zhang
4 年前
当前提交
81c1d349
共有 89 个文件被更改,包括 3274 次插入 和 3613 次删除
-
4com.unity.uiwidgets/Runtime/widgets/banner.cs.meta
-
4com.unity.uiwidgets/Runtime/widgets/basic.cs.meta
-
4com.unity.uiwidgets/Runtime/widgets/binding.cs.meta
-
4com.unity.uiwidgets/Runtime/widgets/bottom_navigation_bar_item.cs.meta
-
4com.unity.uiwidgets/Runtime/widgets/container.cs.meta
-
4com.unity.uiwidgets/Runtime/widgets/dismissible.cs.meta
-
4com.unity.uiwidgets/Runtime/widgets/form.cs.meta
-
4com.unity.uiwidgets/Runtime/widgets/heroes.cs.meta
-
4com.unity.uiwidgets/Runtime/widgets/image.cs.meta
-
4com.unity.uiwidgets/Runtime/widgets/overscroll_indicator.cs.meta
-
4com.unity.uiwidgets/Runtime/widgets/sliver_persistent_header.cs.meta
-
4com.unity.uiwidgets/Runtime/widgets/ticker_provider.cs.meta
-
124com.unity.uiwidgets/Runtime/widgets/value_listenable_builder.cs
-
4com.unity.uiwidgets/Runtime/widgets/visibility.cs.meta
-
4com.unity.uiwidgets/Runtime/widgets/will_pop_scope.cs.meta
-
1001com.unity.uiwidgets/Runtime/ui/txt/emoji.cs
-
564com.unity.uiwidgets/Runtime/ui/txt/layout.cs
-
694com.unity.uiwidgets/Runtime/ui/txt/linebreaker.cs
-
4com.unity.uiwidgets/Runtime/ui/painting/image.cs.meta
-
192com.unity.uiwidgets/Runtime/ui/painting/txt/text_blob.cs
-
8com.unity.uiwidgets/Runtime/rendering/debug.cs
-
4com.unity.uiwidgets/Runtime/rendering/error.cs.meta
-
4com.unity.uiwidgets/Runtime/rendering/image.cs.meta
-
4com.unity.uiwidgets/Runtime/rendering/rotated_box.cs.meta
-
4com.unity.uiwidgets/Runtime/rendering/sliver_persistent_header.cs.meta
-
4com.unity.uiwidgets/Runtime/rendering/wrap.cs.meta
-
4com.unity.uiwidgets/Runtime/promise/EnumerableExt.cs.meta
-
4com.unity.uiwidgets/Runtime/promise/Promise.cs.meta
-
4com.unity.uiwidgets/Runtime/promise/PromiseException.cs.meta
-
4com.unity.uiwidgets/Runtime/promise/PromiseHelpers.cs.meta
-
4com.unity.uiwidgets/Runtime/promise/PromiseStateException.cs.meta
-
4com.unity.uiwidgets/Runtime/promise/PromiseTimer.cs.meta
-
4com.unity.uiwidgets/Runtime/promise/Promise_NonGeneric.cs.meta
-
4com.unity.uiwidgets/Runtime/promise/Tuple.cs.meta
-
4com.unity.uiwidgets/Runtime/painting/box_fit.cs.meta
-
4com.unity.uiwidgets/Runtime/painting/image_cache.cs.meta
-
4com.unity.uiwidgets/Runtime/painting/image_provider.cs.meta
-
4com.unity.uiwidgets/Runtime/painting/image_stream.cs.meta
-
4com.unity.uiwidgets/Runtime/painting/notched_shapes.cs.meta
-
438com.unity.uiwidgets/Runtime/painting/strut_style.cs
-
4com.unity.uiwidgets/Runtime/material/app_bar_theme.cs.meta
-
4com.unity.uiwidgets/Runtime/material/bottom_app_bar.cs.meta
-
4com.unity.uiwidgets/Runtime/material/bottom_app_bar_theme.cs.meta
-
4com.unity.uiwidgets/Runtime/material/bottom_navigation_bar.cs.meta
-
4com.unity.uiwidgets/Runtime/material/card_theme.cs.meta
-
4com.unity.uiwidgets/Runtime/material/chip.cs.meta
-
4com.unity.uiwidgets/Runtime/material/chip_theme.cs.meta
-
4com.unity.uiwidgets/Runtime/material/circle_avatar.cs.meta
-
4com.unity.uiwidgets/Runtime/material/dropdown.cs.meta
-
256com.unity.uiwidgets/Runtime/material/floatting_action_button_theme.cs
-
4com.unity.uiwidgets/Runtime/material/input_border.cs.meta
-
4com.unity.uiwidgets/Runtime/material/input_decorator.cs.meta
-
4com.unity.uiwidgets/Runtime/material/outline_button.cs.meta
-
4com.unity.uiwidgets/Runtime/material/progress_indicator.cs.meta
-
4com.unity.uiwidgets/Runtime/material/radio.cs.meta
-
4com.unity.uiwidgets/Runtime/material/refresh_indicator.cs.meta
-
4com.unity.uiwidgets/Runtime/material/text_form_field.cs.meta
-
4com.unity.uiwidgets/Runtime/material/user_accounts_drawer_header.cs.meta
-
4com.unity.uiwidgets/Runtime/material/animated_icons/animated_icons_data.cs.meta
-
4com.unity.uiwidgets/Runtime/material/animated_icons/data/arrow_menu.g.cs.meta
-
4com.unity.uiwidgets/Runtime/material/animated_icons/data/close_menu.g.cs.meta
-
4com.unity.uiwidgets/Runtime/foundation/constants.cs.meta
-
406com.unity.uiwidgets/Runtime/cupertino/bottom_app_bar.cs
-
1001com.unity.uiwidgets/Runtime/cupertino/nav_bar.cs
-
758com.unity.uiwidgets/Runtime/cupertino/slider.cs
-
440com.unity.uiwidgets/Runtime/cupertino/tab_scaffold.cs
-
789com.unity.uiwidgets/Runtime/Plugins/platform/ios/UIWidgetsTextInputPlugin.mm
-
0/com.unity.uiwidgets/.npmignore
-
0/com.unity.uiwidgets/CHANGELOG.md
-
0/com.unity.uiwidgets/CHANGELOG.md.meta
-
0/com.unity.uiwidgets/Documentation~
-
0/com.unity.uiwidgets/Editor.meta
-
0/com.unity.uiwidgets/Editor
-
0/com.unity.uiwidgets/LICENSE.md
-
0/com.unity.uiwidgets/LICENSE.md.meta
-
0/com.unity.uiwidgets/QAReport.md
-
0/com.unity.uiwidgets/QAReport.md.meta
-
0/com.unity.uiwidgets/Runtime.meta
-
0/com.unity.uiwidgets/Third Party Notices.md
-
0/com.unity.uiwidgets/Third Party Notices.md.meta
-
0/com.unity.uiwidgets/package.json
-
0/com.unity.uiwidgets/package.json.meta
-
0/com.unity.uiwidgets/scripts.meta
-
0/com.unity.uiwidgets/.editorconfig
-
0/com.unity.uiwidgets/UIWidgetCleanupPlugin.DotSettings.meta
-
0/com.unity.uiwidgets/UIWidgetCleanupPlugin.DotSettings
-
0/com.unity.uiwidgets/Scripts
-
0/com.unity.uiwidgets/Runtime
|
|||
fileFormatVersion: 2 |
|||
guid: e2e859ca92594b5d98bcce5ce505ca7b |
|||
fileFormatVersion: 2 |
|||
guid: e2e859ca92594b5d98bcce5ce505ca7b |
|||
timeCreated: 1552882905 |
|
|||
fileFormatVersion: 2 |
|||
guid: 69241aaa538e4e2baa651ffa39d6f355 |
|||
fileFormatVersion: 2 |
|||
guid: 69241aaa538e4e2baa651ffa39d6f355 |
|||
timeCreated: 1536564821 |
|
|||
fileFormatVersion: 2 |
|||
guid: 39d3379616cb4c64afdedd889e3ac2a2 |
|||
fileFormatVersion: 2 |
|||
guid: 39d3379616cb4c64afdedd889e3ac2a2 |
|||
timeCreated: 1536213633 |
|
|||
fileFormatVersion: 2 |
|||
guid: 7000f1f897684e08a0831ca5d920589f |
|||
fileFormatVersion: 2 |
|||
guid: 7000f1f897684e08a0831ca5d920589f |
|||
timeCreated: 1553162159 |
|
|||
fileFormatVersion: 2 |
|||
guid: 7c96e4ace3a14122bc2107afe55771da |
|||
fileFormatVersion: 2 |
|||
guid: 7c96e4ace3a14122bc2107afe55771da |
|||
timeCreated: 1536831109 |
|
|||
fileFormatVersion: 2 |
|||
guid: abd926e0982c44ca80edbb282a52b90b |
|||
fileFormatVersion: 2 |
|||
guid: abd926e0982c44ca80edbb282a52b90b |
|||
timeCreated: 1552033946 |
|
|||
fileFormatVersion: 2 |
|||
guid: 3ce8dad5676b40ec862b142dd03b9f64 |
|||
fileFormatVersion: 2 |
|||
guid: 3ce8dad5676b40ec862b142dd03b9f64 |
|||
timeCreated: 1552545243 |
|
|||
fileFormatVersion: 2 |
|||
guid: ae21d125110c4c989fd2ce9e5a0bc1c0 |
|||
fileFormatVersion: 2 |
|||
guid: ae21d125110c4c989fd2ce9e5a0bc1c0 |
|||
timeCreated: 1554263028 |
|
|||
fileFormatVersion: 2 |
|||
guid: d1d313d74f6044198caad151f52721b8 |
|||
fileFormatVersion: 2 |
|||
guid: d1d313d74f6044198caad151f52721b8 |
|||
timeCreated: 1536219326 |
|
|||
fileFormatVersion: 2 |
|||
guid: b87c58140c4b442e82a27b804f8502c7 |
|||
fileFormatVersion: 2 |
|||
guid: b87c58140c4b442e82a27b804f8502c7 |
|||
timeCreated: 1554348404 |
|
|||
fileFormatVersion: 2 |
|||
guid: 1e795962fe2d4c3291bbbe4bb1146a80 |
|||
fileFormatVersion: 2 |
|||
guid: 1e795962fe2d4c3291bbbe4bb1146a80 |
|||
timeCreated: 1553067038 |
|
|||
fileFormatVersion: 2 |
|||
guid: aa6922a60d7945ffb3b92dede5941836 |
|||
fileFormatVersion: 2 |
|||
guid: aa6922a60d7945ffb3b92dede5941836 |
|||
timeCreated: 1536807803 |
|
|||
using Unity.UIWidgets.foundation; |
|||
|
|||
namespace Unity.UIWidgets.widgets { |
|||
public delegate Widget ValueWidgetBuilder<T>(BuildContext context, T value, Widget child); |
|||
|
|||
public class ValueListenableBuilder<T> : StatefulWidget { |
|||
public ValueListenableBuilder( |
|||
ValueListenable<T> valueListenable, |
|||
ValueWidgetBuilder<T> builder, |
|||
Widget child = null |
|||
) { |
|||
D.assert(valueListenable != null); |
|||
D.assert(builder != null); |
|||
this.valueListenable = valueListenable; |
|||
this.builder = builder; |
|||
this.child = child; |
|||
} |
|||
|
|||
public readonly ValueListenable<T> valueListenable; |
|||
|
|||
public readonly ValueWidgetBuilder<T> builder; |
|||
|
|||
public readonly Widget child; |
|||
|
|||
public override State createState() { |
|||
return new _ValueListenableBuilderState<T>(); |
|||
} |
|||
} |
|||
|
|||
class _ValueListenableBuilderState<T> : State<ValueListenableBuilder<T>> { |
|||
T value; |
|||
|
|||
public override void initState() { |
|||
base.initState(); |
|||
this.value = this.widget.valueListenable.value; |
|||
this.widget.valueListenable.addListener(this._valueChanged); |
|||
} |
|||
|
|||
public override void didUpdateWidget(StatefulWidget _oldWidget) { |
|||
ValueListenableBuilder<T> oldWidget = _oldWidget as ValueListenableBuilder<T>; |
|||
if (oldWidget.valueListenable != this.widget.valueListenable) { |
|||
oldWidget.valueListenable.removeListener(this._valueChanged); |
|||
this.value = this.widget.valueListenable.value; |
|||
this.widget.valueListenable.addListener(this._valueChanged); |
|||
} |
|||
|
|||
base.didUpdateWidget(oldWidget); |
|||
} |
|||
|
|||
public override void dispose() { |
|||
this.widget.valueListenable.removeListener(this._valueChanged); |
|||
base.dispose(); |
|||
} |
|||
|
|||
void _valueChanged() { |
|||
this.setState(() => { this.value = this.widget.valueListenable.value; }); |
|||
} |
|||
|
|||
public override Widget build(BuildContext context) { |
|||
return this.widget.builder(context, this.value, this.widget.child); |
|||
} |
|||
} |
|||
using Unity.UIWidgets.foundation; |
|||
|
|||
namespace Unity.UIWidgets.widgets { |
|||
public delegate Widget ValueWidgetBuilder<T>(BuildContext context, T value, Widget child); |
|||
|
|||
public class ValueListenableBuilder<T> : StatefulWidget { |
|||
public ValueListenableBuilder( |
|||
ValueListenable<T> valueListenable, |
|||
ValueWidgetBuilder<T> builder, |
|||
Widget child = null |
|||
) { |
|||
D.assert(valueListenable != null); |
|||
D.assert(builder != null); |
|||
this.valueListenable = valueListenable; |
|||
this.builder = builder; |
|||
this.child = child; |
|||
} |
|||
|
|||
public readonly ValueListenable<T> valueListenable; |
|||
|
|||
public readonly ValueWidgetBuilder<T> builder; |
|||
|
|||
public readonly Widget child; |
|||
|
|||
public override State createState() { |
|||
return new _ValueListenableBuilderState<T>(); |
|||
} |
|||
} |
|||
|
|||
class _ValueListenableBuilderState<T> : State<ValueListenableBuilder<T>> { |
|||
T value; |
|||
|
|||
public override void initState() { |
|||
base.initState(); |
|||
this.value = this.widget.valueListenable.value; |
|||
this.widget.valueListenable.addListener(this._valueChanged); |
|||
} |
|||
|
|||
public override void didUpdateWidget(StatefulWidget _oldWidget) { |
|||
ValueListenableBuilder<T> oldWidget = _oldWidget as ValueListenableBuilder<T>; |
|||
if (oldWidget.valueListenable != this.widget.valueListenable) { |
|||
oldWidget.valueListenable.removeListener(this._valueChanged); |
|||
this.value = this.widget.valueListenable.value; |
|||
this.widget.valueListenable.addListener(this._valueChanged); |
|||
} |
|||
|
|||
base.didUpdateWidget(oldWidget); |
|||
} |
|||
|
|||
public override void dispose() { |
|||
this.widget.valueListenable.removeListener(this._valueChanged); |
|||
base.dispose(); |
|||
} |
|||
|
|||
void _valueChanged() { |
|||
this.setState(() => { this.value = this.widget.valueListenable.value; }); |
|||
} |
|||
|
|||
public override Widget build(BuildContext context) { |
|||
return this.widget.builder(context, this.value, this.widget.child); |
|||
} |
|||
} |
|||
} |
|
|||
fileFormatVersion: 2 |
|||
guid: f24846c5a2354d9a8b02bf83f3e8398e |
|||
fileFormatVersion: 2 |
|||
guid: f24846c5a2354d9a8b02bf83f3e8398e |
|||
timeCreated: 1552895385 |
|
|||
fileFormatVersion: 2 |
|||
guid: 624e6292cdfc4b43ac439df81ae8efee |
|||
fileFormatVersion: 2 |
|||
guid: 624e6292cdfc4b43ac439df81ae8efee |
|||
timeCreated: 1552547854 |
1001
com.unity.uiwidgets/Runtime/ui/txt/emoji.cs
文件差异内容过多而无法显示
查看文件
文件差异内容过多而无法显示
查看文件
|
|||
using UnityEngine; |
|||
|
|||
namespace Unity.UIWidgets.ui { |
|||
static class Layout { |
|||
// Measure the length of the span of the text. Currently, this is only used to compute the length
|
|||
// of ellipsis, assuming that the ellipsis does not contain any tab, tab is not considered for simplicity
|
|||
public static float measureText(string text, TextStyle style) { |
|||
char startingChar = text[0]; |
|||
float totalWidth = 0; |
|||
if (char.IsHighSurrogate(startingChar) || EmojiUtils.isSingleCharEmoji(startingChar)) { |
|||
float advance = style.fontSize * EmojiUtils.advanceFactor + style.letterSpacing; |
|||
for (int i = 0; i < text.Length; i++) { |
|||
char ch = text[i]; |
|||
if (char.IsHighSurrogate(ch) || EmojiUtils.isSingleCharNonEmptyEmoji(ch)) { |
|||
totalWidth += advance; |
|||
} |
|||
} |
|||
} |
|||
else { |
|||
Font font = FontManager.instance.getOrCreate(style.fontFamily, style.fontWeight, style.fontStyle).font; |
|||
font.RequestCharactersInTextureSafe(text, style.UnityFontSize, style.UnityFontStyle); |
|||
for (int i = 0; i < text.Length; i++) { |
|||
char ch = text[i]; |
|||
if (font.getGlyphInfo(ch, out var glyphInfo, style.UnityFontSize, style.UnityFontStyle)) { |
|||
totalWidth += glyphInfo.advance + style.letterSpacing; |
|||
} |
|||
else { |
|||
totalWidth += style.letterSpacing; |
|||
} |
|||
|
|||
if (LayoutUtils.isWordSpace(ch)) { |
|||
totalWidth += style.wordSpacing; |
|||
} |
|||
} |
|||
} |
|||
|
|||
return totalWidth; |
|||
} |
|||
|
|||
public static int computeTruncateCount(float offset, string text, int start, int count, TextStyle style, |
|||
float advanceLimit, TabStops tabStops) { |
|||
char startingChar = text[start]; |
|||
float currentAdvance = offset; |
|||
if (char.IsHighSurrogate(startingChar) || EmojiUtils.isSingleCharEmoji(startingChar)) { |
|||
float advance = style.fontSize * EmojiUtils.advanceFactor + style.letterSpacing; |
|||
for (int i = 0; i < count; i++) { |
|||
char ch = text[start + i]; |
|||
if (char.IsHighSurrogate(ch) || EmojiUtils.isSingleCharNonEmptyEmoji(ch)) { |
|||
currentAdvance += advance; |
|||
if (currentAdvance > advanceLimit) { |
|||
return count - i; |
|||
} |
|||
} |
|||
} |
|||
} |
|||
else { |
|||
Font font = FontManager.instance.getOrCreate(style.fontFamily, style.fontWeight, style.fontStyle).font; |
|||
for (int i = 0; i < count; i++) { |
|||
char ch = text[start + i]; |
|||
if (ch == '\t') { |
|||
currentAdvance = tabStops.nextTab(currentAdvance); |
|||
} |
|||
else if (font.getGlyphInfo(ch, out var glyphInfo, style.UnityFontSize, style.UnityFontStyle)) { |
|||
currentAdvance += glyphInfo.advance + style.letterSpacing; |
|||
} |
|||
else { |
|||
currentAdvance += style.letterSpacing; |
|||
} |
|||
|
|||
if (LayoutUtils.isWordSpace(ch)) { |
|||
currentAdvance += style.wordSpacing; |
|||
} |
|||
|
|||
if (currentAdvance > advanceLimit) { |
|||
return count - i; |
|||
} |
|||
} |
|||
} |
|||
|
|||
return 0; |
|||
} |
|||
|
|||
public static float computeCharWidths(float offset, string text, int start, int count, TextStyle style, |
|||
float[] advances, int advanceOffset, TabStops tabStops) { |
|||
char startingChar = text[start]; |
|||
float totalWidths = 0; |
|||
if (char.IsHighSurrogate(startingChar) || EmojiUtils.isSingleCharEmoji(startingChar)) { |
|||
float advance = style.fontSize * EmojiUtils.advanceFactor + style.letterSpacing; |
|||
for (int i = 0; i < count; i++) { |
|||
char ch = text[start + i]; |
|||
if (char.IsHighSurrogate(ch) || EmojiUtils.isSingleCharNonEmptyEmoji(ch)) { |
|||
advances[i + advanceOffset] = advance; |
|||
totalWidths += advance; |
|||
} |
|||
else { |
|||
advances[i + advanceOffset] = 0; |
|||
} |
|||
} |
|||
} |
|||
else { |
|||
Font font = FontManager.instance.getOrCreate(style.fontFamily, style.fontWeight, style.fontStyle).font; |
|||
// TODO: it is kind of a waste to require the entire string for this style, but SubString causes alloc
|
|||
font.RequestCharactersInTextureSafe(text, style.UnityFontSize, style.UnityFontStyle); |
|||
for (int i = 0; i < count; i++) { |
|||
char ch = text[start + i]; |
|||
if (ch == '\t') { |
|||
advances[i + advanceOffset] = tabStops.nextTab(offset + totalWidths) - (offset + totalWidths); |
|||
} |
|||
else if (font.getGlyphInfo(ch, out var glyphInfo, style.UnityFontSize, style.UnityFontStyle)) { |
|||
advances[i + advanceOffset] = glyphInfo.advance + style.letterSpacing; |
|||
} |
|||
else { |
|||
advances[i + advanceOffset] = style.letterSpacing; |
|||
} |
|||
|
|||
if (LayoutUtils.isWordSpace(ch)) { |
|||
advances[i + advanceOffset] += style.wordSpacing; |
|||
} |
|||
|
|||
totalWidths += advances[i + advanceOffset]; |
|||
} |
|||
} |
|||
|
|||
return totalWidths; |
|||
} |
|||
|
|||
public static float doLayout(float offset, string text, int start, int count, TextStyle style, |
|||
float[] advances, float[] positions, TabStops tabStops, out UnityEngine.Rect bounds) { |
|||
float advance = 0; |
|||
Font font = FontManager.instance.getOrCreate(style.fontFamily, style.fontWeight, style.fontStyle).font; |
|||
|
|||
char startingChar = text[start]; |
|||
bounds = new UnityEngine.Rect(); |
|||
if (char.IsHighSurrogate(startingChar) || EmojiUtils.isSingleCharEmoji(startingChar)) { |
|||
advance = _layoutEmoji(text, start, count, style, font, advances, positions, ref bounds); |
|||
} |
|||
else { |
|||
// According to the logic of Paragraph.layout, it is assured that all the characters are requested
|
|||
// in the texture before (in computing line breaks), so skip it here for optimization.
|
|||
// The only exception is the ellipsis, which did not appear in line breaking. It is taken care of
|
|||
// only when needed.
|
|||
|
|||
// font.RequestCharactersInTextureSafe(buff.text, style.UnityFontSize, style.UnityFontStyle);
|
|||
|
|||
// int wordstart = start == buff.size
|
|||
// ? start
|
|||
// : LayoutUtils.getPrevWordBreakForCache(buff, start + 1);
|
|||
int wordend; |
|||
for (int iter = start; iter < start + count; iter = wordend) { |
|||
wordend = LayoutUtils.getNextWordBreak(text, iter, start + count); |
|||
advance = _layoutWord(offset, iter - start, text, iter, |
|||
wordend - iter, style, font, advances, positions, advance, |
|||
tabStops, ref bounds); |
|||
} |
|||
} |
|||
|
|||
// bounds relative to first character
|
|||
bounds.x -= positions[0]; |
|||
return advance; |
|||
} |
|||
|
|||
static float _layoutWord(float offset, int layoutOffset, |
|||
string text, int start, int wordCount, TextStyle style, Font font, float[] advances, |
|||
float[] positions, float initAdvance, TabStops tabStops, ref UnityEngine.Rect bounds) { |
|||
float wordSpacing = |
|||
wordCount == 1 && LayoutUtils.isWordSpace(text[start]) ? style.wordSpacing : 0; |
|||
|
|||
float x = initAdvance; |
|||
float letterSpace = style.letterSpacing; |
|||
float letterSpaceHalfLeft = letterSpace * 0.5f; |
|||
float letterSpaceHalfRight = letterSpace - letterSpaceHalfLeft; |
|||
|
|||
for (int i = 0; i < wordCount; i++) { |
|||
initAdvance = x; |
|||
var ch = text[start + i]; |
|||
if (i == 0) { |
|||
x += letterSpaceHalfLeft + wordSpacing; |
|||
advances[i + layoutOffset] = letterSpaceHalfLeft + wordSpacing; |
|||
} |
|||
else { |
|||
advances[i - 1 + layoutOffset] += letterSpaceHalfRight; |
|||
advances[i + layoutOffset] = letterSpaceHalfLeft; |
|||
|
|||
x += letterSpace; |
|||
} |
|||
|
|||
if (font.getGlyphInfo(ch, out var glyphInfo, style.UnityFontSize, style.UnityFontStyle)) { |
|||
_updateBounds(glyphInfo, x, ref bounds); |
|||
} |
|||
|
|||
positions[i + layoutOffset] = x; |
|||
|
|||
float advance; |
|||
if (ch == '\t') { |
|||
advance = tabStops.nextTab(initAdvance + offset) - initAdvance - offset; |
|||
} |
|||
else { |
|||
advance = glyphInfo.advance; |
|||
} |
|||
|
|||
x += advance; |
|||
advances[i + layoutOffset] += advance; |
|||
|
|||
if (i + 1 == wordCount) { |
|||
advances[i + layoutOffset] += letterSpaceHalfRight; |
|||
x += letterSpaceHalfRight; |
|||
} |
|||
} |
|||
|
|||
return x; |
|||
} |
|||
|
|||
static float _layoutEmoji(string text, int start, int count, TextStyle style, Font font, float[] advances, |
|||
float[] positions, ref UnityEngine.Rect bounds) { |
|||
var metrics = FontMetrics.fromFont(font, style.UnityFontSize); |
|||
float x = 0; |
|||
for (int i = 0; i < count; i++) { |
|||
char c = text[start + i]; |
|||
if (EmojiUtils.isSingleCharNonEmptyEmoji(c) || char.IsHighSurrogate(c)) { |
|||
float letterSpace = style.letterSpacing; |
|||
float letterSpaceHalfLeft = letterSpace * 0.5f; |
|||
float letterSpaceHalfRight = letterSpace - letterSpaceHalfLeft; |
|||
|
|||
x += letterSpaceHalfLeft; |
|||
advances[i] = letterSpaceHalfLeft; |
|||
|
|||
float advance = style.fontSize * EmojiUtils.advanceFactor; |
|||
var minX = x; |
|||
var maxX = advance + x; |
|||
var minY = -style.fontSize * EmojiUtils.sizeFactor; |
|||
var maxY = metrics.descent; |
|||
_updateBounds(minX, maxX, minY, maxY, ref bounds); |
|||
|
|||
positions[i] = x; |
|||
|
|||
x += advance; |
|||
|
|||
advances[i] += advance; |
|||
advances[i] += letterSpaceHalfRight; |
|||
|
|||
x += letterSpaceHalfRight; |
|||
} |
|||
else { |
|||
advances[i] = 0; |
|||
positions[i] = x; |
|||
} |
|||
} |
|||
|
|||
return x; |
|||
} |
|||
|
|||
static void _updateBounds(CharacterInfo glyphInfo, float x, ref UnityEngine.Rect bounds) { |
|||
var minX = glyphInfo.minX + x; |
|||
var maxX = glyphInfo.maxX + x; |
|||
var minY = -glyphInfo.maxY; |
|||
var maxY = -glyphInfo.minY; |
|||
_updateBounds(minX, maxX, minY, maxY, ref bounds); |
|||
} |
|||
|
|||
static void _updateBounds(float minX, float maxX, float minY, float maxY, ref UnityEngine.Rect bounds) { |
|||
if (bounds.width <= 0 || bounds.height <= 0) { |
|||
bounds.Set(minX, minY, maxX - minX, maxY - minY); |
|||
} |
|||
else { |
|||
if (minX < bounds.x) { |
|||
bounds.x = minX; |
|||
} |
|||
|
|||
if (minY < bounds.y) { |
|||
bounds.y = minY; |
|||
} |
|||
|
|||
if (maxX > bounds.xMax) { |
|||
bounds.xMax = maxX; |
|||
} |
|||
|
|||
if (maxY > bounds.yMax) { |
|||
bounds.yMax = maxY; |
|||
} |
|||
} |
|||
} |
|||
} |
|||
using UnityEngine; |
|||
|
|||
namespace Unity.UIWidgets.ui { |
|||
static class Layout { |
|||
// Measure the length of the span of the text. Currently, this is only used to compute the length
|
|||
// of ellipsis, assuming that the ellipsis does not contain any tab, tab is not considered for simplicity
|
|||
public static float measureText(string text, TextStyle style) { |
|||
char startingChar = text[0]; |
|||
float totalWidth = 0; |
|||
if (char.IsHighSurrogate(startingChar) || EmojiUtils.isSingleCharEmoji(startingChar)) { |
|||
float advance = style.fontSize * EmojiUtils.advanceFactor + style.letterSpacing; |
|||
for (int i = 0; i < text.Length; i++) { |
|||
char ch = text[i]; |
|||
if (char.IsHighSurrogate(ch) || EmojiUtils.isSingleCharNonEmptyEmoji(ch)) { |
|||
totalWidth += advance; |
|||
} |
|||
} |
|||
} |
|||
else { |
|||
Font font = FontManager.instance.getOrCreate(style.fontFamily, style.fontWeight, style.fontStyle).font; |
|||
font.RequestCharactersInTextureSafe(text, style.UnityFontSize, style.UnityFontStyle); |
|||
for (int i = 0; i < text.Length; i++) { |
|||
char ch = text[i]; |
|||
if (font.getGlyphInfo(ch, out var glyphInfo, style.UnityFontSize, style.UnityFontStyle)) { |
|||
totalWidth += glyphInfo.advance + style.letterSpacing; |
|||
} |
|||
else { |
|||
totalWidth += style.letterSpacing; |
|||
} |
|||
|
|||
if (LayoutUtils.isWordSpace(ch)) { |
|||
totalWidth += style.wordSpacing; |
|||
} |
|||
} |
|||
} |
|||
|
|||
return totalWidth; |
|||
} |
|||
|
|||
public static int computeTruncateCount(float offset, string text, int start, int count, TextStyle style, |
|||
float advanceLimit, TabStops tabStops) { |
|||
char startingChar = text[start]; |
|||
float currentAdvance = offset; |
|||
if (char.IsHighSurrogate(startingChar) || EmojiUtils.isSingleCharEmoji(startingChar)) { |
|||
float advance = style.fontSize * EmojiUtils.advanceFactor + style.letterSpacing; |
|||
for (int i = 0; i < count; i++) { |
|||
char ch = text[start + i]; |
|||
if (char.IsHighSurrogate(ch) || EmojiUtils.isSingleCharNonEmptyEmoji(ch)) { |
|||
currentAdvance += advance; |
|||
if (currentAdvance > advanceLimit) { |
|||
return count - i; |
|||
} |
|||
} |
|||
} |
|||
} |
|||
else { |
|||
Font font = FontManager.instance.getOrCreate(style.fontFamily, style.fontWeight, style.fontStyle).font; |
|||
for (int i = 0; i < count; i++) { |
|||
char ch = text[start + i]; |
|||
if (ch == '\t') { |
|||
currentAdvance = tabStops.nextTab(currentAdvance); |
|||
} |
|||
else if (font.getGlyphInfo(ch, out var glyphInfo, style.UnityFontSize, style.UnityFontStyle)) { |
|||
currentAdvance += glyphInfo.advance + style.letterSpacing; |
|||
} |
|||
else { |
|||
currentAdvance += style.letterSpacing; |
|||
} |
|||
|
|||
if (LayoutUtils.isWordSpace(ch)) { |
|||
currentAdvance += style.wordSpacing; |
|||
} |
|||
|
|||
if (currentAdvance > advanceLimit) { |
|||
return count - i; |
|||
} |
|||
} |
|||
} |
|||
|
|||
return 0; |
|||
} |
|||
|
|||
public static float computeCharWidths(float offset, string text, int start, int count, TextStyle style, |
|||
float[] advances, int advanceOffset, TabStops tabStops) { |
|||
char startingChar = text[start]; |
|||
float totalWidths = 0; |
|||
if (char.IsHighSurrogate(startingChar) || EmojiUtils.isSingleCharEmoji(startingChar)) { |
|||
float advance = style.fontSize * EmojiUtils.advanceFactor + style.letterSpacing; |
|||
for (int i = 0; i < count; i++) { |
|||
char ch = text[start + i]; |
|||
if (char.IsHighSurrogate(ch) || EmojiUtils.isSingleCharNonEmptyEmoji(ch)) { |
|||
advances[i + advanceOffset] = advance; |
|||
totalWidths += advance; |
|||
} |
|||
else { |
|||
advances[i + advanceOffset] = 0; |
|||
} |
|||
} |
|||
} |
|||
else { |
|||
Font font = FontManager.instance.getOrCreate(style.fontFamily, style.fontWeight, style.fontStyle).font; |
|||
// TODO: it is kind of a waste to require the entire string for this style, but SubString causes alloc
|
|||
font.RequestCharactersInTextureSafe(text, style.UnityFontSize, style.UnityFontStyle); |
|||
for (int i = 0; i < count; i++) { |
|||
char ch = text[start + i]; |
|||
if (ch == '\t') { |
|||
advances[i + advanceOffset] = tabStops.nextTab(offset + totalWidths) - (offset + totalWidths); |
|||
} |
|||
else if (font.getGlyphInfo(ch, out var glyphInfo, style.UnityFontSize, style.UnityFontStyle)) { |
|||
advances[i + advanceOffset] = glyphInfo.advance + style.letterSpacing; |
|||
} |
|||
else { |
|||
advances[i + advanceOffset] = style.letterSpacing; |
|||
} |
|||
|
|||
if (LayoutUtils.isWordSpace(ch)) { |
|||
advances[i + advanceOffset] += style.wordSpacing; |
|||
} |
|||
|
|||
totalWidths += advances[i + advanceOffset]; |
|||
} |
|||
} |
|||
|
|||
return totalWidths; |
|||
} |
|||
|
|||
public static float doLayout(float offset, string text, int start, int count, TextStyle style, |
|||
float[] advances, float[] positions, TabStops tabStops, out UnityEngine.Rect bounds) { |
|||
float advance = 0; |
|||
Font font = FontManager.instance.getOrCreate(style.fontFamily, style.fontWeight, style.fontStyle).font; |
|||
|
|||
char startingChar = text[start]; |
|||
bounds = new UnityEngine.Rect(); |
|||
if (char.IsHighSurrogate(startingChar) || EmojiUtils.isSingleCharEmoji(startingChar)) { |
|||
advance = _layoutEmoji(text, start, count, style, font, advances, positions, ref bounds); |
|||
} |
|||
else { |
|||
// According to the logic of Paragraph.layout, it is assured that all the characters are requested
|
|||
// in the texture before (in computing line breaks), so skip it here for optimization.
|
|||
// The only exception is the ellipsis, which did not appear in line breaking. It is taken care of
|
|||
// only when needed.
|
|||
|
|||
// font.RequestCharactersInTextureSafe(buff.text, style.UnityFontSize, style.UnityFontStyle);
|
|||
|
|||
// int wordstart = start == buff.size
|
|||
// ? start
|
|||
// : LayoutUtils.getPrevWordBreakForCache(buff, start + 1);
|
|||
int wordend; |
|||
for (int iter = start; iter < start + count; iter = wordend) { |
|||
wordend = LayoutUtils.getNextWordBreak(text, iter, start + count); |
|||
advance = _layoutWord(offset, iter - start, text, iter, |
|||
wordend - iter, style, font, advances, positions, advance, |
|||
tabStops, ref bounds); |
|||
} |
|||
} |
|||
|
|||
// bounds relative to first character
|
|||
bounds.x -= positions[0]; |
|||
return advance; |
|||
} |
|||
|
|||
static float _layoutWord(float offset, int layoutOffset, |
|||
string text, int start, int wordCount, TextStyle style, Font font, float[] advances, |
|||
float[] positions, float initAdvance, TabStops tabStops, ref UnityEngine.Rect bounds) { |
|||
float wordSpacing = |
|||
wordCount == 1 && LayoutUtils.isWordSpace(text[start]) ? style.wordSpacing : 0; |
|||
|
|||
float x = initAdvance; |
|||
float letterSpace = style.letterSpacing; |
|||
float letterSpaceHalfLeft = letterSpace * 0.5f; |
|||
float letterSpaceHalfRight = letterSpace - letterSpaceHalfLeft; |
|||
|
|||
for (int i = 0; i < wordCount; i++) { |
|||
initAdvance = x; |
|||
var ch = text[start + i]; |
|||
if (i == 0) { |
|||
x += letterSpaceHalfLeft + wordSpacing; |
|||
advances[i + layoutOffset] = letterSpaceHalfLeft + wordSpacing; |
|||
} |
|||
else { |
|||
advances[i - 1 + layoutOffset] += letterSpaceHalfRight; |
|||
advances[i + layoutOffset] = letterSpaceHalfLeft; |
|||
|
|||
x += letterSpace; |
|||
} |
|||
|
|||
if (font.getGlyphInfo(ch, out var glyphInfo, style.UnityFontSize, style.UnityFontStyle)) { |
|||
_updateBounds(glyphInfo, x, ref bounds); |
|||
} |
|||
|
|||
positions[i + layoutOffset] = x; |
|||
|
|||
float advance; |
|||
if (ch == '\t') { |
|||
advance = tabStops.nextTab(initAdvance + offset) - initAdvance - offset; |
|||
} |
|||
else { |
|||
advance = glyphInfo.advance; |
|||
} |
|||
|
|||
x += advance; |
|||
advances[i + layoutOffset] += advance; |
|||
|
|||
if (i + 1 == wordCount) { |
|||
advances[i + layoutOffset] += letterSpaceHalfRight; |
|||
x += letterSpaceHalfRight; |
|||
} |
|||
} |
|||
|
|||
return x; |
|||
} |
|||
|
|||
static float _layoutEmoji(string text, int start, int count, TextStyle style, Font font, float[] advances, |
|||
float[] positions, ref UnityEngine.Rect bounds) { |
|||
var metrics = FontMetrics.fromFont(font, style.UnityFontSize); |
|||
float x = 0; |
|||
for (int i = 0; i < count; i++) { |
|||
char c = text[start + i]; |
|||
if (EmojiUtils.isSingleCharNonEmptyEmoji(c) || char.IsHighSurrogate(c)) { |
|||
float letterSpace = style.letterSpacing; |
|||
float letterSpaceHalfLeft = letterSpace * 0.5f; |
|||
float letterSpaceHalfRight = letterSpace - letterSpaceHalfLeft; |
|||
|
|||
x += letterSpaceHalfLeft; |
|||
advances[i] = letterSpaceHalfLeft; |
|||
|
|||
float advance = style.fontSize * EmojiUtils.advanceFactor; |
|||
var minX = x; |
|||
var maxX = advance + x; |
|||
var minY = -style.fontSize * EmojiUtils.sizeFactor; |
|||
var maxY = metrics.descent; |
|||
_updateBounds(minX, maxX, minY, maxY, ref bounds); |
|||
|
|||
positions[i] = x; |
|||
|
|||
x += advance; |
|||
|
|||
advances[i] += advance; |
|||
advances[i] += letterSpaceHalfRight; |
|||
|
|||
x += letterSpaceHalfRight; |
|||
} |
|||
else { |
|||
advances[i] = 0; |
|||
positions[i] = x; |
|||
} |
|||
} |
|||
|
|||
return x; |
|||
} |
|||
|
|||
static void _updateBounds(CharacterInfo glyphInfo, float x, ref UnityEngine.Rect bounds) { |
|||
var minX = glyphInfo.minX + x; |
|||
var maxX = glyphInfo.maxX + x; |
|||
var minY = -glyphInfo.maxY; |
|||
var maxY = -glyphInfo.minY; |
|||
_updateBounds(minX, maxX, minY, maxY, ref bounds); |
|||
} |
|||
|
|||
static void _updateBounds(float minX, float maxX, float minY, float maxY, ref UnityEngine.Rect bounds) { |
|||
if (bounds.width <= 0 || bounds.height <= 0) { |
|||
bounds.Set(minX, minY, maxX - minX, maxY - minY); |
|||
} |
|||
else { |
|||
if (minX < bounds.x) { |
|||
bounds.x = minX; |
|||
} |
|||
|
|||
if (minY < bounds.y) { |
|||
bounds.y = minY; |
|||
} |
|||
|
|||
if (maxX > bounds.xMax) { |
|||
bounds.xMax = maxX; |
|||
} |
|||
|
|||
if (maxY > bounds.yMax) { |
|||
bounds.yMax = maxY; |
|||
} |
|||
} |
|||
} |
|||
} |
|||
} |
|
|||
using System.Collections.Generic; |
|||
using UnityEngine; |
|||
|
|||
namespace Unity.UIWidgets.ui { |
|||
class TabStops { |
|||
int _tabWidth = int.MaxValue; |
|||
|
|||
Font _font; |
|||
|
|||
int _fontSize; |
|||
|
|||
int _spaceAdvance; |
|||
|
|||
const int kTabSpaceCount = 4; |
|||
|
|||
public void setFont(Font font, int size) { |
|||
if (this._font != font || this._fontSize != size) { |
|||
this._tabWidth = int.MaxValue; |
|||
} |
|||
|
|||
this._font = font; |
|||
// Recompute the advance of space (' ') if font size changes
|
|||
if (this._fontSize != size) { |
|||
this._fontSize = size; |
|||
this._font.RequestCharactersInTextureSafe(" ", this._fontSize); |
|||
this._font.getGlyphInfo(' ', out var glyphInfo, this._fontSize, UnityEngine.FontStyle.Normal); |
|||
this._spaceAdvance = glyphInfo.advance; |
|||
} |
|||
} |
|||
|
|||
public float nextTab(float widthSoFar) { |
|||
if (this._tabWidth == int.MaxValue) { |
|||
if (this._fontSize > 0) { |
|||
this._tabWidth = this._spaceAdvance * kTabSpaceCount; |
|||
} |
|||
} |
|||
|
|||
if (this._tabWidth == 0) { |
|||
return widthSoFar; |
|||
} |
|||
|
|||
return (Mathf.Floor(widthSoFar / this._tabWidth + 1) * this._tabWidth); |
|||
} |
|||
} |
|||
|
|||
struct Candidate { |
|||
public int offset; |
|||
public int pre; |
|||
public float preBreak; |
|||
public float penalty; |
|||
|
|||
public float postBreak; |
|||
public int preSpaceCount; |
|||
public int postSpaceCount; |
|||
} |
|||
|
|||
class LineBreaker { |
|||
const float ScoreInfty = float.MaxValue; |
|||
const float ScoreDesperate = 1e10f; |
|||
|
|||
int _lineLimit = 0; |
|||
|
|||
// Limit number of lines, 0 means no limit
|
|||
public int lineLimit { |
|||
get { return this._lineLimit; } |
|||
set { this._lineLimit = value; } |
|||
} |
|||
|
|||
public static LineBreaker instance { |
|||
get { |
|||
if (_instance == null) { |
|||
_instance = new LineBreaker(); |
|||
} |
|||
|
|||
return _instance; |
|||
} |
|||
} |
|||
|
|||
static LineBreaker _instance; |
|||
|
|||
public static int[] newLinePositions(string text, out int count) { |
|||
count = 0; |
|||
for (var i = 0; i < text.Length; i++) { |
|||
if (text[i] == '\n') { |
|||
count++; |
|||
} |
|||
} |
|||
|
|||
count++; |
|||
|
|||
if (_newLinePositions == null || _newLinePositions.Length < count) { |
|||
_newLinePositions = new int[count]; |
|||
} |
|||
|
|||
count = 0; |
|||
for (var i = 0; i < text.Length; i++) { |
|||
if (text[i] == '\n') { |
|||
_newLinePositions[count++] = i; |
|||
} |
|||
} |
|||
|
|||
_newLinePositions[count++] = text.Length; |
|||
|
|||
return _newLinePositions; |
|||
} |
|||
|
|||
static int[] _newLinePositions; |
|||
|
|||
TextBuff _textBuf; |
|||
float[] _charWidths; |
|||
List<int> _breaks = new List<int>(); |
|||
int _breaksCount = 0; |
|||
List<float> _widths = new List<float>(); |
|||
int _widthsCount = 0; |
|||
WordBreaker _wordBreaker = new WordBreaker(); |
|||
float _width = 0.0f; |
|||
float _preBreak; |
|||
float _lineWidth; |
|||
int _lastBreak; |
|||
int _bestBreak; |
|||
float _bestScore; |
|||
int _spaceCount; |
|||
TabStops _tabStops; |
|||
int mFirstTabIndex; |
|||
List<Candidate> _candidates = new List<Candidate>(); |
|||
int _candidatesCount = 0; |
|||
|
|||
public int computeBreaks() { |
|||
int nCand = this._candidatesCount; |
|||
if (nCand > 0 && (nCand == 1 || this._lastBreak != nCand - 1)) { |
|||
var cand = this._candidates[this._candidatesCount - 1]; |
|||
this._pushBreak(cand.offset, (cand.postBreak - this._preBreak)); |
|||
} |
|||
|
|||
return this._breaksCount; |
|||
} |
|||
|
|||
public int getBreaksCount() { |
|||
return this._breaksCount; |
|||
} |
|||
|
|||
public int getBreak(int i) { |
|||
return this._breaks[i]; |
|||
} |
|||
|
|||
public float getWidth(int i) { |
|||
return this._widths[i]; |
|||
} |
|||
|
|||
public void resize(int size) { |
|||
if (this._charWidths == null || this._charWidths.Length < size) { |
|||
this._charWidths = new float[LayoutUtils.minPowerOfTwo(size)]; |
|||
} |
|||
} |
|||
|
|||
public void setText(string text, int textOffset, int textLength) { |
|||
this._textBuf = new TextBuff(text, textOffset, textLength); |
|||
this._wordBreaker.setText(this._textBuf); |
|||
this._wordBreaker.next(); |
|||
this._candidatesCount = 0; |
|||
Candidate can = new Candidate { |
|||
offset = 0, postBreak = 0, preBreak = 0, postSpaceCount = 0, preSpaceCount = 0, pre = 0 |
|||
}; |
|||
this._addCandidateToList(can); |
|||
this._lastBreak = 0; |
|||
this._bestBreak = 0; |
|||
this._bestScore = ScoreInfty; |
|||
this._preBreak = 0; |
|||
this.mFirstTabIndex = int.MaxValue; |
|||
this._spaceCount = 0; |
|||
} |
|||
|
|||
public void setLineWidth(float lineWidth) { |
|||
this._lineWidth = lineWidth; |
|||
} |
|||
|
|||
public float addStyleRun(TextStyle style, int start, int end) { |
|||
float width = 0; |
|||
if (style != null) { |
|||
// Layout.measureText(this._width - this._preBreak, this._textBuf,
|
|||
// start, end - start, style,
|
|||
// this._charWidths, start, this._tabStops);
|
|||
width = Layout.computeCharWidths(this._width - this._preBreak, this._textBuf.text, |
|||
this._textBuf.offset + start, end - start, style, |
|||
this._charWidths, start, this._tabStops); |
|||
} |
|||
|
|||
int current = this._wordBreaker.current(); |
|||
float postBreak = this._width; |
|||
int postSpaceCount = this._spaceCount; |
|||
|
|||
for (int i = start; i < end; i++) { |
|||
char c = this._textBuf.charAt(i); |
|||
if (c == '\t') { |
|||
this._width = this._preBreak + this._tabStops.nextTab(this._width - this._preBreak); |
|||
if (this.mFirstTabIndex == int.MaxValue) { |
|||
this.mFirstTabIndex = i; |
|||
} |
|||
} |
|||
else { |
|||
if (LayoutUtils.isWordSpace(c)) { |
|||
this._spaceCount += 1; |
|||
} |
|||
|
|||
this._width += this._charWidths[i]; |
|||
if (!LayoutUtils.isLineEndSpace(c)) { |
|||
postBreak = this._width; |
|||
postSpaceCount = this._spaceCount; |
|||
} |
|||
} |
|||
|
|||
if (i + 1 == current) { |
|||
if (style != null || current == end || this._charWidths[current] > 0) { |
|||
this._addWordBreak(current, this._width, postBreak, this._spaceCount, postSpaceCount, 0); |
|||
} |
|||
|
|||
current = this._wordBreaker.next(); |
|||
} |
|||
} |
|||
|
|||
return width; |
|||
} |
|||
|
|||
public void finish() { |
|||
this._wordBreaker.finish(); |
|||
this._width = 0; |
|||
this._candidatesCount = 0; |
|||
this._breaksCount = 0; |
|||
this._widthsCount = 0; |
|||
this._textBuf = default; |
|||
} |
|||
|
|||
public int getWidthsCount() { |
|||
return this._widthsCount; |
|||
} |
|||
|
|||
public void setTabStops(TabStops tabStops) { |
|||
this._tabStops = tabStops; |
|||
} |
|||
|
|||
void _addWordBreak(int offset, float preBreak, float postBreak, int preSpaceCount, int postSpaceCount, |
|||
float penalty) { |
|||
float width = this._candidates[this._candidatesCount - 1].preBreak; |
|||
if (postBreak - width > this._lineWidth) { |
|||
this._addCandidatesInsideWord(width, offset, postSpaceCount); |
|||
} |
|||
|
|||
this._addCandidate(new Candidate { |
|||
offset = offset, |
|||
preBreak = preBreak, |
|||
postBreak = postBreak, |
|||
preSpaceCount = preSpaceCount, |
|||
postSpaceCount = postSpaceCount, |
|||
penalty = penalty |
|||
}); |
|||
} |
|||
|
|||
void _addCandidatesInsideWord(float width, int offset, int postSpaceCount) { |
|||
int i = this._candidates[this._candidatesCount - 1].offset; |
|||
width += this._charWidths[i++]; |
|||
for (; i < offset; i++) { |
|||
float w = this._charWidths[i]; |
|||
if (w > 0) { |
|||
this._addCandidate(new Candidate { |
|||
offset = i, |
|||
preBreak = width, |
|||
postBreak = width, |
|||
preSpaceCount = postSpaceCount, |
|||
postSpaceCount = postSpaceCount, |
|||
penalty = ScoreDesperate, |
|||
}); |
|||
width += w; |
|||
} |
|||
} |
|||
} |
|||
|
|||
void _addCandidateToList(Candidate cand) { |
|||
if (this._candidates.Count == this._candidatesCount) { |
|||
this._candidates.Add(cand); |
|||
this._candidatesCount++; |
|||
} |
|||
else { |
|||
this._candidates[this._candidatesCount++] = cand; |
|||
} |
|||
} |
|||
|
|||
void _addCandidate(Candidate cand) { |
|||
int candIndex = this._candidatesCount; |
|||
this._addCandidateToList(cand); |
|||
if (cand.postBreak - this._preBreak > this._lineWidth) { |
|||
if (this._bestBreak == this._lastBreak) { |
|||
this._bestBreak = candIndex; |
|||
} |
|||
|
|||
this._pushGreedyBreak(); |
|||
} |
|||
|
|||
while (this._lastBreak != candIndex && cand.postBreak - this._preBreak > this._lineWidth) { |
|||
for (int i = this._lastBreak + 1; i < candIndex; i++) { |
|||
float penalty = this._candidates[i].penalty; |
|||
if (penalty <= this._bestScore) { |
|||
this._bestBreak = i; |
|||
this._bestScore = penalty; |
|||
} |
|||
} |
|||
|
|||
if (this._bestBreak == this._lastBreak) { |
|||
this._bestBreak = candIndex; |
|||
} |
|||
|
|||
this._pushGreedyBreak(); |
|||
} |
|||
|
|||
if (cand.penalty <= this._bestScore) { |
|||
this._bestBreak = candIndex; |
|||
this._bestScore = cand.penalty; |
|||
} |
|||
} |
|||
|
|||
void _pushGreedyBreak() { |
|||
var bestCandidate = this._candidates[this._bestBreak]; |
|||
this._pushBreak(bestCandidate.offset, bestCandidate.postBreak - this._preBreak); |
|||
this._bestScore = ScoreInfty; |
|||
this._lastBreak = this._bestBreak; |
|||
this._preBreak = bestCandidate.preBreak; |
|||
} |
|||
|
|||
void _pushBreak(int offset, float width) { |
|||
if (this.lineLimit == 0 || this._breaksCount < this.lineLimit) { |
|||
if (this._breaks.Count == this._breaksCount) { |
|||
this._breaks.Add(offset); |
|||
this._breaksCount++; |
|||
} |
|||
else { |
|||
this._breaks[this._breaksCount++] = offset; |
|||
} |
|||
|
|||
if (this._widths.Count == this._widthsCount) { |
|||
this._widths.Add(width); |
|||
this._widthsCount++; |
|||
} |
|||
else { |
|||
this._widths[this._widthsCount++] = width; |
|||
} |
|||
} |
|||
} |
|||
} |
|||
using System.Collections.Generic; |
|||
using UnityEngine; |
|||
|
|||
namespace Unity.UIWidgets.ui { |
|||
class TabStops { |
|||
int _tabWidth = int.MaxValue; |
|||
|
|||
Font _font; |
|||
|
|||
int _fontSize; |
|||
|
|||
int _spaceAdvance; |
|||
|
|||
const int kTabSpaceCount = 4; |
|||
|
|||
public void setFont(Font font, int size) { |
|||
if (this._font != font || this._fontSize != size) { |
|||
this._tabWidth = int.MaxValue; |
|||
} |
|||
|
|||
this._font = font; |
|||
// Recompute the advance of space (' ') if font size changes
|
|||
if (this._fontSize != size) { |
|||
this._fontSize = size; |
|||
this._font.RequestCharactersInTextureSafe(" ", this._fontSize); |
|||
this._font.getGlyphInfo(' ', out var glyphInfo, this._fontSize, UnityEngine.FontStyle.Normal); |
|||
this._spaceAdvance = glyphInfo.advance; |
|||
} |
|||
} |
|||
|
|||
public float nextTab(float widthSoFar) { |
|||
if (this._tabWidth == int.MaxValue) { |
|||
if (this._fontSize > 0) { |
|||
this._tabWidth = this._spaceAdvance * kTabSpaceCount; |
|||
} |
|||
} |
|||
|
|||
if (this._tabWidth == 0) { |
|||
return widthSoFar; |
|||
} |
|||
|
|||
return (Mathf.Floor(widthSoFar / this._tabWidth + 1) * this._tabWidth); |
|||
} |
|||
} |
|||
|
|||
struct Candidate { |
|||
public int offset; |
|||
public int pre; |
|||
public float preBreak; |
|||
public float penalty; |
|||
|
|||
public float postBreak; |
|||
public int preSpaceCount; |
|||
public int postSpaceCount; |
|||
} |
|||
|
|||
class LineBreaker { |
|||
const float ScoreInfty = float.MaxValue; |
|||
const float ScoreDesperate = 1e10f; |
|||
|
|||
int _lineLimit = 0; |
|||
|
|||
// Limit number of lines, 0 means no limit
|
|||
public int lineLimit { |
|||
get { return this._lineLimit; } |
|||
set { this._lineLimit = value; } |
|||
} |
|||
|
|||
public static LineBreaker instance { |
|||
get { |
|||
if (_instance == null) { |
|||
_instance = new LineBreaker(); |
|||
} |
|||
|
|||
return _instance; |
|||
} |
|||
} |
|||
|
|||
static LineBreaker _instance; |
|||
|
|||
public static int[] newLinePositions(string text, out int count) { |
|||
count = 0; |
|||
for (var i = 0; i < text.Length; i++) { |
|||
if (text[i] == '\n') { |
|||
count++; |
|||
} |
|||
} |
|||
|
|||
count++; |
|||
|
|||
if (_newLinePositions == null || _newLinePositions.Length < count) { |
|||
_newLinePositions = new int[count]; |
|||
} |
|||
|
|||
count = 0; |
|||
for (var i = 0; i < text.Length; i++) { |
|||
if (text[i] == '\n') { |
|||
_newLinePositions[count++] = i; |
|||
} |
|||
} |
|||
|
|||
_newLinePositions[count++] = text.Length; |
|||
|
|||
return _newLinePositions; |
|||
} |
|||
|
|||
static int[] _newLinePositions; |
|||
|
|||
TextBuff _textBuf; |
|||
float[] _charWidths; |
|||
List<int> _breaks = new List<int>(); |
|||
int _breaksCount = 0; |
|||
List<float> _widths = new List<float>(); |
|||
int _widthsCount = 0; |
|||
WordBreaker _wordBreaker = new WordBreaker(); |
|||
float _width = 0.0f; |
|||
float _preBreak; |
|||
float _lineWidth; |
|||
int _lastBreak; |
|||
int _bestBreak; |
|||
float _bestScore; |
|||
int _spaceCount; |
|||
TabStops _tabStops; |
|||
int mFirstTabIndex; |
|||
List<Candidate> _candidates = new List<Candidate>(); |
|||
int _candidatesCount = 0; |
|||
|
|||
public int computeBreaks() { |
|||
int nCand = this._candidatesCount; |
|||
if (nCand > 0 && (nCand == 1 || this._lastBreak != nCand - 1)) { |
|||
var cand = this._candidates[this._candidatesCount - 1]; |
|||
this._pushBreak(cand.offset, (cand.postBreak - this._preBreak)); |
|||
} |
|||
|
|||
return this._breaksCount; |
|||
} |
|||
|
|||
public int getBreaksCount() { |
|||
return this._breaksCount; |
|||
} |
|||
|
|||
public int getBreak(int i) { |
|||
return this._breaks[i]; |
|||
} |
|||
|
|||
public float getWidth(int i) { |
|||
return this._widths[i]; |
|||
} |
|||
|
|||
public void resize(int size) { |
|||
if (this._charWidths == null || this._charWidths.Length < size) { |
|||
this._charWidths = new float[LayoutUtils.minPowerOfTwo(size)]; |
|||
} |
|||
} |
|||
|
|||
public void setText(string text, int textOffset, int textLength) { |
|||
this._textBuf = new TextBuff(text, textOffset, textLength); |
|||
this._wordBreaker.setText(this._textBuf); |
|||
this._wordBreaker.next(); |
|||
this._candidatesCount = 0; |
|||
Candidate can = new Candidate { |
|||
offset = 0, postBreak = 0, preBreak = 0, postSpaceCount = 0, preSpaceCount = 0, pre = 0 |
|||
}; |
|||
this._addCandidateToList(can); |
|||
this._lastBreak = 0; |
|||
this._bestBreak = 0; |
|||
this._bestScore = ScoreInfty; |
|||
this._preBreak = 0; |
|||
this.mFirstTabIndex = int.MaxValue; |
|||
this._spaceCount = 0; |
|||
} |
|||
|
|||
public void setLineWidth(float lineWidth) { |
|||
this._lineWidth = lineWidth; |
|||
} |
|||
|
|||
public float addStyleRun(TextStyle style, int start, int end) { |
|||
float width = 0; |
|||
if (style != null) { |
|||
// Layout.measureText(this._width - this._preBreak, this._textBuf,
|
|||
// start, end - start, style,
|
|||
// this._charWidths, start, this._tabStops);
|
|||
width = Layout.computeCharWidths(this._width - this._preBreak, this._textBuf.text, |
|||
this._textBuf.offset + start, end - start, style, |
|||
this._charWidths, start, this._tabStops); |
|||
} |
|||
|
|||
int current = this._wordBreaker.current(); |
|||
float postBreak = this._width; |
|||
int postSpaceCount = this._spaceCount; |
|||
|
|||
for (int i = start; i < end; i++) { |
|||
char c = this._textBuf.charAt(i); |
|||
if (c == '\t') { |
|||
this._width = this._preBreak + this._tabStops.nextTab(this._width - this._preBreak); |
|||
if (this.mFirstTabIndex == int.MaxValue) { |
|||
this.mFirstTabIndex = i; |
|||
} |
|||
} |
|||
else { |
|||
if (LayoutUtils.isWordSpace(c)) { |
|||
this._spaceCount += 1; |
|||
} |
|||
|
|||
this._width += this._charWidths[i]; |
|||
if (!LayoutUtils.isLineEndSpace(c)) { |
|||
postBreak = this._width; |
|||
postSpaceCount = this._spaceCount; |
|||
} |
|||
} |
|||
|
|||
if (i + 1 == current) { |
|||
if (style != null || current == end || this._charWidths[current] > 0) { |
|||
this._addWordBreak(current, this._width, postBreak, this._spaceCount, postSpaceCount, 0); |
|||
} |
|||
|
|||
current = this._wordBreaker.next(); |
|||
} |
|||
} |
|||
|
|||
return width; |
|||
} |
|||
|
|||
public void finish() { |
|||
this._wordBreaker.finish(); |
|||
this._width = 0; |
|||
this._candidatesCount = 0; |
|||
this._breaksCount = 0; |
|||
this._widthsCount = 0; |
|||
this._textBuf = default; |
|||
} |
|||
|
|||
public int getWidthsCount() { |
|||
return this._widthsCount; |
|||
} |
|||
|
|||
public void setTabStops(TabStops tabStops) { |
|||
this._tabStops = tabStops; |
|||
} |
|||
|
|||
void _addWordBreak(int offset, float preBreak, float postBreak, int preSpaceCount, int postSpaceCount, |
|||
float penalty) { |
|||
float width = this._candidates[this._candidatesCount - 1].preBreak; |
|||
if (postBreak - width > this._lineWidth) { |
|||
this._addCandidatesInsideWord(width, offset, postSpaceCount); |
|||
} |
|||
|
|||
this._addCandidate(new Candidate { |
|||
offset = offset, |
|||
preBreak = preBreak, |
|||
postBreak = postBreak, |
|||
preSpaceCount = preSpaceCount, |
|||
postSpaceCount = postSpaceCount, |
|||
penalty = penalty |
|||
}); |
|||
} |
|||
|
|||
void _addCandidatesInsideWord(float width, int offset, int postSpaceCount) { |
|||
int i = this._candidates[this._candidatesCount - 1].offset; |
|||
width += this._charWidths[i++]; |
|||
for (; i < offset; i++) { |
|||
float w = this._charWidths[i]; |
|||
if (w > 0) { |
|||
this._addCandidate(new Candidate { |
|||
offset = i, |
|||
preBreak = width, |
|||
postBreak = width, |
|||
preSpaceCount = postSpaceCount, |
|||
postSpaceCount = postSpaceCount, |
|||
penalty = ScoreDesperate, |
|||
}); |
|||
width += w; |
|||
} |
|||
} |
|||
} |
|||
|
|||
void _addCandidateToList(Candidate cand) { |
|||
if (this._candidates.Count == this._candidatesCount) { |
|||
this._candidates.Add(cand); |
|||
this._candidatesCount++; |
|||
} |
|||
else { |
|||
this._candidates[this._candidatesCount++] = cand; |
|||
} |
|||
} |
|||
|
|||
void _addCandidate(Candidate cand) { |
|||
int candIndex = this._candidatesCount; |
|||
this._addCandidateToList(cand); |
|||
if (cand.postBreak - this._preBreak > this._lineWidth) { |
|||
if (this._bestBreak == this._lastBreak) { |
|||
this._bestBreak = candIndex; |
|||
} |
|||
|
|||
this._pushGreedyBreak(); |
|||
} |
|||
|
|||
while (this._lastBreak != candIndex && cand.postBreak - this._preBreak > this._lineWidth) { |
|||
for (int i = this._lastBreak + 1; i < candIndex; i++) { |
|||
float penalty = this._candidates[i].penalty; |
|||
if (penalty <= this._bestScore) { |
|||
this._bestBreak = i; |
|||
this._bestScore = penalty; |
|||
} |
|||
} |
|||
|
|||
if (this._bestBreak == this._lastBreak) { |
|||
this._bestBreak = candIndex; |
|||
} |
|||
|
|||
this._pushGreedyBreak(); |
|||
} |
|||
|
|||
if (cand.penalty <= this._bestScore) { |
|||
this._bestBreak = candIndex; |
|||
this._bestScore = cand.penalty; |
|||
} |
|||
} |
|||
|
|||
void _pushGreedyBreak() { |
|||
var bestCandidate = this._candidates[this._bestBreak]; |
|||
this._pushBreak(bestCandidate.offset, bestCandidate.postBreak - this._preBreak); |
|||
this._bestScore = ScoreInfty; |
|||
this._lastBreak = this._bestBreak; |
|||
this._preBreak = bestCandidate.preBreak; |
|||
} |
|||
|
|||
void _pushBreak(int offset, float width) { |
|||
if (this.lineLimit == 0 || this._breaksCount < this.lineLimit) { |
|||
if (this._breaks.Count == this._breaksCount) { |
|||
this._breaks.Add(offset); |
|||
this._breaksCount++; |
|||
} |
|||
else { |
|||
this._breaks[this._breaksCount++] = offset; |
|||
} |
|||
|
|||
if (this._widths.Count == this._widthsCount) { |
|||
this._widths.Add(width); |
|||
this._widthsCount++; |
|||
} |
|||
else { |
|||
this._widths[this._widthsCount++] = width; |
|||
} |
|||
} |
|||
} |
|||
} |
|||
} |
|
|||
fileFormatVersion: 2 |
|||
guid: bfd6fdb0d13948d6acbc528589ebef33 |
|||
fileFormatVersion: 2 |
|||
guid: bfd6fdb0d13948d6acbc528589ebef33 |
|||
timeCreated: 1534821698 |
|
|||
namespace Unity.UIWidgets.ui { |
|||
public struct TextBlob { |
|||
internal TextBlob(string text, int textOffset, int textSize, float[] positionXs, |
|||
UnityEngine.Rect bounds, TextStyle style) { |
|||
this.instanceId = ++_nextInstanceId; |
|||
this._positionXs = positionXs; |
|||
this.text = text; |
|||
this.textOffset = textOffset; |
|||
this.textSize = textSize; |
|||
this.style = style; |
|||
this._bounds = bounds; |
|||
this._boundsInText = null; |
|||
} |
|||
|
|||
public Rect boundsInText { |
|||
get { |
|||
if (this._boundsInText == null) { |
|||
var pos = this.getPositionX(0); |
|||
this._boundsInText = Rect.fromLTWH(this._bounds.xMin + pos, this._bounds.yMin, |
|||
this._bounds.width, this._bounds.height); |
|||
} |
|||
|
|||
return this._boundsInText; |
|||
} |
|||
} |
|||
|
|||
public Rect shiftedBoundsInText(float dx, float dy) { |
|||
var pos = this.getPositionX(0); |
|||
return Rect.fromLTWH(this._bounds.xMin + pos + dx, this._bounds.yMin + dy, |
|||
this._bounds.width, this._bounds.height); |
|||
} |
|||
|
|||
public float getPositionX(int i) { |
|||
return this._positionXs[this.textOffset + i]; |
|||
} |
|||
|
|||
static long _nextInstanceId; |
|||
internal readonly long instanceId; |
|||
internal readonly string text; |
|||
internal readonly int textOffset; |
|||
internal readonly int textSize; |
|||
internal readonly TextStyle style; |
|||
readonly UnityEngine.Rect _bounds; // bounds with positions[start] as origin
|
|||
readonly float[] _positionXs; |
|||
|
|||
Rect _boundsInText; |
|||
} |
|||
|
|||
public struct TextBlobBuilder { |
|||
TextStyle _style; |
|||
float[] _positionXs; |
|||
string _text; |
|||
int _textOffset; |
|||
int _size; |
|||
UnityEngine.Rect _bounds; |
|||
|
|||
public void allocRunPos(painting.TextStyle style, string text, int offset, int size, |
|||
float textScaleFactor = 1.0f) { |
|||
this.allocRunPos(TextStyle.applyStyle(null, style, textScaleFactor), text, offset, size); |
|||
} |
|||
|
|||
internal void allocRunPos(TextStyle style, string text, int offset, int size) { |
|||
this._style = style; |
|||
this._text = text; |
|||
this._textOffset = offset; |
|||
this._size = size; |
|||
// Allocate a single buffer for all text blobs that share this text, to save memory and GC.
|
|||
// It is assumed that all of `text` is being used. This may cause great waste if a long text is passed
|
|||
// but only a small part of it is to be rendered, which is not the case for now.
|
|||
this.allocPos(text.Length); |
|||
} |
|||
|
|||
internal void allocPos(int size) { |
|||
if (this._positionXs == null || this._positionXs.Length < size) { |
|||
this._positionXs = new float[size]; |
|||
} |
|||
} |
|||
|
|||
public void setPositionX(int i, float positionX) { |
|||
this._positionXs[this._textOffset + i] = positionX; |
|||
} |
|||
|
|||
public void setPositionXs(float[] positionXs) { |
|||
this._positionXs = positionXs; |
|||
} |
|||
|
|||
public void setBounds(UnityEngine.Rect bounds) { |
|||
this._bounds = bounds; |
|||
} |
|||
|
|||
public TextBlob make() { |
|||
var result = new TextBlob(this._text, this._textOffset, |
|||
this._size, this._positionXs, this._bounds, this._style); |
|||
return result; |
|||
} |
|||
} |
|||
namespace Unity.UIWidgets.ui { |
|||
public struct TextBlob { |
|||
internal TextBlob(string text, int textOffset, int textSize, float[] positionXs, |
|||
UnityEngine.Rect bounds, TextStyle style) { |
|||
this.instanceId = ++_nextInstanceId; |
|||
this._positionXs = positionXs; |
|||
this.text = text; |
|||
this.textOffset = textOffset; |
|||
this.textSize = textSize; |
|||
this.style = style; |
|||
this._bounds = bounds; |
|||
this._boundsInText = null; |
|||
} |
|||
|
|||
public Rect boundsInText { |
|||
get { |
|||
if (this._boundsInText == null) { |
|||
var pos = this.getPositionX(0); |
|||
this._boundsInText = Rect.fromLTWH(this._bounds.xMin + pos, this._bounds.yMin, |
|||
this._bounds.width, this._bounds.height); |
|||
} |
|||
|
|||
return this._boundsInText; |
|||
} |
|||
} |
|||
|
|||
public Rect shiftedBoundsInText(float dx, float dy) { |
|||
var pos = this.getPositionX(0); |
|||
return Rect.fromLTWH(this._bounds.xMin + pos + dx, this._bounds.yMin + dy, |
|||
this._bounds.width, this._bounds.height); |
|||
} |
|||
|
|||
public float getPositionX(int i) { |
|||
return this._positionXs[this.textOffset + i]; |
|||
} |
|||
|
|||
static long _nextInstanceId; |
|||
internal readonly long instanceId; |
|||
internal readonly string text; |
|||
internal readonly int textOffset; |
|||
internal readonly int textSize; |
|||
internal readonly TextStyle style; |
|||
readonly UnityEngine.Rect _bounds; // bounds with positions[start] as origin
|
|||
readonly float[] _positionXs; |
|||
|
|||
Rect _boundsInText; |
|||
} |
|||
|
|||
public struct TextBlobBuilder { |
|||
TextStyle _style; |
|||
float[] _positionXs; |
|||
string _text; |
|||
int _textOffset; |
|||
int _size; |
|||
UnityEngine.Rect _bounds; |
|||
|
|||
public void allocRunPos(painting.TextStyle style, string text, int offset, int size, |
|||
float textScaleFactor = 1.0f) { |
|||
this.allocRunPos(TextStyle.applyStyle(null, style, textScaleFactor), text, offset, size); |
|||
} |
|||
|
|||
internal void allocRunPos(TextStyle style, string text, int offset, int size) { |
|||
this._style = style; |
|||
this._text = text; |
|||
this._textOffset = offset; |
|||
this._size = size; |
|||
// Allocate a single buffer for all text blobs that share this text, to save memory and GC.
|
|||
// It is assumed that all of `text` is being used. This may cause great waste if a long text is passed
|
|||
// but only a small part of it is to be rendered, which is not the case for now.
|
|||
this.allocPos(text.Length); |
|||
} |
|||
|
|||
internal void allocPos(int size) { |
|||
if (this._positionXs == null || this._positionXs.Length < size) { |
|||
this._positionXs = new float[size]; |
|||
} |
|||
} |
|||
|
|||
public void setPositionX(int i, float positionX) { |
|||
this._positionXs[this._textOffset + i] = positionX; |
|||
} |
|||
|
|||
public void setPositionXs(float[] positionXs) { |
|||
this._positionXs = positionXs; |
|||
} |
|||
|
|||
public void setBounds(UnityEngine.Rect bounds) { |
|||
this._bounds = bounds; |
|||
} |
|||
|
|||
public TextBlob make() { |
|||
var result = new TextBlob(this._text, this._textOffset, |
|||
this._size, this._positionXs, this._bounds, this._style); |
|||
return result; |
|||
} |
|||
} |
|||
} |
|
|||
namespace Unity.UIWidgets.rendering { |
|||
public static class RenderingDebugUtils { |
|||
public static bool debugCheckElevationsEnabled = false; |
|||
} |
|||
namespace Unity.UIWidgets.rendering { |
|||
public static class RenderingDebugUtils { |
|||
public static bool debugCheckElevationsEnabled = false; |
|||
} |
|||
} |
|
|||
fileFormatVersion: 2 |
|||
guid: 64dfb38ea47d49c7b9f3d0debeee4409 |
|||
fileFormatVersion: 2 |
|||
guid: 64dfb38ea47d49c7b9f3d0debeee4409 |
|||
timeCreated: 1536819405 |
|
|||
fileFormatVersion: 2 |
|||
guid: d0413272d07e4c958f45f9c7597fe2b7 |
|||
fileFormatVersion: 2 |
|||
guid: d0413272d07e4c958f45f9c7597fe2b7 |
|||
timeCreated: 1534820838 |
|
|||
fileFormatVersion: 2 |
|||
guid: 057a0f3b94794fce9f9f3aa7bb624256 |
|||
fileFormatVersion: 2 |
|||
guid: 057a0f3b94794fce9f9f3aa7bb624256 |
|||
timeCreated: 1557210363 |
|
|||
fileFormatVersion: 2 |
|||
guid: 8bf4842a43ca4b13ae8d333813e166c9 |
|||
fileFormatVersion: 2 |
|||
guid: 8bf4842a43ca4b13ae8d333813e166c9 |
|||
timeCreated: 1553064435 |
|
|||
fileFormatVersion: 2 |
|||
guid: bf63171d5e9d4f9fb93cf8cf7a0534bb |
|||
fileFormatVersion: 2 |
|||
guid: bf63171d5e9d4f9fb93cf8cf7a0534bb |
|||
timeCreated: 1552011675 |
|
|||
fileFormatVersion: 2 |
|||
guid: bb9a3c5b387541cb800b11c84121f7f3 |
|||
fileFormatVersion: 2 |
|||
guid: bb9a3c5b387541cb800b11c84121f7f3 |
|||
timeCreated: 1534828260 |
|
|||
fileFormatVersion: 2 |
|||
guid: ffdd174fa01b47d99dae6b8b38945413 |
|||
fileFormatVersion: 2 |
|||
guid: ffdd174fa01b47d99dae6b8b38945413 |
|||
timeCreated: 1534828022 |
|
|||
fileFormatVersion: 2 |
|||
guid: ef24fa8abd794ec9ad904f149ade425b |
|||
fileFormatVersion: 2 |
|||
guid: ef24fa8abd794ec9ad904f149ade425b |
|||
timeCreated: 1534828173 |
|
|||
fileFormatVersion: 2 |
|||
guid: 07f0b3c08d53427992f828dd9c4f0f15 |
|||
fileFormatVersion: 2 |
|||
guid: 07f0b3c08d53427992f828dd9c4f0f15 |
|||
timeCreated: 1534828078 |
|
|||
fileFormatVersion: 2 |
|||
guid: d922956f02bc4ff2a82f9404787380b0 |
|||
fileFormatVersion: 2 |
|||
guid: d922956f02bc4ff2a82f9404787380b0 |
|||
timeCreated: 1534828191 |
|
|||
fileFormatVersion: 2 |
|||
guid: e47ddf579a9a45f48fd9203faf93b52f |
|||
fileFormatVersion: 2 |
|||
guid: e47ddf579a9a45f48fd9203faf93b52f |
|||
timeCreated: 1534828095 |
|
|||
fileFormatVersion: 2 |
|||
guid: fad6eb2d15b84c7cb92f115245ac09f8 |
|||
fileFormatVersion: 2 |
|||
guid: fad6eb2d15b84c7cb92f115245ac09f8 |
|||
timeCreated: 1534828106 |
|
|||
fileFormatVersion: 2 |
|||
guid: 2e1ff13e974447a080559101999e17d9 |
|||
fileFormatVersion: 2 |
|||
guid: 2e1ff13e974447a080559101999e17d9 |
|||
timeCreated: 1534828127 |
|
|||
fileFormatVersion: 2 |
|||
guid: 4d75ec15a75b4a7baca1968978b1eec9 |
|||
fileFormatVersion: 2 |
|||
guid: 4d75ec15a75b4a7baca1968978b1eec9 |
|||
timeCreated: 1534820694 |
|
|||
fileFormatVersion: 2 |
|||
guid: 80732e4248ce48ec92e1463ddc59451d |
|||
fileFormatVersion: 2 |
|||
guid: 80732e4248ce48ec92e1463ddc59451d |
|||
timeCreated: 1534833891 |
|
|||
fileFormatVersion: 2 |
|||
guid: 858587f8fb5d435e8a07a0ff46e5bc95 |
|||
fileFormatVersion: 2 |
|||
guid: 858587f8fb5d435e8a07a0ff46e5bc95 |
|||
timeCreated: 1534820746 |
|
|||
fileFormatVersion: 2 |
|||
guid: 462d84cff25d484895d1a81e7ebd2a24 |
|||
fileFormatVersion: 2 |
|||
guid: 462d84cff25d484895d1a81e7ebd2a24 |
|||
timeCreated: 1534820764 |
|
|||
fileFormatVersion: 2 |
|||
guid: c89c524fa7c242aa91900b63841be8b3 |
|||
fileFormatVersion: 2 |
|||
guid: c89c524fa7c242aa91900b63841be8b3 |
|||
timeCreated: 1553153571 |
|
|||
using System.Collections.Generic; |
|||
using System.Linq; |
|||
using Unity.UIWidgets.foundation; |
|||
using Unity.UIWidgets.ui; |
|||
|
|||
namespace Unity.UIWidgets.painting { |
|||
public class StrutStyle : Diagnosticable { |
|||
public StrutStyle( |
|||
string fontFamily = null, |
|||
List<string> fontFamilyFallback = null, |
|||
float? fontSize = null, |
|||
float? height = null, |
|||
float? leading = null, |
|||
FontWeight fontWeight = null, |
|||
FontStyle? fontStyle = null, |
|||
bool forceStrutHeight = false, |
|||
string debugLabel = null |
|||
) { |
|||
D.assert(fontSize == null || fontSize > 0); |
|||
D.assert(leading == null || leading >= 0); |
|||
this.fontFamily = fontFamily; |
|||
this._fontFamilyFallback = fontFamilyFallback; |
|||
this.fontSize = fontSize; |
|||
this.height = height; |
|||
this.fontWeight = fontWeight; |
|||
this.fontStyle = fontStyle; |
|||
this.leading = leading; |
|||
this.forceStrutHeight = forceStrutHeight; |
|||
this.debugLabel = debugLabel; |
|||
} |
|||
|
|||
public static StrutStyle fromTextStyle( |
|||
TextStyle textStyle, |
|||
string fontFamily = null, |
|||
List<string> fontFamilyFallback = null, |
|||
float? fontSize = null, |
|||
float? height = null, |
|||
float? leading = null, |
|||
FontWeight fontWeight = null, |
|||
FontStyle? fontStyle = null, |
|||
bool forceStrutHeight = false, |
|||
string debugLabel = null |
|||
) { |
|||
D.assert(textStyle != null); |
|||
D.assert(fontSize == null || fontSize > 0); |
|||
D.assert(leading == null || leading >= 0); |
|||
return new StrutStyle( |
|||
fontFamily: fontFamily ?? textStyle.fontFamily, |
|||
fontFamilyFallback: fontFamilyFallback ?? textStyle.fontFamilyFallback, |
|||
height: height ?? textStyle.height, |
|||
fontSize: fontSize ?? textStyle.fontSize, |
|||
fontWeight: fontWeight ?? textStyle.fontWeight, |
|||
fontStyle: fontStyle ?? textStyle.fontStyle, |
|||
debugLabel: debugLabel ?? textStyle.debugLabel |
|||
); |
|||
} |
|||
|
|||
public static readonly StrutStyle disabled = new StrutStyle( |
|||
height: 0.0f, |
|||
leading: 0.0f |
|||
); |
|||
|
|||
public readonly string fontFamily; |
|||
|
|||
public List<string> fontFamilyFallback { |
|||
get { return this._fontFamilyFallback; } |
|||
} |
|||
|
|||
readonly List<string> _fontFamilyFallback; |
|||
|
|||
public readonly float? fontSize; |
|||
public readonly float? height; |
|||
public readonly FontWeight fontWeight; |
|||
public readonly FontStyle? fontStyle; |
|||
public readonly float? leading; |
|||
public readonly bool forceStrutHeight; |
|||
public readonly string debugLabel; |
|||
|
|||
public RenderComparison compareTo(StrutStyle other) { |
|||
if (ReferenceEquals(this, other)) { |
|||
return RenderComparison.identical; |
|||
} |
|||
|
|||
if (other == null) { |
|||
return RenderComparison.layout; |
|||
} |
|||
|
|||
if (this.fontFamily != other.fontFamily || |
|||
this.fontSize != other.fontSize || |
|||
this.fontWeight != other.fontWeight || |
|||
this.fontStyle != other.fontStyle || |
|||
this.height != other.height || |
|||
this.leading != other.leading || |
|||
this.forceStrutHeight != other.forceStrutHeight || |
|||
!CollectionUtils.equalsList(this.fontFamilyFallback, other.fontFamilyFallback)) { |
|||
return RenderComparison.layout; |
|||
} |
|||
|
|||
return RenderComparison.identical; |
|||
} |
|||
|
|||
public StrutStyle inheritFromTextStyle(TextStyle other) { |
|||
if (other == null) { |
|||
return this; |
|||
} |
|||
|
|||
return new StrutStyle( |
|||
fontFamily: this.fontFamily ?? other.fontFamily, |
|||
fontFamilyFallback: this.fontFamilyFallback ?? other.fontFamilyFallback, |
|||
height: this.height ?? other.height, |
|||
leading: this.leading, |
|||
fontSize: this.fontSize ?? other.fontSize, |
|||
fontWeight: this.fontWeight ?? other.fontWeight, |
|||
fontStyle: this.fontStyle ?? other.fontStyle, |
|||
forceStrutHeight: this.forceStrutHeight, |
|||
debugLabel: this.debugLabel ?? other.debugLabel |
|||
); |
|||
} |
|||
|
|||
public bool Equals(StrutStyle other) { |
|||
if (ReferenceEquals(null, other)) { |
|||
return false; |
|||
} |
|||
|
|||
if (ReferenceEquals(this, other)) { |
|||
return true; |
|||
} |
|||
|
|||
return this.fontFamily == other.fontFamily && |
|||
this.fontSize == other.fontSize && |
|||
this.fontWeight == other.fontWeight && |
|||
this.fontStyle == other.fontStyle && |
|||
this.height == other.height && |
|||
this.leading == other.leading && |
|||
this.forceStrutHeight == other.forceStrutHeight; |
|||
} |
|||
|
|||
public override bool Equals(object obj) { |
|||
if (ReferenceEquals(null, obj)) { |
|||
return false; |
|||
} |
|||
|
|||
if (ReferenceEquals(this, obj)) { |
|||
return true; |
|||
} |
|||
|
|||
if (obj.GetType() != this.GetType()) { |
|||
return false; |
|||
} |
|||
|
|||
return this.Equals((StrutStyle) obj); |
|||
} |
|||
|
|||
public static bool operator ==(StrutStyle left, StrutStyle right) { |
|||
return Equals(left, right); |
|||
} |
|||
|
|||
public static bool operator !=(StrutStyle left, StrutStyle right) { |
|||
return !Equals(left, right); |
|||
} |
|||
|
|||
public override int GetHashCode() { |
|||
unchecked { |
|||
var hashCode = this.fontFamily?.GetHashCode() ?? 0; |
|||
hashCode = (hashCode * 397) ^ (this.fontSize?.GetHashCode() ?? 0); |
|||
hashCode = (hashCode * 397) ^ (this.fontWeight?.GetHashCode() ?? 0); |
|||
hashCode = (hashCode * 397) ^ (this.fontStyle?.GetHashCode() ?? 0); |
|||
hashCode = (hashCode * 397) ^ (this.height?.GetHashCode() ?? 0); |
|||
hashCode = (hashCode * 397) ^ (this.leading?.GetHashCode() ?? 0); |
|||
hashCode = (hashCode * 397) ^ this.forceStrutHeight.GetHashCode(); |
|||
return hashCode; |
|||
} |
|||
} |
|||
|
|||
public override string toStringShort() { |
|||
return $"{this.GetType()}"; |
|||
} |
|||
|
|||
public override void debugFillProperties(DiagnosticPropertiesBuilder properties) { |
|||
base.debugFillProperties(properties); |
|||
if (this.debugLabel != null) { |
|||
properties.add(new MessageProperty("debugLabel", this.debugLabel)); |
|||
} |
|||
|
|||
List<DiagnosticsNode> styles = new List<DiagnosticsNode>(); |
|||
styles.Add(new StringProperty("family", this.fontFamily, defaultValue: Diagnostics.kNullDefaultValue, |
|||
quoted: false)); |
|||
styles.Add(new EnumerableProperty<string>("familyFallback", this.fontFamilyFallback)); |
|||
styles.Add(new DiagnosticsProperty<float?>("size", this.fontSize, |
|||
defaultValue: Diagnostics.kNullDefaultValue)); |
|||
string weightDescription = ""; |
|||
if (this.fontWeight != null) { |
|||
weightDescription = this.fontWeight.weightValue.ToString(); |
|||
} |
|||
|
|||
styles.Add(new DiagnosticsProperty<FontWeight>( |
|||
"weight", this.fontWeight, |
|||
description: weightDescription, |
|||
defaultValue: Diagnostics.kNullDefaultValue |
|||
)); |
|||
styles.Add(new EnumProperty<FontStyle?>("style", this.fontStyle, |
|||
defaultValue: Diagnostics.kNullDefaultValue)); |
|||
styles.Add(new DiagnosticsProperty<float?>("height", this.height, |
|||
defaultValue: Diagnostics.kNullDefaultValue)); |
|||
styles.Add(new FlagProperty("forceStrutHeight", value: this.forceStrutHeight, |
|||
defaultValue: Diagnostics.kNullDefaultValue)); |
|||
|
|||
bool styleSpecified = styles.Any((DiagnosticsNode n) => !n.isFiltered(DiagnosticLevel.info)); |
|||
foreach (var style in styles) { |
|||
properties.add(style); |
|||
} |
|||
|
|||
if (!styleSpecified) { |
|||
properties.add(new FlagProperty("forceStrutHeight", value: this.forceStrutHeight, |
|||
ifTrue: "<strut height forced>", |
|||
ifFalse: "<strut height normal>")); |
|||
} |
|||
} |
|||
} |
|||
using System.Collections.Generic; |
|||
using System.Linq; |
|||
using Unity.UIWidgets.foundation; |
|||
using Unity.UIWidgets.ui; |
|||
|
|||
namespace Unity.UIWidgets.painting { |
|||
public class StrutStyle : Diagnosticable { |
|||
public StrutStyle( |
|||
string fontFamily = null, |
|||
List<string> fontFamilyFallback = null, |
|||
float? fontSize = null, |
|||
float? height = null, |
|||
float? leading = null, |
|||
FontWeight fontWeight = null, |
|||
FontStyle? fontStyle = null, |
|||
bool forceStrutHeight = false, |
|||
string debugLabel = null |
|||
) { |
|||
D.assert(fontSize == null || fontSize > 0); |
|||
D.assert(leading == null || leading >= 0); |
|||
this.fontFamily = fontFamily; |
|||
this._fontFamilyFallback = fontFamilyFallback; |
|||
this.fontSize = fontSize; |
|||
this.height = height; |
|||
this.fontWeight = fontWeight; |
|||
this.fontStyle = fontStyle; |
|||
this.leading = leading; |
|||
this.forceStrutHeight = forceStrutHeight; |
|||
this.debugLabel = debugLabel; |
|||
} |
|||
|
|||
public static StrutStyle fromTextStyle( |
|||
TextStyle textStyle, |
|||
string fontFamily = null, |
|||
List<string> fontFamilyFallback = null, |
|||
float? fontSize = null, |
|||
float? height = null, |
|||
float? leading = null, |
|||
FontWeight fontWeight = null, |
|||
FontStyle? fontStyle = null, |
|||
bool forceStrutHeight = false, |
|||
string debugLabel = null |
|||
) { |
|||
D.assert(textStyle != null); |
|||
D.assert(fontSize == null || fontSize > 0); |
|||
D.assert(leading == null || leading >= 0); |
|||
return new StrutStyle( |
|||
fontFamily: fontFamily ?? textStyle.fontFamily, |
|||
fontFamilyFallback: fontFamilyFallback ?? textStyle.fontFamilyFallback, |
|||
height: height ?? textStyle.height, |
|||
fontSize: fontSize ?? textStyle.fontSize, |
|||
fontWeight: fontWeight ?? textStyle.fontWeight, |
|||
fontStyle: fontStyle ?? textStyle.fontStyle, |
|||
debugLabel: debugLabel ?? textStyle.debugLabel |
|||
); |
|||
} |
|||
|
|||
public static readonly StrutStyle disabled = new StrutStyle( |
|||
height: 0.0f, |
|||
leading: 0.0f |
|||
); |
|||
|
|||
public readonly string fontFamily; |
|||
|
|||
public List<string> fontFamilyFallback { |
|||
get { return this._fontFamilyFallback; } |
|||
} |
|||
|
|||
readonly List<string> _fontFamilyFallback; |
|||
|
|||
public readonly float? fontSize; |
|||
public readonly float? height; |
|||
public readonly FontWeight fontWeight; |
|||
public readonly FontStyle? fontStyle; |
|||
public readonly float? leading; |
|||
public readonly bool forceStrutHeight; |
|||
public readonly string debugLabel; |
|||
|
|||
public RenderComparison compareTo(StrutStyle other) { |
|||
if (ReferenceEquals(this, other)) { |
|||
return RenderComparison.identical; |
|||
} |
|||
|
|||
if (other == null) { |
|||
return RenderComparison.layout; |
|||
} |
|||
|
|||
if (this.fontFamily != other.fontFamily || |
|||
this.fontSize != other.fontSize || |
|||
this.fontWeight != other.fontWeight || |
|||
this.fontStyle != other.fontStyle || |
|||
this.height != other.height || |
|||
this.leading != other.leading || |
|||
this.forceStrutHeight != other.forceStrutHeight || |
|||
!CollectionUtils.equalsList(this.fontFamilyFallback, other.fontFamilyFallback)) { |
|||
return RenderComparison.layout; |
|||
} |
|||
|
|||
return RenderComparison.identical; |
|||
} |
|||
|
|||
public StrutStyle inheritFromTextStyle(TextStyle other) { |
|||
if (other == null) { |
|||
return this; |
|||
} |
|||
|
|||
return new StrutStyle( |
|||
fontFamily: this.fontFamily ?? other.fontFamily, |
|||
fontFamilyFallback: this.fontFamilyFallback ?? other.fontFamilyFallback, |
|||
height: this.height ?? other.height, |
|||
leading: this.leading, |
|||
fontSize: this.fontSize ?? other.fontSize, |
|||
fontWeight: this.fontWeight ?? other.fontWeight, |
|||
fontStyle: this.fontStyle ?? other.fontStyle, |
|||
forceStrutHeight: this.forceStrutHeight, |
|||
debugLabel: this.debugLabel ?? other.debugLabel |
|||
); |
|||
} |
|||
|
|||
public bool Equals(StrutStyle other) { |
|||
if (ReferenceEquals(null, other)) { |
|||
return false; |
|||
} |
|||
|
|||
if (ReferenceEquals(this, other)) { |
|||
return true; |
|||
} |
|||
|
|||
return this.fontFamily == other.fontFamily && |
|||
this.fontSize == other.fontSize && |
|||
this.fontWeight == other.fontWeight && |
|||
this.fontStyle == other.fontStyle && |
|||
this.height == other.height && |
|||
this.leading == other.leading && |
|||
this.forceStrutHeight == other.forceStrutHeight; |
|||
} |
|||
|
|||
public override bool Equals(object obj) { |
|||
if (ReferenceEquals(null, obj)) { |
|||
return false; |
|||
} |
|||
|
|||
if (ReferenceEquals(this, obj)) { |
|||
return true; |
|||
} |
|||
|
|||
if (obj.GetType() != this.GetType()) { |
|||
return false; |
|||
} |
|||
|
|||
return this.Equals((StrutStyle) obj); |
|||
} |
|||
|
|||
public static bool operator ==(StrutStyle left, StrutStyle right) { |
|||
return Equals(left, right); |
|||
} |
|||
|
|||
public static bool operator !=(StrutStyle left, StrutStyle right) { |
|||
return !Equals(left, right); |
|||
} |
|||
|
|||
public override int GetHashCode() { |
|||
unchecked { |
|||
var hashCode = this.fontFamily?.GetHashCode() ?? 0; |
|||
hashCode = (hashCode * 397) ^ (this.fontSize?.GetHashCode() ?? 0); |
|||
hashCode = (hashCode * 397) ^ (this.fontWeight?.GetHashCode() ?? 0); |
|||
hashCode = (hashCode * 397) ^ (this.fontStyle?.GetHashCode() ?? 0); |
|||
hashCode = (hashCode * 397) ^ (this.height?.GetHashCode() ?? 0); |
|||
hashCode = (hashCode * 397) ^ (this.leading?.GetHashCode() ?? 0); |
|||
hashCode = (hashCode * 397) ^ this.forceStrutHeight.GetHashCode(); |
|||
return hashCode; |
|||
} |
|||
} |
|||
|
|||
public override string toStringShort() { |
|||
return $"{this.GetType()}"; |
|||
} |
|||
|
|||
public override void debugFillProperties(DiagnosticPropertiesBuilder properties) { |
|||
base.debugFillProperties(properties); |
|||
if (this.debugLabel != null) { |
|||
properties.add(new MessageProperty("debugLabel", this.debugLabel)); |
|||
} |
|||
|
|||
List<DiagnosticsNode> styles = new List<DiagnosticsNode>(); |
|||
styles.Add(new StringProperty("family", this.fontFamily, defaultValue: Diagnostics.kNullDefaultValue, |
|||
quoted: false)); |
|||
styles.Add(new EnumerableProperty<string>("familyFallback", this.fontFamilyFallback)); |
|||
styles.Add(new DiagnosticsProperty<float?>("size", this.fontSize, |
|||
defaultValue: Diagnostics.kNullDefaultValue)); |
|||
string weightDescription = ""; |
|||
if (this.fontWeight != null) { |
|||
weightDescription = this.fontWeight.weightValue.ToString(); |
|||
} |
|||
|
|||
styles.Add(new DiagnosticsProperty<FontWeight>( |
|||
"weight", this.fontWeight, |
|||
description: weightDescription, |
|||
defaultValue: Diagnostics.kNullDefaultValue |
|||
)); |
|||
styles.Add(new EnumProperty<FontStyle?>("style", this.fontStyle, |
|||
defaultValue: Diagnostics.kNullDefaultValue)); |
|||
styles.Add(new DiagnosticsProperty<float?>("height", this.height, |
|||
defaultValue: Diagnostics.kNullDefaultValue)); |
|||
styles.Add(new FlagProperty("forceStrutHeight", value: this.forceStrutHeight, |
|||
defaultValue: Diagnostics.kNullDefaultValue)); |
|||
|
|||
bool styleSpecified = styles.Any((DiagnosticsNode n) => !n.isFiltered(DiagnosticLevel.info)); |
|||
foreach (var style in styles) { |
|||
properties.add(style); |
|||
} |
|||
|
|||
if (!styleSpecified) { |
|||
properties.add(new FlagProperty("forceStrutHeight", value: this.forceStrutHeight, |
|||
ifTrue: "<strut height forced>", |
|||
ifFalse: "<strut height normal>")); |
|||
} |
|||
} |
|||
} |
|||
} |
|
|||
fileFormatVersion: 2 |
|||
guid: 0c0ee979ef6b4f32a092532c3bdae31e |
|||
fileFormatVersion: 2 |
|||
guid: 0c0ee979ef6b4f32a092532c3bdae31e |
|||
timeCreated: 1556178331 |
|
|||
fileFormatVersion: 2 |
|||
guid: aa89369405044a62a19225b2c20815f6 |
|||
fileFormatVersion: 2 |
|||
guid: aa89369405044a62a19225b2c20815f6 |
|||
timeCreated: 1553156127 |
|
|||
fileFormatVersion: 2 |
|||
guid: 9c88d19dd1254f4eb6c0c998084832ea |
|||
fileFormatVersion: 2 |
|||
guid: 9c88d19dd1254f4eb6c0c998084832ea |
|||
timeCreated: 1556185402 |
|
|||
fileFormatVersion: 2 |
|||
guid: 6b8f793d1b8d4004b2c19c1c085752c3 |
|||
fileFormatVersion: 2 |
|||
guid: 6b8f793d1b8d4004b2c19c1c085752c3 |
|||
timeCreated: 1553161698 |
|
|||
fileFormatVersion: 2 |
|||
guid: d74c4f94f0994a3880425c80ec3d9ba3 |
|||
fileFormatVersion: 2 |
|||
guid: d74c4f94f0994a3880425c80ec3d9ba3 |
|||
timeCreated: 1556187816 |
|
|||
fileFormatVersion: 2 |
|||
guid: d4ca6b86209d4615b9aba98d6d112673 |
|||
fileFormatVersion: 2 |
|||
guid: d4ca6b86209d4615b9aba98d6d112673 |
|||
timeCreated: 1555477997 |
|
|||
fileFormatVersion: 2 |
|||
guid: 4d46bfb5d5bb492e92f07114849df905 |
|||
fileFormatVersion: 2 |
|||
guid: 4d46bfb5d5bb492e92f07114849df905 |
|||
timeCreated: 1556165564 |
|
|||
fileFormatVersion: 2 |
|||
guid: 32591e65e11049cab124c86bc8fe573c |
|||
fileFormatVersion: 2 |
|||
guid: 32591e65e11049cab124c86bc8fe573c |
|||
timeCreated: 1556169017 |
|
|||
fileFormatVersion: 2 |
|||
guid: 54d382ec712d4e87b802716ca97c1f3d |
|||
fileFormatVersion: 2 |
|||
guid: 54d382ec712d4e87b802716ca97c1f3d |
|||
timeCreated: 1552537966 |
|
|||
using Unity.UIWidgets.foundation; |
|||
using Unity.UIWidgets.painting; |
|||
using Unity.UIWidgets.ui; |
|||
|
|||
namespace Unity.UIWidgets.material { |
|||
public class FloatingActionButtonThemeData : Diagnosticable { |
|||
public FloatingActionButtonThemeData( |
|||
Color backgroundColor = null, |
|||
Color foregroundColor = null, |
|||
float? elevation = null, |
|||
float? disabledElevation = null, |
|||
float? highlightElevation = null, |
|||
ShapeBorder shape = null |
|||
) { |
|||
this.backgroundColor = backgroundColor; |
|||
this.foregroundColor = foregroundColor; |
|||
this.elevation = elevation; |
|||
this.disabledElevation = disabledElevation; |
|||
this.highlightElevation = highlightElevation; |
|||
this.shape = shape; |
|||
} |
|||
|
|||
public readonly Color backgroundColor; |
|||
|
|||
public readonly Color foregroundColor; |
|||
|
|||
public readonly float? elevation; |
|||
|
|||
public readonly float? disabledElevation; |
|||
|
|||
public readonly float? highlightElevation; |
|||
|
|||
public readonly ShapeBorder shape; |
|||
|
|||
public FloatingActionButtonThemeData copyWith( |
|||
Color backgroundColor, |
|||
Color foregroundColor, |
|||
float? elevation, |
|||
float? disabledElevation, |
|||
float? highlightElevation, |
|||
ShapeBorder shape |
|||
) { |
|||
return new FloatingActionButtonThemeData( |
|||
backgroundColor: backgroundColor ?? this.backgroundColor, |
|||
foregroundColor: foregroundColor ?? this.foregroundColor, |
|||
elevation: elevation ?? this.elevation, |
|||
disabledElevation: disabledElevation ?? this.disabledElevation, |
|||
highlightElevation: highlightElevation ?? this.highlightElevation, |
|||
shape: shape ?? this.shape |
|||
); |
|||
} |
|||
|
|||
public static FloatingActionButtonThemeData lerp(FloatingActionButtonThemeData a, FloatingActionButtonThemeData b, |
|||
float t) { |
|||
if (a == null && b == null) { |
|||
return null; |
|||
} |
|||
|
|||
return new FloatingActionButtonThemeData( |
|||
backgroundColor: Color.lerp(a?.backgroundColor, b?.backgroundColor, t), |
|||
foregroundColor: Color.lerp(a?.foregroundColor, b?.foregroundColor, t), |
|||
elevation: MathUtils.lerpFloat(a?.elevation ?? 0, b?.elevation ?? 0, t), |
|||
disabledElevation: MathUtils.lerpFloat(a?.disabledElevation ?? 0, b?.disabledElevation ?? 0, t), |
|||
highlightElevation: MathUtils.lerpFloat(a?.highlightElevation ?? 0, b?.highlightElevation ?? 0, t), |
|||
shape: ShapeBorder.lerp(a?.shape, b?.shape, t) |
|||
); |
|||
} |
|||
|
|||
public override int GetHashCode() { |
|||
var hashCode = this.backgroundColor?.GetHashCode() ?? 0; |
|||
hashCode = (hashCode * 397) ^ this.foregroundColor?.GetHashCode() ?? 0; |
|||
hashCode = (hashCode * 397) ^ this.elevation?.GetHashCode() ?? 0; |
|||
hashCode = (hashCode * 397) ^ this.disabledElevation?.GetHashCode() ?? 0; |
|||
hashCode = (hashCode * 397) ^ this.highlightElevation?.GetHashCode() ?? 0; |
|||
hashCode = (hashCode * 397) ^ this.shape?.GetHashCode() ?? 0; |
|||
return hashCode; |
|||
} |
|||
|
|||
public bool Equals(FloatingActionButtonThemeData other) { |
|||
if (ReferenceEquals(null, other)) { |
|||
return false; |
|||
} |
|||
|
|||
if (ReferenceEquals(this, other)) { |
|||
return true; |
|||
} |
|||
|
|||
return Equals(this.backgroundColor, other.backgroundColor) |
|||
&& Equals(this.elevation, other.elevation) |
|||
&& Equals(this.shape, other.shape) |
|||
&& Equals(this.foregroundColor, other.foregroundColor) |
|||
&& Equals(this.disabledElevation, other.disabledElevation) |
|||
&& Equals(this.highlightElevation, other.highlightElevation); |
|||
} |
|||
|
|||
public override bool Equals(object obj) { |
|||
if (ReferenceEquals(null, obj)) { |
|||
return false; |
|||
} |
|||
|
|||
if (ReferenceEquals(this, obj)) { |
|||
return true; |
|||
} |
|||
|
|||
if (obj.GetType() != this.GetType()) { |
|||
return false; |
|||
} |
|||
|
|||
return this.Equals((FloatingActionButtonThemeData) obj); |
|||
} |
|||
|
|||
public override void debugFillProperties(DiagnosticPropertiesBuilder properties) { |
|||
base.debugFillProperties(properties); |
|||
FloatingActionButtonThemeData defaultData = new FloatingActionButtonThemeData(); |
|||
|
|||
properties.add(new DiagnosticsProperty<Color>("backgroundColor", this.backgroundColor, |
|||
defaultValue: defaultData.backgroundColor)); |
|||
properties.add(new DiagnosticsProperty<Color>("foregroundColor", this.foregroundColor, |
|||
defaultValue: defaultData.foregroundColor)); |
|||
properties.add(new DiagnosticsProperty<float?>("elevation", this.elevation, |
|||
defaultValue: defaultData.elevation)); |
|||
properties.add(new DiagnosticsProperty<float?>("disabledElevation", this.disabledElevation, |
|||
defaultValue: defaultData.disabledElevation)); |
|||
properties.add(new DiagnosticsProperty<float?>("highlightElevation", this.highlightElevation, |
|||
defaultValue: defaultData.highlightElevation)); |
|||
properties.add(new DiagnosticsProperty<ShapeBorder>("shape", this.shape, defaultValue: defaultData.shape)); |
|||
} |
|||
} |
|||
using Unity.UIWidgets.foundation; |
|||
using Unity.UIWidgets.painting; |
|||
using Unity.UIWidgets.ui; |
|||
|
|||
namespace Unity.UIWidgets.material { |
|||
public class FloatingActionButtonThemeData : Diagnosticable { |
|||
public FloatingActionButtonThemeData( |
|||
Color backgroundColor = null, |
|||
Color foregroundColor = null, |
|||
float? elevation = null, |
|||
float? disabledElevation = null, |
|||
float? highlightElevation = null, |
|||
ShapeBorder shape = null |
|||
) { |
|||
this.backgroundColor = backgroundColor; |
|||
this.foregroundColor = foregroundColor; |
|||
this.elevation = elevation; |
|||
this.disabledElevation = disabledElevation; |
|||
this.highlightElevation = highlightElevation; |
|||
this.shape = shape; |
|||
} |
|||
|
|||
public readonly Color backgroundColor; |
|||
|
|||
public readonly Color foregroundColor; |
|||
|
|||
public readonly float? elevation; |
|||
|
|||
public readonly float? disabledElevation; |
|||
|
|||
public readonly float? highlightElevation; |
|||
|
|||
public readonly ShapeBorder shape; |
|||
|
|||
public FloatingActionButtonThemeData copyWith( |
|||
Color backgroundColor, |
|||
Color foregroundColor, |
|||
float? elevation, |
|||
float? disabledElevation, |
|||
float? highlightElevation, |
|||
ShapeBorder shape |
|||
) { |
|||
return new FloatingActionButtonThemeData( |
|||
backgroundColor: backgroundColor ?? this.backgroundColor, |
|||
foregroundColor: foregroundColor ?? this.foregroundColor, |
|||
elevation: elevation ?? this.elevation, |
|||
disabledElevation: disabledElevation ?? this.disabledElevation, |
|||
highlightElevation: highlightElevation ?? this.highlightElevation, |
|||
shape: shape ?? this.shape |
|||
); |
|||
} |
|||
|
|||
public static FloatingActionButtonThemeData lerp(FloatingActionButtonThemeData a, FloatingActionButtonThemeData b, |
|||
float t) { |
|||
if (a == null && b == null) { |
|||
return null; |
|||
} |
|||
|
|||
return new FloatingActionButtonThemeData( |
|||
backgroundColor: Color.lerp(a?.backgroundColor, b?.backgroundColor, t), |
|||
foregroundColor: Color.lerp(a?.foregroundColor, b?.foregroundColor, t), |
|||
elevation: MathUtils.lerpFloat(a?.elevation ?? 0, b?.elevation ?? 0, t), |
|||
disabledElevation: MathUtils.lerpFloat(a?.disabledElevation ?? 0, b?.disabledElevation ?? 0, t), |
|||
highlightElevation: MathUtils.lerpFloat(a?.highlightElevation ?? 0, b?.highlightElevation ?? 0, t), |
|||
shape: ShapeBorder.lerp(a?.shape, b?.shape, t) |
|||
); |
|||
} |
|||
|
|||
public override int GetHashCode() { |
|||
var hashCode = this.backgroundColor?.GetHashCode() ?? 0; |
|||
hashCode = (hashCode * 397) ^ this.foregroundColor?.GetHashCode() ?? 0; |
|||
hashCode = (hashCode * 397) ^ this.elevation?.GetHashCode() ?? 0; |
|||
hashCode = (hashCode * 397) ^ this.disabledElevation?.GetHashCode() ?? 0; |
|||
hashCode = (hashCode * 397) ^ this.highlightElevation?.GetHashCode() ?? 0; |
|||
hashCode = (hashCode * 397) ^ this.shape?.GetHashCode() ?? 0; |
|||
return hashCode; |
|||
} |
|||
|
|||
public bool Equals(FloatingActionButtonThemeData other) { |
|||
if (ReferenceEquals(null, other)) { |
|||
return false; |
|||
} |
|||
|
|||
if (ReferenceEquals(this, other)) { |
|||
return true; |
|||
} |
|||
|
|||
return Equals(this.backgroundColor, other.backgroundColor) |
|||
&& Equals(this.elevation, other.elevation) |
|||
&& Equals(this.shape, other.shape) |
|||
&& Equals(this.foregroundColor, other.foregroundColor) |
|||
&& Equals(this.disabledElevation, other.disabledElevation) |
|||
&& Equals(this.highlightElevation, other.highlightElevation); |
|||
} |
|||
|
|||
public override bool Equals(object obj) { |
|||
if (ReferenceEquals(null, obj)) { |
|||
return false; |
|||
} |
|||
|
|||
if (ReferenceEquals(this, obj)) { |
|||
return true; |
|||
} |
|||
|
|||
if (obj.GetType() != this.GetType()) { |
|||
return false; |
|||
} |
|||
|
|||
return this.Equals((FloatingActionButtonThemeData) obj); |
|||
} |
|||
|
|||
public override void debugFillProperties(DiagnosticPropertiesBuilder properties) { |
|||
base.debugFillProperties(properties); |
|||
FloatingActionButtonThemeData defaultData = new FloatingActionButtonThemeData(); |
|||
|
|||
properties.add(new DiagnosticsProperty<Color>("backgroundColor", this.backgroundColor, |
|||
defaultValue: defaultData.backgroundColor)); |
|||
properties.add(new DiagnosticsProperty<Color>("foregroundColor", this.foregroundColor, |
|||
defaultValue: defaultData.foregroundColor)); |
|||
properties.add(new DiagnosticsProperty<float?>("elevation", this.elevation, |
|||
defaultValue: defaultData.elevation)); |
|||
properties.add(new DiagnosticsProperty<float?>("disabledElevation", this.disabledElevation, |
|||
defaultValue: defaultData.disabledElevation)); |
|||
properties.add(new DiagnosticsProperty<float?>("highlightElevation", this.highlightElevation, |
|||
defaultValue: defaultData.highlightElevation)); |
|||
properties.add(new DiagnosticsProperty<ShapeBorder>("shape", this.shape, defaultValue: defaultData.shape)); |
|||
} |
|||
} |
|||
} |
|
|||
fileFormatVersion: 2 |
|||
guid: 6431f8efd0b444ea9982ff6d0a0e74f4 |
|||
fileFormatVersion: 2 |
|||
guid: 6431f8efd0b444ea9982ff6d0a0e74f4 |
|||
timeCreated: 1552619063 |
|
|||
fileFormatVersion: 2 |
|||
guid: 2106361ab28b4560a12fdbae0f1b1c8f |
|||
fileFormatVersion: 2 |
|||
guid: 2106361ab28b4560a12fdbae0f1b1c8f |
|||
timeCreated: 1552548801 |
|
|||
fileFormatVersion: 2 |
|||
guid: 5c535b1d86054795b33e7f0f358d957a |
|||
fileFormatVersion: 2 |
|||
guid: 5c535b1d86054795b33e7f0f358d957a |
|||
timeCreated: 1552641292 |
|
|||
fileFormatVersion: 2 |
|||
guid: 42b0b0b180624c38b2dec573a232bd8a |
|||
fileFormatVersion: 2 |
|||
guid: 42b0b0b180624c38b2dec573a232bd8a |
|||
timeCreated: 1552530632 |
|
|||
fileFormatVersion: 2 |
|||
guid: 1328fb437a4047c49b51dfe3a2c3d106 |
|||
fileFormatVersion: 2 |
|||
guid: 1328fb437a4047c49b51dfe3a2c3d106 |
|||
timeCreated: 1553154501 |
|
|||
fileFormatVersion: 2 |
|||
guid: 74dd879101fe4541a5b42619cf4f309c |
|||
fileFormatVersion: 2 |
|||
guid: 74dd879101fe4541a5b42619cf4f309c |
|||
timeCreated: 1554344854 |
|
|||
fileFormatVersion: 2 |
|||
guid: 0a9f1467be1c4653b3dbf8a1c7209f70 |
|||
fileFormatVersion: 2 |
|||
guid: 0a9f1467be1c4653b3dbf8a1c7209f70 |
|||
timeCreated: 1557304128 |
|
|||
fileFormatVersion: 2 |
|||
guid: fb34d1a7ff484d05b8074a10730d51a5 |
|||
fileFormatVersion: 2 |
|||
guid: fb34d1a7ff484d05b8074a10730d51a5 |
|||
timeCreated: 1554352270 |
|
|||
fileFormatVersion: 2 |
|||
guid: 439a75e2cc39472ba806029b41996e9b |
|||
fileFormatVersion: 2 |
|||
guid: 439a75e2cc39472ba806029b41996e9b |
|||
timeCreated: 1552892371 |
|
|||
fileFormatVersion: 2 |
|||
guid: f38795e53cc54f628c2125e5e50fea1c |
|||
fileFormatVersion: 2 |
|||
guid: f38795e53cc54f628c2125e5e50fea1c |
|||
timeCreated: 1552894502 |
|
|||
fileFormatVersion: 2 |
|||
guid: e3a716495016460c8599ef52f9bd690f |
|||
fileFormatVersion: 2 |
|||
guid: e3a716495016460c8599ef52f9bd690f |
|||
timeCreated: 1552894786 |
|
|||
fileFormatVersion: 2 |
|||
guid: 36d19e17f3d947f98c7accec2cd5771b |
|||
fileFormatVersion: 2 |
|||
guid: 36d19e17f3d947f98c7accec2cd5771b |
|||
timeCreated: 1556097117 |
|
|||
using System.Collections.Generic; |
|||
using Unity.UIWidgets.foundation; |
|||
using Unity.UIWidgets.gestures; |
|||
using Unity.UIWidgets.painting; |
|||
using Unity.UIWidgets.rendering; |
|||
using Unity.UIWidgets.ui; |
|||
using Unity.UIWidgets.widgets; |
|||
using TextStyle = Unity.UIWidgets.painting.TextStyle; |
|||
|
|||
namespace Unity.UIWidgets.cupertino { |
|||
class BottomAppBarUtils { |
|||
public const float _kTabBarHeight = 50.0f; |
|||
public static readonly Color _kDefaultTabBarBorderColor = new Color(0x4C000000); |
|||
} |
|||
|
|||
|
|||
public class CupertinoTabBar : StatelessWidget { |
|||
public CupertinoTabBar( |
|||
Key key = null, |
|||
List<BottomNavigationBarItem> items = null, |
|||
ValueChanged<int> onTap = null, |
|||
int currentIndex = 0, |
|||
Color backgroundColor = null, |
|||
Color activeColor = null, |
|||
Color inactiveColor = null, |
|||
float iconSize = 30.0f, |
|||
Border border = null |
|||
) : base(key: key) { |
|||
D.assert(items != null); |
|||
D.assert(items.Count >= 2, |
|||
() => "Tabs need at least 2 items to conform to Apple's HIG" |
|||
); |
|||
D.assert(0 <= currentIndex && currentIndex < items.Count); |
|||
|
|||
|
|||
this.items = items; |
|||
this.onTap = onTap; |
|||
this.currentIndex = currentIndex; |
|||
|
|||
this.backgroundColor = backgroundColor; |
|||
this.activeColor = activeColor; |
|||
this.inactiveColor = inactiveColor ?? CupertinoColors.inactiveGray; |
|||
this.iconSize = iconSize; |
|||
this.border = border ?? new Border( |
|||
top: new BorderSide( |
|||
color: BottomAppBarUtils._kDefaultTabBarBorderColor, |
|||
width: 0.0f, // One physical pixel.
|
|||
style: BorderStyle.solid |
|||
) |
|||
); |
|||
} |
|||
|
|||
public readonly List<BottomNavigationBarItem> items; |
|||
|
|||
public readonly ValueChanged<int> onTap; |
|||
|
|||
public readonly int currentIndex; |
|||
|
|||
public readonly Color backgroundColor; |
|||
|
|||
public readonly Color activeColor; |
|||
|
|||
public readonly Color inactiveColor; |
|||
|
|||
public readonly float iconSize; |
|||
|
|||
public readonly Border border; |
|||
|
|||
public Size preferredSize { |
|||
get { return Size.fromHeight(BottomAppBarUtils._kTabBarHeight); } |
|||
} |
|||
|
|||
public bool opaque(BuildContext context) { |
|||
Color backgroundColor = |
|||
this.backgroundColor ?? CupertinoTheme.of(context).barBackgroundColor; |
|||
return backgroundColor.alpha == 0xFF; |
|||
} |
|||
|
|||
public override Widget build(BuildContext context) { |
|||
float bottomPadding = MediaQuery.of(context).padding.bottom; |
|||
|
|||
Widget result = new DecoratedBox( |
|||
decoration: new BoxDecoration( |
|||
border: this.border, |
|||
color: this.backgroundColor ?? CupertinoTheme.of(context).barBackgroundColor |
|||
), |
|||
child: new SizedBox( |
|||
height: BottomAppBarUtils._kTabBarHeight + bottomPadding, |
|||
child: IconTheme.merge( // Default with the inactive state.
|
|||
data: new IconThemeData( |
|||
color: this.inactiveColor, |
|||
size: this.iconSize |
|||
), |
|||
child: new DefaultTextStyle( // Default with the inactive state.
|
|||
style: CupertinoTheme.of(context).textTheme.tabLabelTextStyle |
|||
.copyWith(color: this.inactiveColor), |
|||
child: new Padding( |
|||
padding: EdgeInsets.only(bottom: bottomPadding), |
|||
child: new Row( |
|||
crossAxisAlignment: CrossAxisAlignment.end, |
|||
children: this._buildTabItems(context) |
|||
) |
|||
) |
|||
) |
|||
) |
|||
) |
|||
); |
|||
|
|||
if (!this.opaque(context)) { |
|||
result = new ClipRect( |
|||
child: new BackdropFilter( |
|||
filter: ImageFilter.blur(sigmaX: 10.0f, sigmaY: 10.0f), |
|||
child: result |
|||
) |
|||
); |
|||
} |
|||
|
|||
return result; |
|||
} |
|||
|
|||
List<Widget> _buildTabItems(BuildContext context) { |
|||
List<Widget> result = new List<Widget> { }; |
|||
|
|||
for (int index = 0; index < this.items.Count; index += 1) { |
|||
bool active = index == this.currentIndex; |
|||
var tabIndex = index; |
|||
result.Add( |
|||
this._wrapActiveItem( |
|||
context, |
|||
new Expanded( |
|||
child: new GestureDetector( |
|||
behavior: HitTestBehavior.opaque, |
|||
onTap: this.onTap == null ? null : (GestureTapCallback) (() => { this.onTap(tabIndex); }), |
|||
child: new Padding( |
|||
padding: EdgeInsets.only(bottom: 4.0f), |
|||
child: new Column( |
|||
mainAxisAlignment: MainAxisAlignment.end, |
|||
children: this._buildSingleTabItem(this.items[index], active) |
|||
) |
|||
) |
|||
) |
|||
), |
|||
active: active |
|||
) |
|||
); |
|||
} |
|||
|
|||
return result; |
|||
} |
|||
|
|||
List<Widget> _buildSingleTabItem(BottomNavigationBarItem item, bool active) { |
|||
List<Widget> components = new List<Widget> { |
|||
new Expanded( |
|||
child: new Center(child: active ? item.activeIcon : item.icon) |
|||
) |
|||
}; |
|||
|
|||
if (item.title != null) { |
|||
components.Add(item.title); |
|||
} |
|||
|
|||
return components; |
|||
} |
|||
|
|||
Widget _wrapActiveItem(BuildContext context, Widget item, bool active) { |
|||
if (!active) { |
|||
return item; |
|||
} |
|||
|
|||
Color activeColor = this.activeColor ?? CupertinoTheme.of(context).primaryColor; |
|||
return IconTheme.merge( |
|||
data: new IconThemeData(color: activeColor), |
|||
child: DefaultTextStyle.merge( |
|||
style: new TextStyle(color: activeColor), |
|||
child: item |
|||
) |
|||
); |
|||
} |
|||
|
|||
public CupertinoTabBar copyWith( |
|||
Key key = null, |
|||
List<BottomNavigationBarItem> items = null, |
|||
Color backgroundColor = null, |
|||
Color activeColor = null, |
|||
Color inactiveColor = null, |
|||
float? iconSize = null, |
|||
Border border = null, |
|||
int? currentIndex = null, |
|||
ValueChanged<int> onTap = null |
|||
) { |
|||
return new CupertinoTabBar( |
|||
key: key ?? this.key, |
|||
items: items ?? this.items, |
|||
backgroundColor: backgroundColor ?? this.backgroundColor, |
|||
activeColor: activeColor ?? this.activeColor, |
|||
inactiveColor: inactiveColor ?? this.inactiveColor, |
|||
iconSize: iconSize ?? this.iconSize, |
|||
border: border ?? this.border, |
|||
currentIndex: currentIndex ?? this.currentIndex, |
|||
onTap: onTap ?? this.onTap |
|||
); |
|||
} |
|||
} |
|||
using System.Collections.Generic; |
|||
using Unity.UIWidgets.foundation; |
|||
using Unity.UIWidgets.gestures; |
|||
using Unity.UIWidgets.painting; |
|||
using Unity.UIWidgets.rendering; |
|||
using Unity.UIWidgets.ui; |
|||
using Unity.UIWidgets.widgets; |
|||
using TextStyle = Unity.UIWidgets.painting.TextStyle; |
|||
|
|||
namespace Unity.UIWidgets.cupertino { |
|||
class BottomAppBarUtils { |
|||
public const float _kTabBarHeight = 50.0f; |
|||
public static readonly Color _kDefaultTabBarBorderColor = new Color(0x4C000000); |
|||
} |
|||
|
|||
|
|||
public class CupertinoTabBar : StatelessWidget { |
|||
public CupertinoTabBar( |
|||
Key key = null, |
|||
List<BottomNavigationBarItem> items = null, |
|||
ValueChanged<int> onTap = null, |
|||
int currentIndex = 0, |
|||
Color backgroundColor = null, |
|||
Color activeColor = null, |
|||
Color inactiveColor = null, |
|||
float iconSize = 30.0f, |
|||
Border border = null |
|||
) : base(key: key) { |
|||
D.assert(items != null); |
|||
D.assert(items.Count >= 2, |
|||
() => "Tabs need at least 2 items to conform to Apple's HIG" |
|||
); |
|||
D.assert(0 <= currentIndex && currentIndex < items.Count); |
|||
|
|||
|
|||
this.items = items; |
|||
this.onTap = onTap; |
|||
this.currentIndex = currentIndex; |
|||
|
|||
this.backgroundColor = backgroundColor; |
|||
this.activeColor = activeColor; |
|||
this.inactiveColor = inactiveColor ?? CupertinoColors.inactiveGray; |
|||
this.iconSize = iconSize; |
|||
this.border = border ?? new Border( |
|||
top: new BorderSide( |
|||
color: BottomAppBarUtils._kDefaultTabBarBorderColor, |
|||
width: 0.0f, // One physical pixel.
|
|||
style: BorderStyle.solid |
|||
) |
|||
); |
|||
} |
|||
|
|||
public readonly List<BottomNavigationBarItem> items; |
|||
|
|||
public readonly ValueChanged<int> onTap; |
|||
|
|||
public readonly int currentIndex; |
|||
|
|||
public readonly Color backgroundColor; |
|||
|
|||
public readonly Color activeColor; |
|||
|
|||
public readonly Color inactiveColor; |
|||
|
|||
public readonly float iconSize; |
|||
|
|||
public readonly Border border; |
|||
|
|||
public Size preferredSize { |
|||
get { return Size.fromHeight(BottomAppBarUtils._kTabBarHeight); } |
|||
} |
|||
|
|||
public bool opaque(BuildContext context) { |
|||
Color backgroundColor = |
|||
this.backgroundColor ?? CupertinoTheme.of(context).barBackgroundColor; |
|||
return backgroundColor.alpha == 0xFF; |
|||
} |
|||
|
|||
public override Widget build(BuildContext context) { |
|||
float bottomPadding = MediaQuery.of(context).padding.bottom; |
|||
|
|||
Widget result = new DecoratedBox( |
|||
decoration: new BoxDecoration( |
|||
border: this.border, |
|||
color: this.backgroundColor ?? CupertinoTheme.of(context).barBackgroundColor |
|||
), |
|||
child: new SizedBox( |
|||
height: BottomAppBarUtils._kTabBarHeight + bottomPadding, |
|||
child: IconTheme.merge( // Default with the inactive state.
|
|||
data: new IconThemeData( |
|||
color: this.inactiveColor, |
|||
size: this.iconSize |
|||
), |
|||
child: new DefaultTextStyle( // Default with the inactive state.
|
|||
style: CupertinoTheme.of(context).textTheme.tabLabelTextStyle |
|||
.copyWith(color: this.inactiveColor), |
|||
child: new Padding( |
|||
padding: EdgeInsets.only(bottom: bottomPadding), |
|||
child: new Row( |
|||
crossAxisAlignment: CrossAxisAlignment.end, |
|||
children: this._buildTabItems(context) |
|||
) |
|||
) |
|||
) |
|||
) |
|||
) |
|||
); |
|||
|
|||
if (!this.opaque(context)) { |
|||
result = new ClipRect( |
|||
child: new BackdropFilter( |
|||
filter: ImageFilter.blur(sigmaX: 10.0f, sigmaY: 10.0f), |
|||
child: result |
|||
) |
|||
); |
|||
} |
|||
|
|||
return result; |
|||
} |
|||
|
|||
List<Widget> _buildTabItems(BuildContext context) { |
|||
List<Widget> result = new List<Widget> { }; |
|||
|
|||
for (int index = 0; index < this.items.Count; index += 1) { |
|||
bool active = index == this.currentIndex; |
|||
var tabIndex = index; |
|||
result.Add( |
|||
this._wrapActiveItem( |
|||
context, |
|||
new Expanded( |
|||
child: new GestureDetector( |
|||
behavior: HitTestBehavior.opaque, |
|||
onTap: this.onTap == null ? null : (GestureTapCallback) (() => { this.onTap(tabIndex); }), |
|||
child: new Padding( |
|||
padding: EdgeInsets.only(bottom: 4.0f), |
|||
child: new Column( |
|||
mainAxisAlignment: MainAxisAlignment.end, |
|||
children: this._buildSingleTabItem(this.items[index], active) |
|||
) |
|||
) |
|||
) |
|||
), |
|||
active: active |
|||
) |
|||
); |
|||
} |
|||
|
|||
return result; |
|||
} |
|||
|
|||
List<Widget> _buildSingleTabItem(BottomNavigationBarItem item, bool active) { |
|||
List<Widget> components = new List<Widget> { |
|||
new Expanded( |
|||
child: new Center(child: active ? item.activeIcon : item.icon) |
|||
) |
|||
}; |
|||
|
|||
if (item.title != null) { |
|||
components.Add(item.title); |
|||
} |
|||
|
|||
return components; |
|||
} |
|||
|
|||
Widget _wrapActiveItem(BuildContext context, Widget item, bool active) { |
|||
if (!active) { |
|||
return item; |
|||
} |
|||
|
|||
Color activeColor = this.activeColor ?? CupertinoTheme.of(context).primaryColor; |
|||
return IconTheme.merge( |
|||
data: new IconThemeData(color: activeColor), |
|||
child: DefaultTextStyle.merge( |
|||
style: new TextStyle(color: activeColor), |
|||
child: item |
|||
) |
|||
); |
|||
} |
|||
|
|||
public CupertinoTabBar copyWith( |
|||
Key key = null, |
|||
List<BottomNavigationBarItem> items = null, |
|||
Color backgroundColor = null, |
|||
Color activeColor = null, |
|||
Color inactiveColor = null, |
|||
float? iconSize = null, |
|||
Border border = null, |
|||
int? currentIndex = null, |
|||
ValueChanged<int> onTap = null |
|||
) { |
|||
return new CupertinoTabBar( |
|||
key: key ?? this.key, |
|||
items: items ?? this.items, |
|||
backgroundColor: backgroundColor ?? this.backgroundColor, |
|||
activeColor: activeColor ?? this.activeColor, |
|||
inactiveColor: inactiveColor ?? this.inactiveColor, |
|||
iconSize: iconSize ?? this.iconSize, |
|||
border: border ?? this.border, |
|||
currentIndex: currentIndex ?? this.currentIndex, |
|||
onTap: onTap ?? this.onTap |
|||
); |
|||
} |
|||
} |
|||
} |
1001
com.unity.uiwidgets/Runtime/cupertino/nav_bar.cs
文件差异内容过多而无法显示
查看文件
文件差异内容过多而无法显示
查看文件
|
|||
using System; |
|||
using Unity.UIWidgets.animation; |
|||
using Unity.UIWidgets.foundation; |
|||
using Unity.UIWidgets.gestures; |
|||
using Unity.UIWidgets.rendering; |
|||
using Unity.UIWidgets.scheduler; |
|||
using Unity.UIWidgets.ui; |
|||
using Unity.UIWidgets.widgets; |
|||
using UnityEngine; |
|||
using Canvas = Unity.UIWidgets.ui.Canvas; |
|||
using Color = Unity.UIWidgets.ui.Color; |
|||
using Rect = Unity.UIWidgets.ui.Rect; |
|||
|
|||
namespace Unity.UIWidgets.cupertino { |
|||
class SliderUtils { |
|||
public const float _kPadding = 8.0f; |
|||
public static readonly Color _kTrackColor = new Color(0xFFB5B5B5); |
|||
public const float _kSliderHeight = 2.0f * (CupertinoThumbPainter.radius + _kPadding); |
|||
public const float _kSliderWidth = 176.0f; // Matches Material Design slider.
|
|||
public static readonly TimeSpan _kDiscreteTransitionDuration = new TimeSpan(0, 0, 0, 0, 500); |
|||
public const float _kAdjustmentUnit = 0.1f; // Matches iOS implementation of material slider.
|
|||
} |
|||
|
|||
public class CupertinoSlider : StatefulWidget { |
|||
public CupertinoSlider( |
|||
Key key = null, |
|||
float? value = null, |
|||
ValueChanged<float> onChanged = null, |
|||
ValueChanged<float> onChangeStart = null, |
|||
ValueChanged<float> onChangeEnd = null, |
|||
float min = 0.0f, |
|||
float max = 1.0f, |
|||
int? divisions = null, |
|||
Color activeColor = null |
|||
) : base(key: key) { |
|||
D.assert(value != null); |
|||
D.assert(onChanged != null); |
|||
D.assert(value >= min && value <= max); |
|||
D.assert(divisions == null || divisions > 0); |
|||
this.value = value.Value; |
|||
this.onChanged = onChanged; |
|||
this.onChangeStart = onChangeStart; |
|||
this.onChangeEnd = onChangeEnd; |
|||
this.min = min; |
|||
this.max = max; |
|||
this.divisions = divisions; |
|||
this.activeColor = activeColor; |
|||
} |
|||
|
|||
public readonly float value; |
|||
|
|||
public readonly ValueChanged<float> onChanged; |
|||
|
|||
public readonly ValueChanged<float> onChangeStart; |
|||
|
|||
public readonly ValueChanged<float> onChangeEnd; |
|||
|
|||
public readonly float min; |
|||
|
|||
public readonly float max; |
|||
|
|||
public readonly int? divisions; |
|||
|
|||
public readonly Color activeColor; |
|||
|
|||
public override State createState() { |
|||
return new _CupertinoSliderState(); |
|||
} |
|||
|
|||
public override void debugFillProperties(DiagnosticPropertiesBuilder properties) { |
|||
base.debugFillProperties(properties); |
|||
properties.add(new FloatProperty("value", this.value)); |
|||
properties.add(new FloatProperty("min", this.min)); |
|||
properties.add(new FloatProperty("max", this.max)); |
|||
} |
|||
} |
|||
|
|||
class _CupertinoSliderState : TickerProviderStateMixin<CupertinoSlider> { |
|||
void _handleChanged(float value) { |
|||
D.assert(this.widget.onChanged != null); |
|||
float lerpValue = MathUtils.lerpFloat(this.widget.min, this.widget.max, value); |
|||
if (lerpValue != this.widget.value) { |
|||
this.widget.onChanged(lerpValue); |
|||
} |
|||
} |
|||
|
|||
void _handleDragStart(float value) { |
|||
D.assert(this.widget.onChangeStart != null); |
|||
this.widget.onChangeStart(MathUtils.lerpFloat(this.widget.min, this.widget.max, value)); |
|||
} |
|||
|
|||
void _handleDragEnd(float value) { |
|||
D.assert(this.widget.onChangeEnd != null); |
|||
this.widget.onChangeEnd(MathUtils.lerpFloat(this.widget.min, this.widget.max, value)); |
|||
} |
|||
|
|||
public override Widget build(BuildContext context) { |
|||
return new _CupertinoSliderRenderObjectWidget( |
|||
value: (this.widget.value - this.widget.min) / (this.widget.max - this.widget.min), |
|||
divisions: this.widget.divisions, |
|||
activeColor: this.widget.activeColor ?? CupertinoTheme.of(context).primaryColor, |
|||
onChanged: this.widget.onChanged != null ? (ValueChanged<float>) this._handleChanged : null, |
|||
onChangeStart: this.widget.onChangeStart != null ? (ValueChanged<float>) this._handleDragStart : null, |
|||
onChangeEnd: this.widget.onChangeEnd != null ? (ValueChanged<float>) this._handleDragEnd : null, |
|||
vsync: this |
|||
); |
|||
} |
|||
} |
|||
|
|||
class _CupertinoSliderRenderObjectWidget : LeafRenderObjectWidget { |
|||
public _CupertinoSliderRenderObjectWidget( |
|||
Key key = null, |
|||
float? value = null, |
|||
int? divisions = null, |
|||
Color activeColor = null, |
|||
ValueChanged<float> onChanged = null, |
|||
ValueChanged<float> onChangeStart = null, |
|||
ValueChanged<float> onChangeEnd = null, |
|||
TickerProvider vsync = null |
|||
) : base(key: key) { |
|||
this.value = value; |
|||
this.divisions = divisions; |
|||
this.activeColor = activeColor; |
|||
this.onChanged = onChanged; |
|||
this.onChangeStart = onChangeStart; |
|||
this.onChangeEnd = onChangeEnd; |
|||
this.vsync = vsync; |
|||
} |
|||
|
|||
public readonly float? value; |
|||
public readonly int? divisions; |
|||
public readonly Color activeColor; |
|||
public readonly ValueChanged<float> onChanged; |
|||
public readonly ValueChanged<float> onChangeStart; |
|||
public readonly ValueChanged<float> onChangeEnd; |
|||
public readonly TickerProvider vsync; |
|||
|
|||
public override RenderObject createRenderObject(BuildContext context) { |
|||
return new _RenderCupertinoSlider( |
|||
value: this.value ?? 0.0f, |
|||
divisions: this.divisions, |
|||
activeColor: this.activeColor, |
|||
onChanged: this.onChanged, |
|||
onChangeStart: this.onChangeStart, |
|||
onChangeEnd: this.onChangeEnd, |
|||
vsync: this.vsync |
|||
); |
|||
} |
|||
|
|||
public override void updateRenderObject(BuildContext context, RenderObject _renderObject) { |
|||
_RenderCupertinoSlider renderObject = _renderObject as _RenderCupertinoSlider; |
|||
renderObject.value = this.value ?? 0.0f; |
|||
renderObject.divisions = this.divisions; |
|||
renderObject.activeColor = this.activeColor; |
|||
renderObject.onChanged = this.onChanged; |
|||
renderObject.onChangeStart = this.onChangeStart; |
|||
renderObject.onChangeEnd = this.onChangeEnd; |
|||
} |
|||
} |
|||
|
|||
class _RenderCupertinoSlider : RenderConstrainedBox { |
|||
public _RenderCupertinoSlider( |
|||
float value, |
|||
int? divisions = null, |
|||
Color activeColor = null, |
|||
ValueChanged<float> onChanged = null, |
|||
ValueChanged<float> onChangeStart = null, |
|||
ValueChanged<float> onChangeEnd = null, |
|||
TickerProvider vsync = null |
|||
) : base(additionalConstraints: BoxConstraints.tightFor(width: SliderUtils._kSliderWidth, |
|||
height: SliderUtils._kSliderHeight)) { |
|||
D.assert(value >= 0.0f && value <= 1.0f); |
|||
this._value = value; |
|||
this._divisions = divisions; |
|||
this._activeColor = activeColor; |
|||
this._onChanged = onChanged; |
|||
this.onChangeStart = onChangeStart; |
|||
this.onChangeEnd = onChangeEnd; |
|||
this._drag = new HorizontalDragGestureRecognizer(); |
|||
this._drag.onStart = this._handleDragStart; |
|||
this._drag.onUpdate = this._handleDragUpdate; |
|||
this._drag.onEnd = this._handleDragEnd; |
|||
this._position = new AnimationController( |
|||
value: value, |
|||
duration: SliderUtils._kDiscreteTransitionDuration, |
|||
vsync: vsync |
|||
); |
|||
this._position.addListener(this.markNeedsPaint); |
|||
} |
|||
|
|||
public float value { |
|||
get { return this._value; } |
|||
set { |
|||
D.assert(value >= 0.0f && value <= 1.0f); |
|||
if (value == this._value) { |
|||
return; |
|||
} |
|||
|
|||
this._value = value; |
|||
if (this.divisions != null) { |
|||
this._position.animateTo(value, curve: Curves.fastOutSlowIn); |
|||
} |
|||
else { |
|||
this._position.setValue(value); |
|||
} |
|||
} |
|||
} |
|||
|
|||
float _value; |
|||
|
|||
public int? divisions { |
|||
get { return this._divisions; } |
|||
set { |
|||
if (value == this._divisions) { |
|||
return; |
|||
} |
|||
|
|||
this._divisions = value; |
|||
this.markNeedsPaint(); |
|||
} |
|||
} |
|||
|
|||
int? _divisions; |
|||
|
|||
public Color activeColor { |
|||
get { return this._activeColor; } |
|||
set { |
|||
if (value == this._activeColor) { |
|||
return; |
|||
} |
|||
|
|||
this._activeColor = value; |
|||
this.markNeedsPaint(); |
|||
} |
|||
} |
|||
|
|||
Color _activeColor; |
|||
|
|||
public ValueChanged<float> onChanged { |
|||
get { return this._onChanged; } |
|||
set { |
|||
if (value == this._onChanged) { |
|||
return; |
|||
} |
|||
|
|||
this._onChanged = value; |
|||
} |
|||
} |
|||
|
|||
ValueChanged<float> _onChanged; |
|||
|
|||
public ValueChanged<float> onChangeStart; |
|||
public ValueChanged<float> onChangeEnd; |
|||
|
|||
|
|||
AnimationController _position; |
|||
|
|||
HorizontalDragGestureRecognizer _drag; |
|||
float _currentDragValue = 0.0f; |
|||
|
|||
float _discretizedCurrentDragValue { |
|||
get { |
|||
float dragValue = this._currentDragValue.clamp(0.0f, 1.0f); |
|||
if (this.divisions != null) { |
|||
dragValue = Mathf.Round(dragValue * this.divisions.Value) / this.divisions.Value; |
|||
} |
|||
|
|||
return dragValue; |
|||
} |
|||
} |
|||
|
|||
public float _trackLeft { |
|||
get { return SliderUtils._kPadding; } |
|||
} |
|||
|
|||
public float _trackRight { |
|||
get { return this.size.width - SliderUtils._kPadding; } |
|||
} |
|||
|
|||
float _thumbCenter { |
|||
get { |
|||
float visualPosition = this._value; |
|||
|
|||
return MathUtils.lerpFloat(this._trackLeft + CupertinoThumbPainter.radius, |
|||
this._trackRight - CupertinoThumbPainter.radius, |
|||
visualPosition); |
|||
} |
|||
} |
|||
|
|||
public bool isInteractive { |
|||
get { return this.onChanged != null; } |
|||
} |
|||
|
|||
void _handleDragStart(DragStartDetails details) { |
|||
this._startInteraction(details.globalPosition); |
|||
} |
|||
|
|||
void _handleDragUpdate(DragUpdateDetails details) { |
|||
if (this.isInteractive) { |
|||
float extent = Mathf.Max(SliderUtils._kPadding, |
|||
this.size.width - 2.0f * (SliderUtils._kPadding + CupertinoThumbPainter.radius)); |
|||
float? valueDelta = details.primaryDelta / extent; |
|||
this._currentDragValue += valueDelta ?? 0.0f; |
|||
|
|||
this.onChanged(this._discretizedCurrentDragValue); |
|||
} |
|||
} |
|||
|
|||
void _handleDragEnd(DragEndDetails details) { |
|||
this._endInteraction(); |
|||
} |
|||
|
|||
void _startInteraction(Offset globalPosition) { |
|||
if (this.isInteractive) { |
|||
if (this.onChangeStart != null) { |
|||
this.onChangeStart(this._discretizedCurrentDragValue); |
|||
} |
|||
|
|||
this._currentDragValue = this._value; |
|||
this.onChanged(this._discretizedCurrentDragValue); |
|||
} |
|||
} |
|||
|
|||
void _endInteraction() { |
|||
if (this.onChangeEnd != null) { |
|||
this.onChangeEnd(this._discretizedCurrentDragValue); |
|||
} |
|||
|
|||
this._currentDragValue = 0.0f; |
|||
} |
|||
|
|||
protected override bool hitTestSelf(Offset position) { |
|||
return (position.dx - this._thumbCenter).abs() < CupertinoThumbPainter.radius + SliderUtils._kPadding; |
|||
} |
|||
|
|||
public override void handleEvent(PointerEvent e, HitTestEntry entry) { |
|||
D.assert(this.debugHandleEvent(e, entry)); |
|||
if (e is PointerDownEvent pointerDownEvent && this.isInteractive) { |
|||
this._drag.addPointer(pointerDownEvent); |
|||
} |
|||
} |
|||
|
|||
CupertinoThumbPainter _thumbPainter = new CupertinoThumbPainter(); |
|||
|
|||
public override |
|||
void paint(PaintingContext context, Offset offset) { |
|||
float visualPosition; |
|||
Color leftColor; |
|||
Color rightColor; |
|||
visualPosition = this._position.value; |
|||
leftColor = SliderUtils._kTrackColor; |
|||
rightColor = this._activeColor; |
|||
|
|||
float trackCenter = offset.dy + this.size.height / 2.0f; |
|||
float trackLeft = offset.dx + this._trackLeft; |
|||
float trackTop = trackCenter - 1.0f; |
|||
float trackBottom = trackCenter + 1.0f; |
|||
float trackRight = offset.dx + this._trackRight; |
|||
float trackActive = offset.dx + this._thumbCenter; |
|||
|
|||
Canvas canvas = context.canvas; |
|||
|
|||
if (visualPosition > 0.0f) { |
|||
Paint paint = new Paint(); |
|||
paint.color = rightColor; |
|||
canvas.drawRRect(RRect.fromLTRBXY(trackLeft, trackTop, trackActive, trackBottom, 1.0f, 1.0f), paint); |
|||
} |
|||
|
|||
if (visualPosition < 1.0f) { |
|||
Paint paint = new Paint(); |
|||
paint.color = leftColor; |
|||
canvas.drawRRect(RRect.fromLTRBXY(trackActive, trackTop, trackRight, trackBottom, 1.0f, 1.0f), paint); |
|||
} |
|||
|
|||
Offset thumbCenter = new Offset(trackActive, trackCenter); |
|||
this._thumbPainter.paint(canvas, |
|||
Rect.fromCircle(center: thumbCenter, radius: CupertinoThumbPainter.radius)); |
|||
} |
|||
} |
|||
using System; |
|||
using Unity.UIWidgets.animation; |
|||
using Unity.UIWidgets.foundation; |
|||
using Unity.UIWidgets.gestures; |
|||
using Unity.UIWidgets.rendering; |
|||
using Unity.UIWidgets.scheduler; |
|||
using Unity.UIWidgets.ui; |
|||
using Unity.UIWidgets.widgets; |
|||
using UnityEngine; |
|||
using Canvas = Unity.UIWidgets.ui.Canvas; |
|||
using Color = Unity.UIWidgets.ui.Color; |
|||
using Rect = Unity.UIWidgets.ui.Rect; |
|||
|
|||
namespace Unity.UIWidgets.cupertino { |
|||
class SliderUtils { |
|||
public const float _kPadding = 8.0f; |
|||
public static readonly Color _kTrackColor = new Color(0xFFB5B5B5); |
|||
public const float _kSliderHeight = 2.0f * (CupertinoThumbPainter.radius + _kPadding); |
|||
public const float _kSliderWidth = 176.0f; // Matches Material Design slider.
|
|||
public static readonly TimeSpan _kDiscreteTransitionDuration = new TimeSpan(0, 0, 0, 0, 500); |
|||
public const float _kAdjustmentUnit = 0.1f; // Matches iOS implementation of material slider.
|
|||
} |
|||
|
|||
public class CupertinoSlider : StatefulWidget { |
|||
public CupertinoSlider( |
|||
Key key = null, |
|||
float? value = null, |
|||
ValueChanged<float> onChanged = null, |
|||
ValueChanged<float> onChangeStart = null, |
|||
ValueChanged<float> onChangeEnd = null, |
|||
float min = 0.0f, |
|||
float max = 1.0f, |
|||
int? divisions = null, |
|||
Color activeColor = null |
|||
) : base(key: key) { |
|||
D.assert(value != null); |
|||
D.assert(onChanged != null); |
|||
D.assert(value >= min && value <= max); |
|||
D.assert(divisions == null || divisions > 0); |
|||
this.value = value.Value; |
|||
this.onChanged = onChanged; |
|||
this.onChangeStart = onChangeStart; |
|||
this.onChangeEnd = onChangeEnd; |
|||
this.min = min; |
|||
this.max = max; |
|||
this.divisions = divisions; |
|||
this.activeColor = activeColor; |
|||
} |
|||
|
|||
public readonly float value; |
|||
|
|||
public readonly ValueChanged<float> onChanged; |
|||
|
|||
public readonly ValueChanged<float> onChangeStart; |
|||
|
|||
public readonly ValueChanged<float> onChangeEnd; |
|||
|
|||
public readonly float min; |
|||
|
|||
public readonly float max; |
|||
|
|||
public readonly int? divisions; |
|||
|
|||
public readonly Color activeColor; |
|||
|
|||
public override State createState() { |
|||
return new _CupertinoSliderState(); |
|||
} |
|||
|
|||
public override void debugFillProperties(DiagnosticPropertiesBuilder properties) { |
|||
base.debugFillProperties(properties); |
|||
properties.add(new FloatProperty("value", this.value)); |
|||
properties.add(new FloatProperty("min", this.min)); |
|||
properties.add(new FloatProperty("max", this.max)); |
|||
} |
|||
} |
|||
|
|||
class _CupertinoSliderState : TickerProviderStateMixin<CupertinoSlider> { |
|||
void _handleChanged(float value) { |
|||
D.assert(this.widget.onChanged != null); |
|||
float lerpValue = MathUtils.lerpFloat(this.widget.min, this.widget.max, value); |
|||
if (lerpValue != this.widget.value) { |
|||
this.widget.onChanged(lerpValue); |
|||
} |
|||
} |
|||
|
|||
void _handleDragStart(float value) { |
|||
D.assert(this.widget.onChangeStart != null); |
|||
this.widget.onChangeStart(MathUtils.lerpFloat(this.widget.min, this.widget.max, value)); |
|||
} |
|||
|
|||
void _handleDragEnd(float value) { |
|||
D.assert(this.widget.onChangeEnd != null); |
|||
this.widget.onChangeEnd(MathUtils.lerpFloat(this.widget.min, this.widget.max, value)); |
|||
} |
|||
|
|||
public override Widget build(BuildContext context) { |
|||
return new _CupertinoSliderRenderObjectWidget( |
|||
value: (this.widget.value - this.widget.min) / (this.widget.max - this.widget.min), |
|||
divisions: this.widget.divisions, |
|||
activeColor: this.widget.activeColor ?? CupertinoTheme.of(context).primaryColor, |
|||
onChanged: this.widget.onChanged != null ? (ValueChanged<float>) this._handleChanged : null, |
|||
onChangeStart: this.widget.onChangeStart != null ? (ValueChanged<float>) this._handleDragStart : null, |
|||
onChangeEnd: this.widget.onChangeEnd != null ? (ValueChanged<float>) this._handleDragEnd : null, |
|||
vsync: this |
|||
); |
|||
} |
|||
} |
|||
|
|||
class _CupertinoSliderRenderObjectWidget : LeafRenderObjectWidget { |
|||
public _CupertinoSliderRenderObjectWidget( |
|||
Key key = null, |
|||
float? value = null, |
|||
int? divisions = null, |
|||
Color activeColor = null, |
|||
ValueChanged<float> onChanged = null, |
|||
ValueChanged<float> onChangeStart = null, |
|||
ValueChanged<float> onChangeEnd = null, |
|||
TickerProvider vsync = null |
|||
) : base(key: key) { |
|||
this.value = value; |
|||
this.divisions = divisions; |
|||
this.activeColor = activeColor; |
|||
this.onChanged = onChanged; |
|||
this.onChangeStart = onChangeStart; |
|||
this.onChangeEnd = onChangeEnd; |
|||
this.vsync = vsync; |
|||
} |
|||
|
|||
public readonly float? value; |
|||
public readonly int? divisions; |
|||
public readonly Color activeColor; |
|||
public readonly ValueChanged<float> onChanged; |
|||
public readonly ValueChanged<float> onChangeStart; |
|||
public readonly ValueChanged<float> onChangeEnd; |
|||
public readonly TickerProvider vsync; |
|||
|
|||
public override RenderObject createRenderObject(BuildContext context) { |
|||
return new _RenderCupertinoSlider( |
|||
value: this.value ?? 0.0f, |
|||
divisions: this.divisions, |
|||
activeColor: this.activeColor, |
|||
onChanged: this.onChanged, |
|||
onChangeStart: this.onChangeStart, |
|||
onChangeEnd: this.onChangeEnd, |
|||
vsync: this.vsync |
|||
); |
|||
} |
|||
|
|||
public override void updateRenderObject(BuildContext context, RenderObject _renderObject) { |
|||
_RenderCupertinoSlider renderObject = _renderObject as _RenderCupertinoSlider; |
|||
renderObject.value = this.value ?? 0.0f; |
|||
renderObject.divisions = this.divisions; |
|||
renderObject.activeColor = this.activeColor; |
|||
renderObject.onChanged = this.onChanged; |
|||
renderObject.onChangeStart = this.onChangeStart; |
|||
renderObject.onChangeEnd = this.onChangeEnd; |
|||
} |
|||
} |
|||
|
|||
class _RenderCupertinoSlider : RenderConstrainedBox { |
|||
public _RenderCupertinoSlider( |
|||
float value, |
|||
int? divisions = null, |
|||
Color activeColor = null, |
|||
ValueChanged<float> onChanged = null, |
|||
ValueChanged<float> onChangeStart = null, |
|||
ValueChanged<float> onChangeEnd = null, |
|||
TickerProvider vsync = null |
|||
) : base(additionalConstraints: BoxConstraints.tightFor(width: SliderUtils._kSliderWidth, |
|||
height: SliderUtils._kSliderHeight)) { |
|||
D.assert(value >= 0.0f && value <= 1.0f); |
|||
this._value = value; |
|||
this._divisions = divisions; |
|||
this._activeColor = activeColor; |
|||
this._onChanged = onChanged; |
|||
this.onChangeStart = onChangeStart; |
|||
this.onChangeEnd = onChangeEnd; |
|||
this._drag = new HorizontalDragGestureRecognizer(); |
|||
this._drag.onStart = this._handleDragStart; |
|||
this._drag.onUpdate = this._handleDragUpdate; |
|||
this._drag.onEnd = this._handleDragEnd; |
|||
this._position = new AnimationController( |
|||
value: value, |
|||
duration: SliderUtils._kDiscreteTransitionDuration, |
|||
vsync: vsync |
|||
); |
|||
this._position.addListener(this.markNeedsPaint); |
|||
} |
|||
|
|||
public float value { |
|||
get { return this._value; } |
|||
set { |
|||
D.assert(value >= 0.0f && value <= 1.0f); |
|||
if (value == this._value) { |
|||
return; |
|||
} |
|||
|
|||
this._value = value; |
|||
if (this.divisions != null) { |
|||
this._position.animateTo(value, curve: Curves.fastOutSlowIn); |
|||
} |
|||
else { |
|||
this._position.setValue(value); |
|||
} |
|||
} |
|||
} |
|||
|
|||
float _value; |
|||
|
|||
public int? divisions { |
|||
get { return this._divisions; } |
|||
set { |
|||
if (value == this._divisions) { |
|||
return; |
|||
} |
|||
|
|||
this._divisions = value; |
|||
this.markNeedsPaint(); |
|||
} |
|||
} |
|||
|
|||
int? _divisions; |
|||
|
|||
public Color activeColor { |
|||
get { return this._activeColor; } |
|||
set { |
|||
if (value == this._activeColor) { |
|||
return; |
|||
} |
|||
|
|||
this._activeColor = value; |
|||
this.markNeedsPaint(); |
|||
} |
|||
} |
|||
|
|||
Color _activeColor; |
|||
|
|||
public ValueChanged<float> onChanged { |
|||
get { return this._onChanged; } |
|||
set { |
|||
if (value == this._onChanged) { |
|||
return; |
|||
} |
|||
|
|||
this._onChanged = value; |
|||
} |
|||
} |
|||
|
|||
ValueChanged<float> _onChanged; |
|||
|
|||
public ValueChanged<float> onChangeStart; |
|||
public ValueChanged<float> onChangeEnd; |
|||
|
|||
|
|||
AnimationController _position; |
|||
|
|||
HorizontalDragGestureRecognizer _drag; |
|||
float _currentDragValue = 0.0f; |
|||
|
|||
float _discretizedCurrentDragValue { |
|||
get { |
|||
float dragValue = this._currentDragValue.clamp(0.0f, 1.0f); |
|||
if (this.divisions != null) { |
|||
dragValue = Mathf.Round(dragValue * this.divisions.Value) / this.divisions.Value; |
|||
} |
|||
|
|||
return dragValue; |
|||
} |
|||
} |
|||
|
|||
public float _trackLeft { |
|||
get { return SliderUtils._kPadding; } |
|||
} |
|||
|
|||
public float _trackRight { |
|||
get { return this.size.width - SliderUtils._kPadding; } |
|||
} |
|||
|
|||
float _thumbCenter { |
|||
get { |
|||
float visualPosition = this._value; |
|||
|
|||
return MathUtils.lerpFloat(this._trackLeft + CupertinoThumbPainter.radius, |
|||
this._trackRight - CupertinoThumbPainter.radius, |
|||
visualPosition); |
|||
} |
|||
} |
|||
|
|||
public bool isInteractive { |
|||
get { return this.onChanged != null; } |
|||
} |
|||
|
|||
void _handleDragStart(DragStartDetails details) { |
|||
this._startInteraction(details.globalPosition); |
|||
} |
|||
|
|||
void _handleDragUpdate(DragUpdateDetails details) { |
|||
if (this.isInteractive) { |
|||
float extent = Mathf.Max(SliderUtils._kPadding, |
|||
this.size.width - 2.0f * (SliderUtils._kPadding + CupertinoThumbPainter.radius)); |
|||
float? valueDelta = details.primaryDelta / extent; |
|||
this._currentDragValue += valueDelta ?? 0.0f; |
|||
|
|||
this.onChanged(this._discretizedCurrentDragValue); |
|||
} |
|||
} |
|||
|
|||
void _handleDragEnd(DragEndDetails details) { |
|||
this._endInteraction(); |
|||
} |
|||
|
|||
void _startInteraction(Offset globalPosition) { |
|||
if (this.isInteractive) { |
|||
if (this.onChangeStart != null) { |
|||
this.onChangeStart(this._discretizedCurrentDragValue); |
|||
} |
|||
|
|||
this._currentDragValue = this._value; |
|||
this.onChanged(this._discretizedCurrentDragValue); |
|||
} |
|||
} |
|||
|
|||
void _endInteraction() { |
|||
if (this.onChangeEnd != null) { |
|||
this.onChangeEnd(this._discretizedCurrentDragValue); |
|||
} |
|||
|
|||
this._currentDragValue = 0.0f; |
|||
} |
|||
|
|||
protected override bool hitTestSelf(Offset position) { |
|||
return (position.dx - this._thumbCenter).abs() < CupertinoThumbPainter.radius + SliderUtils._kPadding; |
|||
} |
|||
|
|||
public override void handleEvent(PointerEvent e, HitTestEntry entry) { |
|||
D.assert(this.debugHandleEvent(e, entry)); |
|||
if (e is PointerDownEvent pointerDownEvent && this.isInteractive) { |
|||
this._drag.addPointer(pointerDownEvent); |
|||
} |
|||
} |
|||
|
|||
CupertinoThumbPainter _thumbPainter = new CupertinoThumbPainter(); |
|||
|
|||
public override |
|||
void paint(PaintingContext context, Offset offset) { |
|||
float visualPosition; |
|||
Color leftColor; |
|||
Color rightColor; |
|||
visualPosition = this._position.value; |
|||
leftColor = SliderUtils._kTrackColor; |
|||
rightColor = this._activeColor; |
|||
|
|||
float trackCenter = offset.dy + this.size.height / 2.0f; |
|||
float trackLeft = offset.dx + this._trackLeft; |
|||
float trackTop = trackCenter - 1.0f; |
|||
float trackBottom = trackCenter + 1.0f; |
|||
float trackRight = offset.dx + this._trackRight; |
|||
float trackActive = offset.dx + this._thumbCenter; |
|||
|
|||
Canvas canvas = context.canvas; |
|||
|
|||
if (visualPosition > 0.0f) { |
|||
Paint paint = new Paint(); |
|||
paint.color = rightColor; |
|||
canvas.drawRRect(RRect.fromLTRBXY(trackLeft, trackTop, trackActive, trackBottom, 1.0f, 1.0f), paint); |
|||
} |
|||
|
|||
if (visualPosition < 1.0f) { |
|||
Paint paint = new Paint(); |
|||
paint.color = leftColor; |
|||
canvas.drawRRect(RRect.fromLTRBXY(trackActive, trackTop, trackRight, trackBottom, 1.0f, 1.0f), paint); |
|||
} |
|||
|
|||
Offset thumbCenter = new Offset(trackActive, trackCenter); |
|||
this._thumbPainter.paint(canvas, |
|||
Rect.fromCircle(center: thumbCenter, radius: CupertinoThumbPainter.radius)); |
|||
} |
|||
} |
|||
} |
|
|||
using System.Collections.Generic; |
|||
using System.Linq; |
|||
using Unity.UIWidgets.foundation; |
|||
using Unity.UIWidgets.painting; |
|||
using Unity.UIWidgets.rendering; |
|||
using Unity.UIWidgets.ui; |
|||
using Unity.UIWidgets.widgets; |
|||
|
|||
namespace Unity.UIWidgets.cupertino { |
|||
public class CupertinoTabScaffold : StatefulWidget { |
|||
public CupertinoTabScaffold( |
|||
Key key = null, |
|||
CupertinoTabBar tabBar = null, |
|||
IndexedWidgetBuilder tabBuilder = null, |
|||
Color backgroundColor = null, |
|||
bool resizeToAvoidBottomInset = true |
|||
) : base(key: key) { |
|||
D.assert(tabBar != null); |
|||
D.assert(tabBuilder != null); |
|||
this.tabBar = tabBar; |
|||
this.tabBuilder = tabBuilder; |
|||
this.backgroundColor = backgroundColor; |
|||
this.resizeToAvoidBottomInset = resizeToAvoidBottomInset; |
|||
} |
|||
|
|||
|
|||
public readonly CupertinoTabBar tabBar; |
|||
|
|||
public readonly IndexedWidgetBuilder tabBuilder; |
|||
|
|||
public readonly Color backgroundColor; |
|||
|
|||
public readonly bool resizeToAvoidBottomInset; |
|||
|
|||
public override State createState() { |
|||
return new _CupertinoTabScaffoldState(); |
|||
} |
|||
} |
|||
|
|||
class _CupertinoTabScaffoldState : State<CupertinoTabScaffold> { |
|||
int _currentPage; |
|||
|
|||
public override void initState() { |
|||
base.initState(); |
|||
this._currentPage = this.widget.tabBar.currentIndex; |
|||
|
|||
} |
|||
|
|||
public override void didUpdateWidget(StatefulWidget _oldWidget) { |
|||
CupertinoTabScaffold oldWidget = _oldWidget as CupertinoTabScaffold; |
|||
base.didUpdateWidget(oldWidget); |
|||
if (this._currentPage >= this.widget.tabBar.items.Count) { |
|||
this._currentPage = this.widget.tabBar.items.Count - 1; |
|||
D.assert(this._currentPage >= 0, |
|||
() => "CupertinoTabBar is expected to keep at least 2 tabs after updating" |
|||
); |
|||
} |
|||
|
|||
if (this.widget.tabBar.currentIndex != oldWidget.tabBar.currentIndex) { |
|||
this._currentPage = this.widget.tabBar.currentIndex; |
|||
} |
|||
} |
|||
|
|||
public override Widget build(BuildContext context) { |
|||
List<Widget> stacked = new List<Widget> { }; |
|||
|
|||
MediaQueryData existingMediaQuery = MediaQuery.of(context); |
|||
MediaQueryData newMediaQuery = MediaQuery.of(context); |
|||
|
|||
Widget content = new _TabSwitchingView( |
|||
currentTabIndex: this._currentPage, |
|||
tabNumber: this.widget.tabBar.items.Count, |
|||
tabBuilder: this.widget.tabBuilder |
|||
); |
|||
EdgeInsets contentPadding = EdgeInsets.zero; |
|||
|
|||
if (this.widget.resizeToAvoidBottomInset) { |
|||
newMediaQuery = newMediaQuery.removeViewInsets(removeBottom: true); |
|||
contentPadding = EdgeInsets.only(bottom: existingMediaQuery.viewInsets.bottom); |
|||
} |
|||
|
|||
if (this.widget.tabBar != null && |
|||
(!this.widget.resizeToAvoidBottomInset || |
|||
this.widget.tabBar.preferredSize.height > existingMediaQuery.viewInsets.bottom)) { |
|||
float bottomPadding = this.widget.tabBar.preferredSize.height + existingMediaQuery.padding.bottom; |
|||
|
|||
if (this.widget.tabBar.opaque(context)) { |
|||
contentPadding = EdgeInsets.only(bottom: bottomPadding); |
|||
} |
|||
else { |
|||
newMediaQuery = newMediaQuery.copyWith( |
|||
padding: newMediaQuery.padding.copyWith( |
|||
bottom: bottomPadding |
|||
) |
|||
); |
|||
} |
|||
} |
|||
|
|||
content = new MediaQuery( |
|||
data: newMediaQuery, |
|||
child: new Padding( |
|||
padding: contentPadding, |
|||
child: content |
|||
) |
|||
); |
|||
|
|||
stacked.Add(content); |
|||
|
|||
if (this.widget.tabBar != null) { |
|||
stacked.Add(new Align( |
|||
alignment: Alignment.bottomCenter, |
|||
child: this.widget.tabBar.copyWith( |
|||
currentIndex: this._currentPage, |
|||
onTap: (int newIndex) => { |
|||
this.setState(() => { this._currentPage = newIndex; }); |
|||
if (this.widget.tabBar.onTap != null) { |
|||
this.widget.tabBar.onTap(newIndex); |
|||
} |
|||
} |
|||
) |
|||
)); |
|||
} |
|||
|
|||
return new DecoratedBox( |
|||
decoration: new BoxDecoration( |
|||
color: this.widget.backgroundColor ?? CupertinoTheme.of(context).scaffoldBackgroundColor |
|||
), |
|||
child: new Stack( |
|||
children: stacked |
|||
) |
|||
); |
|||
} |
|||
} |
|||
|
|||
class _TabSwitchingView : StatefulWidget { |
|||
public _TabSwitchingView( |
|||
int currentTabIndex, |
|||
int tabNumber, |
|||
IndexedWidgetBuilder tabBuilder |
|||
) { |
|||
D.assert(tabNumber > 0); |
|||
D.assert(tabBuilder != null); |
|||
this.currentTabIndex = currentTabIndex; |
|||
this.tabNumber = tabNumber; |
|||
this.tabBuilder = tabBuilder; |
|||
} |
|||
|
|||
public readonly int currentTabIndex; |
|||
public readonly int tabNumber; |
|||
public readonly IndexedWidgetBuilder tabBuilder; |
|||
|
|||
public override State createState() { |
|||
return new _TabSwitchingViewState(); |
|||
} |
|||
} |
|||
|
|||
class _TabSwitchingViewState : State<_TabSwitchingView> { |
|||
List<Widget> tabs; |
|||
List<FocusScopeNode> tabFocusNodes; |
|||
|
|||
public override void initState() { |
|||
base.initState(); |
|||
this.tabs = new List<Widget>(this.widget.tabNumber); |
|||
for (int i = 0; i < this.widget.tabNumber; i++) { |
|||
this.tabs.Add(null); |
|||
} |
|||
this.tabFocusNodes = Enumerable.Repeat(new FocusScopeNode(), this.widget.tabNumber).ToList(); |
|||
} |
|||
|
|||
public override void didChangeDependencies() { |
|||
base.didChangeDependencies(); |
|||
this._focusActiveTab(); |
|||
} |
|||
|
|||
public override void didUpdateWidget(StatefulWidget _oldWidget) { |
|||
_TabSwitchingView oldWidget = _oldWidget as _TabSwitchingView; |
|||
base.didUpdateWidget(oldWidget); |
|||
this._focusActiveTab(); |
|||
} |
|||
|
|||
void _focusActiveTab() { |
|||
FocusScope.of(this.context).setFirstFocus(this.tabFocusNodes[this.widget.currentTabIndex]); |
|||
} |
|||
|
|||
public override void dispose() { |
|||
foreach (FocusScopeNode focusScopeNode in this.tabFocusNodes) { |
|||
focusScopeNode.detach(); |
|||
} |
|||
|
|||
base.dispose(); |
|||
} |
|||
|
|||
public override Widget build(BuildContext context) { |
|||
List<Widget> children = new List<Widget>(); |
|||
for (int index = 0; index < this.widget.tabNumber; index++) { |
|||
bool active = index == this.widget.currentTabIndex; |
|||
|
|||
var tabIndex = index; |
|||
if (active || this.tabs[index] != null) { |
|||
this.tabs[index] = this.widget.tabBuilder(context, tabIndex); |
|||
} |
|||
|
|||
children.Add(new Offstage( |
|||
offstage: !active, |
|||
child: new TickerMode( |
|||
enabled: active, |
|||
child: new FocusScope( |
|||
node: this.tabFocusNodes[index], |
|||
child: this.tabs[index] ?? new Container() |
|||
) |
|||
) |
|||
)); |
|||
} |
|||
|
|||
return new Stack( |
|||
fit: StackFit.expand, |
|||
children: children |
|||
); |
|||
} |
|||
} |
|||
using System.Collections.Generic; |
|||
using System.Linq; |
|||
using Unity.UIWidgets.foundation; |
|||
using Unity.UIWidgets.painting; |
|||
using Unity.UIWidgets.rendering; |
|||
using Unity.UIWidgets.ui; |
|||
using Unity.UIWidgets.widgets; |
|||
|
|||
namespace Unity.UIWidgets.cupertino { |
|||
public class CupertinoTabScaffold : StatefulWidget { |
|||
public CupertinoTabScaffold( |
|||
Key key = null, |
|||
CupertinoTabBar tabBar = null, |
|||
IndexedWidgetBuilder tabBuilder = null, |
|||
Color backgroundColor = null, |
|||
bool resizeToAvoidBottomInset = true |
|||
) : base(key: key) { |
|||
D.assert(tabBar != null); |
|||
D.assert(tabBuilder != null); |
|||
this.tabBar = tabBar; |
|||
this.tabBuilder = tabBuilder; |
|||
this.backgroundColor = backgroundColor; |
|||
this.resizeToAvoidBottomInset = resizeToAvoidBottomInset; |
|||
} |
|||
|
|||
|
|||
public readonly CupertinoTabBar tabBar; |
|||
|
|||
public readonly IndexedWidgetBuilder tabBuilder; |
|||
|
|||
public readonly Color backgroundColor; |
|||
|
|||
public readonly bool resizeToAvoidBottomInset; |
|||
|
|||
public override State createState() { |
|||
return new _CupertinoTabScaffoldState(); |
|||
} |
|||
} |
|||
|
|||
class _CupertinoTabScaffoldState : State<CupertinoTabScaffold> { |
|||
int _currentPage; |
|||
|
|||
public override void initState() { |
|||
base.initState(); |
|||
this._currentPage = this.widget.tabBar.currentIndex; |
|||
|
|||
} |
|||
|
|||
public override void didUpdateWidget(StatefulWidget _oldWidget) { |
|||
CupertinoTabScaffold oldWidget = _oldWidget as CupertinoTabScaffold; |
|||
base.didUpdateWidget(oldWidget); |
|||
if (this._currentPage >= this.widget.tabBar.items.Count) { |
|||
this._currentPage = this.widget.tabBar.items.Count - 1; |
|||
D.assert(this._currentPage >= 0, |
|||
() => "CupertinoTabBar is expected to keep at least 2 tabs after updating" |
|||
); |
|||
} |
|||
|
|||
if (this.widget.tabBar.currentIndex != oldWidget.tabBar.currentIndex) { |
|||
this._currentPage = this.widget.tabBar.currentIndex; |
|||
} |
|||
} |
|||
|
|||
public override Widget build(BuildContext context) { |
|||
List<Widget> stacked = new List<Widget> { }; |
|||
|
|||
MediaQueryData existingMediaQuery = MediaQuery.of(context); |
|||
MediaQueryData newMediaQuery = MediaQuery.of(context); |
|||
|
|||
Widget content = new _TabSwitchingView( |
|||
currentTabIndex: this._currentPage, |
|||
tabNumber: this.widget.tabBar.items.Count, |
|||
tabBuilder: this.widget.tabBuilder |
|||
); |
|||
EdgeInsets contentPadding = EdgeInsets.zero; |
|||
|
|||
if (this.widget.resizeToAvoidBottomInset) { |
|||
newMediaQuery = newMediaQuery.removeViewInsets(removeBottom: true); |
|||
contentPadding = EdgeInsets.only(bottom: existingMediaQuery.viewInsets.bottom); |
|||
} |
|||
|
|||
if (this.widget.tabBar != null && |
|||
(!this.widget.resizeToAvoidBottomInset || |
|||
this.widget.tabBar.preferredSize.height > existingMediaQuery.viewInsets.bottom)) { |
|||
float bottomPadding = this.widget.tabBar.preferredSize.height + existingMediaQuery.padding.bottom; |
|||
|
|||
if (this.widget.tabBar.opaque(context)) { |
|||
contentPadding = EdgeInsets.only(bottom: bottomPadding); |
|||
} |
|||
else { |
|||
newMediaQuery = newMediaQuery.copyWith( |
|||
padding: newMediaQuery.padding.copyWith( |
|||
bottom: bottomPadding |
|||
) |
|||
); |
|||
} |
|||
} |
|||
|
|||
content = new MediaQuery( |
|||
data: newMediaQuery, |
|||
child: new Padding( |
|||
padding: contentPadding, |
|||
child: content |
|||
) |
|||
); |
|||
|
|||
stacked.Add(content); |
|||
|
|||
if (this.widget.tabBar != null) { |
|||
stacked.Add(new Align( |
|||
alignment: Alignment.bottomCenter, |
|||
child: this.widget.tabBar.copyWith( |
|||
currentIndex: this._currentPage, |
|||
onTap: (int newIndex) => { |
|||
this.setState(() => { this._currentPage = newIndex; }); |
|||
if (this.widget.tabBar.onTap != null) { |
|||
this.widget.tabBar.onTap(newIndex); |
|||
} |
|||
} |
|||
) |
|||
)); |
|||
} |
|||
|
|||
return new DecoratedBox( |
|||
decoration: new BoxDecoration( |
|||
color: this.widget.backgroundColor ?? CupertinoTheme.of(context).scaffoldBackgroundColor |
|||
), |
|||
child: new Stack( |
|||
children: stacked |
|||
) |
|||
); |
|||
} |
|||
} |
|||
|
|||
class _TabSwitchingView : StatefulWidget { |
|||
public _TabSwitchingView( |
|||
int currentTabIndex, |
|||
int tabNumber, |
|||
IndexedWidgetBuilder tabBuilder |
|||
) { |
|||
D.assert(tabNumber > 0); |
|||
D.assert(tabBuilder != null); |
|||
this.currentTabIndex = currentTabIndex; |
|||
this.tabNumber = tabNumber; |
|||
this.tabBuilder = tabBuilder; |
|||
} |
|||
|
|||
public readonly int currentTabIndex; |
|||
public readonly int tabNumber; |
|||
public readonly IndexedWidgetBuilder tabBuilder; |
|||
|
|||
public override State createState() { |
|||
return new _TabSwitchingViewState(); |
|||
} |
|||
} |
|||
|
|||
class _TabSwitchingViewState : State<_TabSwitchingView> { |
|||
List<Widget> tabs; |
|||
List<FocusScopeNode> tabFocusNodes; |
|||
|
|||
public override void initState() { |
|||
base.initState(); |
|||
this.tabs = new List<Widget>(this.widget.tabNumber); |
|||
for (int i = 0; i < this.widget.tabNumber; i++) { |
|||
this.tabs.Add(null); |
|||
} |
|||
this.tabFocusNodes = Enumerable.Repeat(new FocusScopeNode(), this.widget.tabNumber).ToList(); |
|||
} |
|||
|
|||
public override void didChangeDependencies() { |
|||
base.didChangeDependencies(); |
|||
this._focusActiveTab(); |
|||
} |
|||
|
|||
public override void didUpdateWidget(StatefulWidget _oldWidget) { |
|||
_TabSwitchingView oldWidget = _oldWidget as _TabSwitchingView; |
|||
base.didUpdateWidget(oldWidget); |
|||
this._focusActiveTab(); |
|||
} |
|||
|
|||
void _focusActiveTab() { |
|||
FocusScope.of(this.context).setFirstFocus(this.tabFocusNodes[this.widget.currentTabIndex]); |
|||
} |
|||
|
|||
public override void dispose() { |
|||
foreach (FocusScopeNode focusScopeNode in this.tabFocusNodes) { |
|||
focusScopeNode.detach(); |
|||
} |
|||
|
|||
base.dispose(); |
|||
} |
|||
|
|||
public override Widget build(BuildContext context) { |
|||
List<Widget> children = new List<Widget>(); |
|||
for (int index = 0; index < this.widget.tabNumber; index++) { |
|||
bool active = index == this.widget.currentTabIndex; |
|||
|
|||
var tabIndex = index; |
|||
if (active || this.tabs[index] != null) { |
|||
this.tabs[index] = this.widget.tabBuilder(context, tabIndex); |
|||
} |
|||
|
|||
children.Add(new Offstage( |
|||
offstage: !active, |
|||
child: new TickerMode( |
|||
enabled: active, |
|||
child: new FocusScope( |
|||
node: this.tabFocusNodes[index], |
|||
child: this.tabs[index] ?? new Container() |
|||
) |
|||
) |
|||
)); |
|||
} |
|||
|
|||
return new Stack( |
|||
fit: StackFit.expand, |
|||
children: children |
|||
); |
|||
} |
|||
} |
|||
} |
|
|||
#include "UIWidgetsTextInputPlugin.h" |
|||
#include "UIWidgetsMessageManager.h" |
|||
#include <Foundation/Foundation.h> |
|||
#include <UIKit/UIKit.h> |
|||
|
|||
static const char _kTextAffinityDownstream[] = "TextAffinity.downstream"; |
|||
static const char _kTextAffinityUpstream[] = "TextAffinity.upstream"; |
|||
|
|||
static UIKeyboardType ToUIKeyboardType(NSDictionary* type) { |
|||
NSString* inputType = type[@"name"]; |
|||
if ([inputType isEqualToString:@"TextInputType.text"]) |
|||
return UIKeyboardTypeDefault; |
|||
if ([inputType isEqualToString:@"TextInputType.multiline"]) |
|||
return UIKeyboardTypeDefault; |
|||
if ([inputType isEqualToString:@"TextInputType.number"]) { |
|||
if ([type[@"signed"] boolValue]) |
|||
return UIKeyboardTypeNumbersAndPunctuation; |
|||
return UIKeyboardTypeDecimalPad; |
|||
} |
|||
if ([inputType isEqualToString:@"TextInputType.phone"]) |
|||
return UIKeyboardTypePhonePad; |
|||
if ([inputType isEqualToString:@"TextInputType.emailAddress"]) |
|||
return UIKeyboardTypeEmailAddress; |
|||
if ([inputType isEqualToString:@"TextInputType.url"]) |
|||
return UIKeyboardTypeURL; |
|||
return UIKeyboardTypeDefault; |
|||
} |
|||
|
|||
static UITextAutocapitalizationType ToUITextAutoCapitalizationType(NSDictionary* type) { |
|||
NSString* textCapitalization = type[@"textCapitalization"]; |
|||
if ([textCapitalization isEqualToString:@"TextCapitalization.characters"]) { |
|||
return UITextAutocapitalizationTypeAllCharacters; |
|||
} else if ([textCapitalization isEqualToString:@"TextCapitalization.sentences"]) { |
|||
return UITextAutocapitalizationTypeSentences; |
|||
} else if ([textCapitalization isEqualToString:@"TextCapitalization.words"]) { |
|||
return UITextAutocapitalizationTypeWords; |
|||
} |
|||
return UITextAutocapitalizationTypeNone; |
|||
} |
|||
|
|||
static UIReturnKeyType ToUIReturnKeyType(NSString* inputType) { |
|||
// Where did the term "unspecified" come from? iOS has a "default" and Android |
|||
// has "unspecified." These 2 terms seem to mean the same thing but we need |
|||
// to pick just one. "unspecified" was chosen because "default" is often a |
|||
// reserved word in languages with switch statements (dart, java, etc). |
|||
if ([inputType isEqualToString:@"TextInputAction.unspecified"]) |
|||
return UIReturnKeyDefault; |
|||
|
|||
if ([inputType isEqualToString:@"TextInputAction.done"]) |
|||
return UIReturnKeyDone; |
|||
|
|||
if ([inputType isEqualToString:@"TextInputAction.go"]) |
|||
return UIReturnKeyGo; |
|||
|
|||
if ([inputType isEqualToString:@"TextInputAction.send"]) |
|||
return UIReturnKeySend; |
|||
|
|||
if ([inputType isEqualToString:@"TextInputAction.search"]) |
|||
return UIReturnKeySearch; |
|||
|
|||
if ([inputType isEqualToString:@"TextInputAction.next"]) |
|||
return UIReturnKeyNext; |
|||
|
|||
if (@available(iOS 9.0, *)) |
|||
if ([inputType isEqualToString:@"TextInputAction.continueAction"]) |
|||
return UIReturnKeyContinue; |
|||
|
|||
if ([inputType isEqualToString:@"TextInputAction.join"]) |
|||
return UIReturnKeyJoin; |
|||
|
|||
if ([inputType isEqualToString:@"TextInputAction.route"]) |
|||
return UIReturnKeyRoute; |
|||
|
|||
if ([inputType isEqualToString:@"TextInputAction.emergencyCall"]) |
|||
return UIReturnKeyEmergencyCall; |
|||
|
|||
if ([inputType isEqualToString:@"TextInputAction.newline"]) |
|||
return UIReturnKeyDefault; |
|||
|
|||
// Present default key if bad input type is given. |
|||
return UIReturnKeyDefault; |
|||
} |
|||
|
|||
#pragma mark - UIWidgetsTextPosition |
|||
|
|||
@implementation UIWidgetsTextPosition |
|||
|
|||
+ (instancetype)positionWithIndex:(NSUInteger)index { |
|||
return [[[UIWidgetsTextPosition alloc] initWithIndex:index] autorelease]; |
|||
} |
|||
|
|||
- (instancetype)initWithIndex:(NSUInteger)index { |
|||
self = [super init]; |
|||
if (self) { |
|||
_index = index; |
|||
} |
|||
return self; |
|||
} |
|||
|
|||
@end |
|||
|
|||
#pragma mark - UIWidgetsTextRange |
|||
|
|||
@implementation UIWidgetsTextRange |
|||
|
|||
+ (instancetype)rangeWithNSRange:(NSRange)range { |
|||
return [[[UIWidgetsTextRange alloc] initWithNSRange:range] autorelease]; |
|||
} |
|||
|
|||
- (instancetype)initWithNSRange:(NSRange)range { |
|||
self = [super init]; |
|||
if (self) { |
|||
_range = range; |
|||
} |
|||
return self; |
|||
} |
|||
|
|||
- (UITextPosition*)start { |
|||
return [UIWidgetsTextPosition positionWithIndex:self.range.location]; |
|||
} |
|||
|
|||
- (UITextPosition*)end { |
|||
return [UIWidgetsTextPosition positionWithIndex:self.range.location + self.range.length]; |
|||
} |
|||
|
|||
- (BOOL)isEmpty { |
|||
return self.range.length == 0; |
|||
} |
|||
|
|||
- (id)copyWithZone:(NSZone*)zone { |
|||
return [[UIWidgetsTextRange allocWithZone:zone] initWithNSRange:self.range]; |
|||
} |
|||
|
|||
@end |
|||
|
|||
@interface UIWidgetsTextInputView : UIView <UITextInput> |
|||
|
|||
// UITextInput |
|||
@property(nonatomic, readonly) NSMutableString* text; |
|||
@property(nonatomic, readonly) NSMutableString* markedText; |
|||
@property(readwrite, copy) UITextRange* selectedTextRange; |
|||
@property(nonatomic, strong) UITextRange* markedTextRange; |
|||
@property(nonatomic, copy) NSDictionary* markedTextStyle; |
|||
@property(nonatomic, assign) id<UITextInputDelegate> inputDelegate; |
|||
|
|||
// UITextInputTraits |
|||
@property(nonatomic) UITextAutocapitalizationType autocapitalizationType; |
|||
@property(nonatomic) UITextAutocorrectionType autocorrectionType; |
|||
@property(nonatomic) UITextSpellCheckingType spellCheckingType; |
|||
@property(nonatomic) BOOL enablesReturnKeyAutomatically; |
|||
@property(nonatomic) UIKeyboardAppearance keyboardAppearance; |
|||
@property(nonatomic) UIKeyboardType keyboardType; |
|||
@property(nonatomic) UIReturnKeyType returnKeyType; |
|||
@property(nonatomic, getter=isSecureTextEntry) BOOL secureTextEntry; |
|||
|
|||
@property(nonatomic, assign) id<UIWidgetsTextInputDelegate> textInputDelegate; |
|||
|
|||
@end |
|||
|
|||
@implementation UIWidgetsTextInputView { |
|||
int _textInputClient; |
|||
const char* _selectionAffinity; |
|||
UIWidgetsTextRange* _selectedTextRange; |
|||
} |
|||
|
|||
@synthesize tokenizer = _tokenizer; |
|||
|
|||
- (instancetype)init { |
|||
self = [super init]; |
|||
|
|||
if (self) { |
|||
_textInputClient = 0; |
|||
_selectionAffinity = _kTextAffinityUpstream; |
|||
|
|||
// UITextInput |
|||
_text = [[NSMutableString alloc] init]; |
|||
_markedText = [[NSMutableString alloc] init]; |
|||
_selectedTextRange = [[UIWidgetsTextRange alloc] initWithNSRange:NSMakeRange(0, 0)]; |
|||
|
|||
// UITextInputTraits |
|||
_autocapitalizationType = UITextAutocapitalizationTypeSentences; |
|||
_autocorrectionType = UITextAutocorrectionTypeDefault; |
|||
_spellCheckingType = UITextSpellCheckingTypeDefault; |
|||
_enablesReturnKeyAutomatically = NO; |
|||
_keyboardAppearance = UIKeyboardAppearanceDefault; |
|||
_keyboardType = UIKeyboardTypeDefault; |
|||
_returnKeyType = UIReturnKeyDone; |
|||
_secureTextEntry = NO; |
|||
} |
|||
|
|||
return self; |
|||
} |
|||
|
|||
- (void)dealloc { |
|||
[_text release]; |
|||
[_markedText release]; |
|||
[_markedTextRange release]; |
|||
[_selectedTextRange release]; |
|||
[_tokenizer release]; |
|||
[super dealloc]; |
|||
} |
|||
|
|||
- (void)setTextInputClient:(int)client { |
|||
_textInputClient = client; |
|||
} |
|||
|
|||
- (void)setTextInputState:(NSDictionary*)state { |
|||
NSString* newText = state[@"text"]; |
|||
BOOL textChanged = ![self.text isEqualToString:newText]; |
|||
if (textChanged) { |
|||
[self.inputDelegate textWillChange:self]; |
|||
[self.text setString:newText]; |
|||
} |
|||
|
|||
NSInteger composingBase = [state[@"composingBase"] intValue]; |
|||
NSInteger composingExtent = [state[@"composingExtent"] intValue]; |
|||
NSRange composingRange = [self clampSelection:NSMakeRange(MIN(composingBase, composingExtent), ABS(composingBase - composingExtent)) forText:self.text]; |
|||
self.markedTextRange = composingRange.length > 0 ? [UIWidgetsTextRange rangeWithNSRange:composingRange] : nil; |
|||
|
|||
NSInteger selectionBase = [state[@"selectionBase"] intValue]; |
|||
NSInteger selectionExtent = [state[@"selectionExtent"] intValue]; |
|||
NSRange selectedRange = [self clampSelection:NSMakeRange(MIN(selectionBase, selectionExtent), |
|||
ABS(selectionBase - selectionExtent)) |
|||
forText:self.text]; |
|||
NSRange oldSelectedRange = [(UIWidgetsTextRange*)self.selectedTextRange range]; |
|||
if (selectedRange.location != oldSelectedRange.location || |
|||
selectedRange.length != oldSelectedRange.length) { |
|||
[self.inputDelegate selectionWillChange:self]; |
|||
[self setSelectedTextRange:[UIWidgetsTextRange rangeWithNSRange:selectedRange] |
|||
updateEditingState:NO]; |
|||
_selectionAffinity = _kTextAffinityDownstream; |
|||
if ([state[@"selectionAffinity"] isEqualToString:@(_kTextAffinityUpstream)]) |
|||
_selectionAffinity = _kTextAffinityUpstream; |
|||
[self.inputDelegate selectionDidChange:self]; |
|||
} |
|||
|
|||
if (textChanged) { |
|||
[self.inputDelegate textDidChange:self]; |
|||
|
|||
// For consistency with Android behavior, send an update to the framework. |
|||
[self updateEditingState]; |
|||
} |
|||
} |
|||
|
|||
- (NSRange)clampSelection:(NSRange)range forText:(NSString*)text { |
|||
int start = MIN(MAX(range.location, 0), text.length); |
|||
int length = MIN(range.length, text.length - start); |
|||
return NSMakeRange(start, length); |
|||
} |
|||
|
|||
#pragma mark - UIResponder Overrides |
|||
|
|||
- (BOOL)canBecomeFirstResponder { |
|||
return YES; |
|||
} |
|||
|
|||
#pragma mark - UITextInput Overrides |
|||
|
|||
- (id<UITextInputTokenizer>)tokenizer { |
|||
if (_tokenizer == nil) { |
|||
_tokenizer = [[UITextInputStringTokenizer alloc] initWithTextInput:self]; |
|||
} |
|||
return _tokenizer; |
|||
} |
|||
|
|||
- (UITextRange*)selectedTextRange { |
|||
return [[_selectedTextRange copy] autorelease]; |
|||
} |
|||
|
|||
- (void)setSelectedTextRange:(UITextRange*)selectedTextRange { |
|||
[self setSelectedTextRange:selectedTextRange updateEditingState:YES]; |
|||
} |
|||
|
|||
- (void)setSelectedTextRange:(UITextRange*)selectedTextRange updateEditingState:(BOOL)update { |
|||
if (_selectedTextRange != selectedTextRange) { |
|||
UITextRange* oldSelectedRange = _selectedTextRange; |
|||
_selectedTextRange = [selectedTextRange copy]; |
|||
[oldSelectedRange release]; |
|||
|
|||
if (update) |
|||
[self updateEditingState]; |
|||
} |
|||
} |
|||
|
|||
- (id)insertDictationResultPlaceholder { |
|||
return @""; |
|||
} |
|||
|
|||
- (void)removeDictationResultPlaceholder:(id)placeholder willInsertResult:(BOOL)willInsertResult { |
|||
} |
|||
|
|||
- (NSString*)textInRange:(UITextRange*)range { |
|||
NSRange textRange = ((UIWidgetsTextRange*)range).range; |
|||
return [self.text substringWithRange:textRange]; |
|||
} |
|||
|
|||
- (void)replaceRange:(UITextRange*)range withText:(NSString*)text { |
|||
NSRange replaceRange = ((UIWidgetsTextRange*)range).range; |
|||
NSRange selectedRange = _selectedTextRange.range; |
|||
// Adjust the text selection: |
|||
// * reduce the length by the intersection length |
|||
// * adjust the location by newLength - oldLength + intersectionLength |
|||
NSRange intersectionRange = NSIntersectionRange(replaceRange, selectedRange); |
|||
if (replaceRange.location <= selectedRange.location) |
|||
selectedRange.location += text.length - replaceRange.length; |
|||
if (intersectionRange.location != NSNotFound) { |
|||
selectedRange.location += intersectionRange.length; |
|||
selectedRange.length -= intersectionRange.length; |
|||
} |
|||
|
|||
[self.text replaceCharactersInRange:[self clampSelection:replaceRange forText:self.text] |
|||
withString:text]; |
|||
[self setSelectedTextRange:[UIWidgetsTextRange rangeWithNSRange:[self clampSelection:selectedRange |
|||
forText:self.text]] |
|||
updateEditingState:NO]; |
|||
|
|||
[self updateEditingState]; |
|||
} |
|||
|
|||
- (BOOL)shouldChangeTextInRange:(UITextRange*)range replacementText:(NSString*)text { |
|||
if (self.returnKeyType == UIReturnKeyDefault && [text isEqualToString:@"\n"]) { |
|||
[_textInputDelegate performAction:UIWidgetsTextInputActionNewline withClient:_textInputClient]; |
|||
return YES; |
|||
} |
|||
|
|||
if ([text isEqualToString:@"\n"]) { |
|||
UIWidgetsTextInputAction action; |
|||
switch (self.returnKeyType) { |
|||
case UIReturnKeyDefault: |
|||
action = UIWidgetsTextInputActionUnspecified; |
|||
break; |
|||
case UIReturnKeyDone: |
|||
action = UIWidgetsTextInputActionDone; |
|||
break; |
|||
case UIReturnKeyGo: |
|||
action = UIWidgetsTextInputActionGo; |
|||
break; |
|||
case UIReturnKeySend: |
|||
action = UIWidgetsTextInputActionSend; |
|||
break; |
|||
case UIReturnKeySearch: |
|||
case UIReturnKeyGoogle: |
|||
case UIReturnKeyYahoo: |
|||
action = UIWidgetsTextInputActionSearch; |
|||
break; |
|||
case UIReturnKeyNext: |
|||
action = UIWidgetsTextInputActionNext; |
|||
break; |
|||
case UIReturnKeyContinue: |
|||
action = UIWidgetsTextInputActionContinue; |
|||
break; |
|||
case UIReturnKeyJoin: |
|||
action = UIWidgetsTextInputActionJoin; |
|||
break; |
|||
case UIReturnKeyRoute: |
|||
action = UIWidgetsTextInputActionRoute; |
|||
break; |
|||
case UIReturnKeyEmergencyCall: |
|||
action = UIWidgetsTextInputActionEmergencyCall; |
|||
break; |
|||
} |
|||
|
|||
[_textInputDelegate performAction:action withClient:_textInputClient]; |
|||
return NO; |
|||
} |
|||
|
|||
return YES; |
|||
} |
|||
|
|||
- (void)setMarkedText:(NSString*)markedText selectedRange:(NSRange)markedSelectedRange { |
|||
NSRange selectedRange = _selectedTextRange.range; |
|||
NSRange markedTextRange = ((UIWidgetsTextRange*)self.markedTextRange).range; |
|||
|
|||
if (markedText == nil) |
|||
markedText = @""; |
|||
|
|||
if (markedTextRange.length > 0) { |
|||
// Replace text in the marked range with the new text. |
|||
[self replaceRange:self.markedTextRange withText:markedText]; |
|||
markedTextRange.length = markedText.length; |
|||
} else { |
|||
// Replace text in the selected range with the new text. |
|||
[self replaceRange:_selectedTextRange withText:markedText]; |
|||
markedTextRange = NSMakeRange(selectedRange.location, markedText.length); |
|||
} |
|||
|
|||
self.markedTextRange = |
|||
markedTextRange.length > 0 ? [UIWidgetsTextRange rangeWithNSRange:markedTextRange] : nil; |
|||
|
|||
NSUInteger selectionLocation = markedSelectedRange.location + markedTextRange.location; |
|||
selectedRange = NSMakeRange(selectionLocation, markedSelectedRange.length); |
|||
[self setSelectedTextRange:[UIWidgetsTextRange rangeWithNSRange:[self clampSelection:selectedRange |
|||
forText:self.text]] |
|||
updateEditingState:YES]; |
|||
} |
|||
|
|||
- (void)unmarkText { |
|||
self.markedTextRange = nil; |
|||
[self updateEditingState]; |
|||
} |
|||
|
|||
- (UITextRange*)textRangeFromPosition:(UITextPosition*)fromPosition |
|||
toPosition:(UITextPosition*)toPosition { |
|||
NSUInteger fromIndex = ((UIWidgetsTextPosition*)fromPosition).index; |
|||
NSUInteger toIndex = ((UIWidgetsTextPosition*)toPosition).index; |
|||
return [UIWidgetsTextRange rangeWithNSRange:NSMakeRange(fromIndex, toIndex - fromIndex)]; |
|||
} |
|||
|
|||
/** Returns the range of the character sequence at the specified index in the |
|||
* text. */ |
|||
- (NSRange)rangeForCharacterAtIndex:(NSUInteger)index { |
|||
if (index < self.text.length) |
|||
return [self.text rangeOfComposedCharacterSequenceAtIndex:index]; |
|||
return NSMakeRange(index, 0); |
|||
} |
|||
|
|||
- (NSUInteger)decrementOffsetPosition:(NSUInteger)position { |
|||
return [self rangeForCharacterAtIndex:MAX(0, position - 1)].location; |
|||
} |
|||
|
|||
- (NSUInteger)incrementOffsetPosition:(NSUInteger)position { |
|||
NSRange charRange = [self rangeForCharacterAtIndex:position]; |
|||
return MIN(position + charRange.length, self.text.length); |
|||
} |
|||
|
|||
- (UITextPosition*)positionFromPosition:(UITextPosition*)position offset:(NSInteger)offset { |
|||
NSUInteger offsetPosition = ((UIWidgetsTextPosition*)position).index; |
|||
NSInteger newLocation = (NSInteger)offsetPosition + offset; |
|||
if (newLocation < 0 || newLocation > (NSInteger)self.text.length) { |
|||
return nil; |
|||
} |
|||
if (offset >= 0) { |
|||
for (NSInteger i = 0; i < offset && offsetPosition < self.text.length; ++i) |
|||
offsetPosition = [self incrementOffsetPosition:offsetPosition]; |
|||
} else { |
|||
for (NSInteger i = 0; i < ABS(offset) && offsetPosition > 0; ++i) |
|||
offsetPosition = [self decrementOffsetPosition:offsetPosition]; |
|||
} |
|||
return [UIWidgetsTextPosition positionWithIndex:offsetPosition]; |
|||
} |
|||
|
|||
- (UITextPosition*)positionFromPosition:(UITextPosition*)position |
|||
inDirection:(UITextLayoutDirection)direction |
|||
offset:(NSInteger)offset { |
|||
// TODO(cbracken) Add RTL handling. |
|||
switch (direction) { |
|||
case UITextLayoutDirectionLeft: |
|||
case UITextLayoutDirectionUp: |
|||
return [self positionFromPosition:position offset:offset * -1]; |
|||
case UITextLayoutDirectionRight: |
|||
case UITextLayoutDirectionDown: |
|||
return [self positionFromPosition:position offset:1]; |
|||
} |
|||
} |
|||
|
|||
- (UITextPosition*)beginningOfDocument { |
|||
return [UIWidgetsTextPosition positionWithIndex:0]; |
|||
} |
|||
|
|||
- (UITextPosition*)endOfDocument { |
|||
return [UIWidgetsTextPosition positionWithIndex:self.text.length]; |
|||
} |
|||
|
|||
- (NSComparisonResult)comparePosition:(UITextPosition*)position toPosition:(UITextPosition*)other { |
|||
NSUInteger positionIndex = ((UIWidgetsTextPosition*)position).index; |
|||
NSUInteger otherIndex = ((UIWidgetsTextPosition*)other).index; |
|||
if (positionIndex < otherIndex) |
|||
return NSOrderedAscending; |
|||
if (positionIndex > otherIndex) |
|||
return NSOrderedDescending; |
|||
return NSOrderedSame; |
|||
} |
|||
|
|||
- (NSInteger)offsetFromPosition:(UITextPosition*)from toPosition:(UITextPosition*)toPosition { |
|||
return ((UIWidgetsTextPosition*)toPosition).index - ((UIWidgetsTextPosition*)from).index; |
|||
} |
|||
|
|||
- (UITextPosition*)positionWithinRange:(UITextRange*)range |
|||
farthestInDirection:(UITextLayoutDirection)direction { |
|||
NSUInteger index; |
|||
switch (direction) { |
|||
case UITextLayoutDirectionLeft: |
|||
case UITextLayoutDirectionUp: |
|||
index = ((UIWidgetsTextPosition*)range.start).index; |
|||
break; |
|||
case UITextLayoutDirectionRight: |
|||
case UITextLayoutDirectionDown: |
|||
index = ((UIWidgetsTextPosition*)range.end).index; |
|||
break; |
|||
} |
|||
return [UIWidgetsTextPosition positionWithIndex:index]; |
|||
} |
|||
|
|||
- (UITextRange*)characterRangeByExtendingPosition:(UITextPosition*)position |
|||
inDirection:(UITextLayoutDirection)direction { |
|||
NSUInteger positionIndex = ((UIWidgetsTextPosition*)position).index; |
|||
NSUInteger startIndex; |
|||
NSUInteger endIndex; |
|||
switch (direction) { |
|||
case UITextLayoutDirectionLeft: |
|||
case UITextLayoutDirectionUp: |
|||
startIndex = [self decrementOffsetPosition:positionIndex]; |
|||
endIndex = positionIndex; |
|||
break; |
|||
case UITextLayoutDirectionRight: |
|||
case UITextLayoutDirectionDown: |
|||
startIndex = positionIndex; |
|||
endIndex = [self incrementOffsetPosition:positionIndex]; |
|||
break; |
|||
} |
|||
return [UIWidgetsTextRange rangeWithNSRange:NSMakeRange(startIndex, endIndex - startIndex)]; |
|||
} |
|||
|
|||
#pragma mark - UITextInput text direction handling |
|||
|
|||
- (UITextWritingDirection)baseWritingDirectionForPosition:(UITextPosition*)position |
|||
inDirection:(UITextStorageDirection)direction { |
|||
return UITextWritingDirectionNatural; |
|||
} |
|||
|
|||
- (void)setBaseWritingDirection:(UITextWritingDirection)writingDirection |
|||
forRange:(UITextRange*)range { |
|||
} |
|||
|
|||
#pragma mark - UITextInput cursor, selection rect handling |
|||
|
|||
- (CGRect)firstRectForRange:(UITextRange*)range { |
|||
return CGRectZero; |
|||
} |
|||
|
|||
- (CGRect)caretRectForPosition:(UITextPosition*)position { |
|||
return CGRectZero; |
|||
} |
|||
|
|||
- (UITextPosition*)closestPositionToPoint:(CGPoint)point { |
|||
NSUInteger currentIndex = ((UIWidgetsTextPosition*)_selectedTextRange.start).index; |
|||
return [UIWidgetsTextPosition positionWithIndex:currentIndex]; |
|||
} |
|||
|
|||
- (NSArray*)selectionRectsForRange:(UITextRange*)range { |
|||
return @[]; |
|||
} |
|||
|
|||
- (UITextPosition*)closestPositionToPoint:(CGPoint)point withinRange:(UITextRange*)range { |
|||
return range.start; |
|||
} |
|||
|
|||
- (UITextRange*)characterRangeAtPoint:(CGPoint)point { |
|||
NSUInteger currentIndex = ((UIWidgetsTextPosition*)_selectedTextRange.start).index; |
|||
return [UIWidgetsTextRange rangeWithNSRange:[self rangeForCharacterAtIndex:currentIndex]]; |
|||
} |
|||
|
|||
#pragma mark - UIKeyInput Overrides |
|||
|
|||
- (void)updateEditingState { |
|||
NSUInteger selectionBase = ((UIWidgetsTextPosition*)_selectedTextRange.start).index; |
|||
NSUInteger selectionExtent = ((UIWidgetsTextPosition*)_selectedTextRange.end).index; |
|||
|
|||
NSUInteger composingBase = 0; |
|||
NSUInteger composingExtent = 0; |
|||
if (self.markedTextRange != nil) { |
|||
composingBase = ((UIWidgetsTextPosition*)self.markedTextRange.start).index; |
|||
composingExtent = ((UIWidgetsTextPosition*)self.markedTextRange.end).index; |
|||
} |
|||
[_textInputDelegate updateEditingClient:_textInputClient |
|||
withState:@{ |
|||
@"selectionBase" : @(selectionBase), |
|||
@"selectionExtent" : @(selectionExtent), |
|||
@"selectionAffinity" : @(_selectionAffinity), |
|||
@"selectionIsDirectional" : @(false), |
|||
@"composingBase" : @(composingBase), |
|||
@"composingExtent" : @(composingExtent), |
|||
@"text" : [NSString stringWithString:self.text], |
|||
}]; |
|||
} |
|||
|
|||
- (BOOL)hasText { |
|||
return self.text.length > 0; |
|||
} |
|||
|
|||
- (void)insertText:(NSString*)text { |
|||
_selectionAffinity = _kTextAffinityDownstream; |
|||
[self replaceRange:_selectedTextRange withText:text]; |
|||
} |
|||
|
|||
- (void)deleteBackward { |
|||
_selectionAffinity = _kTextAffinityDownstream; |
|||
if (!_selectedTextRange.isEmpty) |
|||
[self replaceRange:_selectedTextRange withText:@""]; |
|||
} |
|||
|
|||
@end |
|||
|
|||
/** |
|||
* Hides `UIWidgetsTextInputView` from iOS accessibility system so it |
|||
* does not show up twice, once where it is in the `UIView` hierarchy, |
|||
* and a second time as part of the `SemanticsObject` hierarchy. |
|||
*/ |
|||
@interface UIWidgetsTextInputViewAccessibilityHider : UIView { |
|||
} |
|||
|
|||
@end |
|||
|
|||
@implementation UIWidgetsTextInputViewAccessibilityHider { |
|||
} |
|||
|
|||
- (BOOL)accessibilityElementsHidden { |
|||
return YES; |
|||
} |
|||
|
|||
@end |
|||
|
|||
@implementation UIWidgetsTextInputPlugin { |
|||
UIWidgetsTextInputView* _view; |
|||
UIWidgetsTextInputViewAccessibilityHider* _inputHider; |
|||
} |
|||
|
|||
@synthesize textInputDelegate = _textInputDelegate; |
|||
|
|||
- (instancetype)init { |
|||
self = [super init]; |
|||
|
|||
if (self) { |
|||
_view = [[UIWidgetsTextInputView alloc] init]; |
|||
_inputHider = [[UIWidgetsTextInputViewAccessibilityHider alloc] init]; |
|||
} |
|||
|
|||
return self; |
|||
} |
|||
|
|||
- (void)dealloc { |
|||
[self hideTextInput]; |
|||
[_view release]; |
|||
[_inputHider release]; |
|||
|
|||
[super dealloc]; |
|||
} |
|||
|
|||
- (UIView<UITextInput>*)textInputView { |
|||
return _view; |
|||
} |
|||
|
|||
- (void)showTextInput { |
|||
NSAssert([UIApplication sharedApplication].keyWindow != nullptr, |
|||
@"The application must have a key window since the keyboard client " |
|||
@"must be part of the responder chain to function"); |
|||
_view.textInputDelegate = _textInputDelegate; |
|||
[_inputHider addSubview:_view]; |
|||
[[UIApplication sharedApplication].keyWindow addSubview:_inputHider]; |
|||
[_view becomeFirstResponder]; |
|||
} |
|||
|
|||
- (void)hideTextInput { |
|||
[_view resignFirstResponder]; |
|||
[_view removeFromSuperview]; |
|||
[_inputHider removeFromSuperview]; |
|||
} |
|||
|
|||
- (void)setTextInputClient:(int)client withConfiguration:(NSDictionary*)configuration { |
|||
NSDictionary* inputType = configuration[@"inputType"]; |
|||
NSString* keyboardAppearance = configuration[@"keyboardAppearance"]; |
|||
_view.keyboardType = ToUIKeyboardType(inputType); |
|||
_view.returnKeyType = ToUIReturnKeyType(configuration[@"inputAction"]); |
|||
_view.autocapitalizationType = ToUITextAutoCapitalizationType(configuration); |
|||
if ([keyboardAppearance isEqualToString:@"Brightness.dark"]) { |
|||
_view.keyboardAppearance = UIKeyboardAppearanceDark; |
|||
} else if ([keyboardAppearance isEqualToString:@"Brightness.light"]) { |
|||
_view.keyboardAppearance = UIKeyboardAppearanceLight; |
|||
} else { |
|||
_view.keyboardAppearance = UIKeyboardAppearanceDefault; |
|||
} |
|||
_view.secureTextEntry = [configuration[@"obscureText"] boolValue]; |
|||
NSString* autocorrect = configuration[@"autocorrect"]; |
|||
_view.autocorrectionType = autocorrect && ![autocorrect boolValue] |
|||
? UITextAutocorrectionTypeNo |
|||
: UITextAutocorrectionTypeDefault; |
|||
[_view setTextInputClient:client]; |
|||
[_view reloadInputViews]; |
|||
} |
|||
|
|||
- (void)setTextInputEditingState:(NSDictionary*)state { |
|||
[_view setTextInputState:state]; |
|||
} |
|||
|
|||
- (void)clearTextInputClient { |
|||
[_view setTextInputClient:0]; |
|||
} |
|||
|
|||
+ (instancetype)sharedInstance { |
|||
static UIWidgetsTextInputPlugin *sharedInstance = nil; |
|||
static dispatch_once_t onceToken; |
|||
|
|||
dispatch_once(&onceToken, ^{ |
|||
sharedInstance = [[UIWidgetsTextInputPlugin alloc] init]; |
|||
sharedInstance.textInputDelegate = [[DefaultUIWidgetsTextInputDelegate alloc] init]; |
|||
}); |
|||
return sharedInstance; |
|||
} |
|||
@end |
|||
|
|||
|
|||
@implementation DefaultUIWidgetsTextInputDelegate |
|||
|
|||
- (void)updateEditingClient:(int)client withState:(NSDictionary*)state { |
|||
UIWidgetsMethodMessage(@"TextInput", @"TextInputClient.updateEditingState", @[@(client), state]); |
|||
} |
|||
|
|||
- (void)performAction:(UIWidgetsTextInputAction)action withClient:(int)client { |
|||
NSString* actionString; |
|||
switch (action) { |
|||
case UIWidgetsTextInputActionUnspecified: |
|||
// Where did the term "unspecified" come from? iOS has a "default" and Android |
|||
// has "unspecified." These 2 terms seem to mean the same thing but we need |
|||
// to pick just one. "unspecified" was chosen because "default" is often a |
|||
// reserved word in languages with switch statements (dart, java, etc). |
|||
actionString = @"TextInputAction.unspecified"; |
|||
break; |
|||
case UIWidgetsTextInputActionDone: |
|||
actionString = @"TextInputAction.done"; |
|||
break; |
|||
case UIWidgetsTextInputActionGo: |
|||
actionString = @"TextInputAction.go"; |
|||
break; |
|||
case UIWidgetsTextInputActionSend: |
|||
actionString = @"TextInputAction.send"; |
|||
break; |
|||
case UIWidgetsTextInputActionSearch: |
|||
actionString = @"TextInputAction.search"; |
|||
break; |
|||
case UIWidgetsTextInputActionNext: |
|||
actionString = @"TextInputAction.next"; |
|||
break; |
|||
case UIWidgetsTextInputActionContinue: |
|||
actionString = @"TextInputAction.continue"; |
|||
break; |
|||
case UIWidgetsTextInputActionJoin: |
|||
actionString = @"TextInputAction.join"; |
|||
break; |
|||
case UIWidgetsTextInputActionRoute: |
|||
actionString = @"TextInputAction.route"; |
|||
break; |
|||
case UIWidgetsTextInputActionEmergencyCall: |
|||
actionString = @"TextInputAction.emergencyCall"; |
|||
break; |
|||
case UIWidgetsTextInputActionNewline: |
|||
actionString = @"TextInputAction.newline"; |
|||
break; |
|||
} |
|||
|
|||
UIWidgetsMethodMessage(@"TextInput", @"TextInputClient.performAction", @[@(client), actionString]); |
|||
} |
|||
@end |
|||
|
|||
extern "C" { |
|||
|
|||
void UIWidgetsTextInputShow() { |
|||
[[UIWidgetsTextInputPlugin sharedInstance] showTextInput]; |
|||
} |
|||
|
|||
void UIWidgetsTextInputHide() { |
|||
[[UIWidgetsTextInputPlugin sharedInstance] hideTextInput]; |
|||
} |
|||
|
|||
void UIWidgetsTextInputSetClient(int client, const char* configurationJson) { |
|||
NSError *jsonError = nil; |
|||
NSString *nsJsonString=[NSString stringWithUTF8String:configurationJson]; |
|||
NSData *objectData = [nsJsonString dataUsingEncoding:NSUTF8StringEncoding]; |
|||
NSDictionary *json = [NSJSONSerialization JSONObjectWithData:objectData |
|||
options:NSJSONReadingMutableContainers |
|||
error:&jsonError]; |
|||
|
|||
[[UIWidgetsTextInputPlugin sharedInstance] setTextInputClient:client withConfiguration:json]; |
|||
} |
|||
|
|||
void UIWidgetsTextInputSetTextInputEditingState(const char* jsonText) { |
|||
NSError *jsonError; |
|||
NSString *nsJsonString=[NSString stringWithUTF8String:jsonText]; |
|||
NSData *objectData = [nsJsonString dataUsingEncoding:NSUTF8StringEncoding]; |
|||
NSDictionary *args = [NSJSONSerialization JSONObjectWithData:objectData |
|||
options:NSJSONReadingMutableContainers |
|||
error:&jsonError]; |
|||
[[UIWidgetsTextInputPlugin sharedInstance] setTextInputEditingState:args]; |
|||
} |
|||
|
|||
void UIWidgetsTextInputClearTextInputClient() { |
|||
[[UIWidgetsTextInputPlugin sharedInstance] clearTextInputClient]; |
|||
} |
|||
} |
|||
|
撰写
预览
正在加载...
取消
保存
Reference in new issue