Skip to content
体验新版
项目
组织
正在加载...
登录
切换导航
打开侧边栏
sxychenjing
engine
提交
6a5971da
E
engine
项目概览
sxychenjing
/
engine
与 Fork 源项目一致
从无法访问的项目Fork
通知
3
Star
0
Fork
0
代码
文件
提交
分支
Tags
贡献者
分支图
Diff
Issue
0
列表
看板
标记
里程碑
合并请求
0
Wiki
0
Wiki
分析
仓库
DevOps
项目成员
Pages
E
engine
项目概览
项目概览
详情
发布
仓库
仓库
文件
提交
分支
标签
贡献者
分支图
比较
Issue
0
Issue
0
列表
看板
标记
里程碑
合并请求
0
合并请求
0
Pages
分析
分析
仓库分析
DevOps
Wiki
0
Wiki
成员
成员
收起侧边栏
关闭侧边栏
动态
分支图
创建新Issue
提交
Issue看板
体验新版 GitCode,发现更多精彩内容 >>
未验证
提交
6a5971da
编写于
12月 03, 2020
作者:
M
Mouad Debbar
提交者:
GitHub
12月 03, 2020
浏览文件
操作
浏览文件
下载
电子邮件补丁
差异文件
[web] Initial rich measurement implementation (#22779)
上级
6ae2594b
变更
7
展开全部
隐藏空白更改
内联
并排
Showing
7 changed file
with
1257 addition
and
26 deletion
+1257
-26
ci/licenses_golden/licenses_flutter
ci/licenses_golden/licenses_flutter
+1
-0
lib/web_ui/lib/src/engine.dart
lib/web_ui/lib/src/engine.dart
+1
-0
lib/web_ui/lib/src/engine/text/canvas_paragraph.dart
lib/web_ui/lib/src/engine/text/canvas_paragraph.dart
+48
-25
lib/web_ui/lib/src/engine/text/layout_service.dart
lib/web_ui/lib/src/engine/text/layout_service.dart
+381
-0
lib/web_ui/lib/src/engine/text/paragraph.dart
lib/web_ui/lib/src/engine/text/paragraph.dart
+1
-1
lib/web_ui/test/text/layout_service_helper.dart
lib/web_ui/test/text/layout_service_helper.dart
+142
-0
lib/web_ui/test/text/layout_service_plain_test.dart
lib/web_ui/test/text/layout_service_plain_test.dart
+683
-0
未找到文件。
ci/licenses_golden/licenses_flutter
浏览文件 @
6a5971da
...
...
@@ -530,6 +530,7 @@ FILE: ../../../flutter/lib/web_ui/lib/src/engine/shadow.dart
FILE: ../../../flutter/lib/web_ui/lib/src/engine/test_embedding.dart
FILE: ../../../flutter/lib/web_ui/lib/src/engine/text/canvas_paragraph.dart
FILE: ../../../flutter/lib/web_ui/lib/src/engine/text/font_collection.dart
FILE: ../../../flutter/lib/web_ui/lib/src/engine/text/layout_service.dart
FILE: ../../../flutter/lib/web_ui/lib/src/engine/text/line_break_properties.dart
FILE: ../../../flutter/lib/web_ui/lib/src/engine/text/line_breaker.dart
FILE: ../../../flutter/lib/web_ui/lib/src/engine/text/measurement.dart
...
...
lib/web_ui/lib/src/engine.dart
浏览文件 @
6a5971da
...
...
@@ -125,6 +125,7 @@ part 'engine/services/serialization.dart';
part
'engine/shadow.dart'
;
part
'engine/test_embedding.dart'
;
part
'engine/text/font_collection.dart'
;
part
'engine/text/layout_service.dart'
;
part
'engine/text/line_break_properties.dart'
;
part
'engine/text/line_breaker.dart'
;
part
'engine/text/measurement.dart'
;
...
...
lib/web_ui/lib/src/engine/text/canvas_paragraph.dart
浏览文件 @
6a5971da
...
...
@@ -34,39 +34,34 @@ class CanvasParagraph implements EngineParagraph {
/// The number of placeholders in this paragraph.
final
int
placeholderCount
;
// Defaulting to -1 for non-laid-out paragraphs like the native engine does.
@override
double
width
=
-
1.0
;
double
get
width
=>
_layoutService
.
width
;
@override
double
height
=
0.0
;
double
get
height
=>
_layoutService
.
height
;
@override
double
get
longestLine
{
assert
(
isLaidOut
);
// TODO(mdebbar): Use the line metrics generated during layout to find out
// the longest line.
return
0.0
;
}
double
get
longestLine
=>
_layoutService
.
longestLine
;
@override
double
minIntrinsicWidth
=
0.0
;
double
get
minIntrinsicWidth
=>
_layoutService
.
minIntrinsicWidth
;
@override
double
maxIntrinsicWidth
=
0.0
;
double
get
maxIntrinsicWidth
=>
_layoutService
.
maxIntrinsicWidth
;
@override
double
alphabeticBaseline
=
-
1.0
;
double
get
alphabeticBaseline
=>
_layoutService
.
alphabeticBaseline
;
@override
double
ideographicBaseline
=
-
1.0
;
double
get
ideographicBaseline
=>
_layoutService
.
ideographicBaseline
;
@override
bool
get
didExceedMaxLines
=>
_didExceedMaxLines
;
bool
_didExceedMaxLines
=
false
;
bool
get
didExceedMaxLines
=>
_layoutService
.
didExceedMaxLines
;
ui
.
ParagraphConstraints
?
_lastUsedConstraints
;
late
final
TextLayoutService
_layoutService
=
TextLayoutService
(
this
);
@override
void
layout
(
ui
.
ParagraphConstraints
constraints
)
{
// When constraint width has a decimal place, we floor it to avoid getting
...
...
@@ -88,8 +83,7 @@ class CanvasParagraph implements EngineParagraph {
if
(
Profiler
.
isBenchmarkMode
)
{
stopwatch
=
Stopwatch
()..
start
();
}
// TODO(mdebbar): Perform the layout using a new rich text measurement service.
// TODO(mdebbar): Don't forget to update `_didExceedMaxLines`.
_layoutService
.
performLayout
(
constraints
);
if
(
Profiler
.
isBenchmarkMode
)
{
stopwatch
.
stop
();
Profiler
.
instance
...
...
@@ -161,7 +155,7 @@ class CanvasParagraph implements EngineParagraph {
isSpan:
true
,
);
domRenderer
.
append
(
element
,
spanElement
);
}
else
if
(
span
is
P
aragraphPlaceholder
)
{
}
else
if
(
span
is
P
laceholderSpan
)
{
domRenderer
.
append
(
element
,
_createPlaceholderElement
(
placeholder:
span
),
...
...
@@ -234,8 +228,7 @@ class CanvasParagraph implements EngineParagraph {
@override
List
<
ui
.
LineMetrics
>
computeLineMetrics
()
{
// TODO(mdebbar): After layout, line metrics should be available.
return
<
ui
.
LineMetrics
>[];
return
_layoutService
.
lines
;
}
}
...
...
@@ -243,7 +236,11 @@ class CanvasParagraph implements EngineParagraph {
///
/// These spans are stored as a flat list in the paragraph object.
abstract
class
ParagraphSpan
{
const
ParagraphSpan
();
/// The index of the beginning of the range of text represented by this span.
int
get
start
;
/// The index of the end of the range of text represented by this span.
int
get
end
;
}
/// Represent a span of text in the paragraph.
...
...
@@ -254,7 +251,7 @@ abstract class ParagraphSpan {
/// Instead of keeping spans and styles in a tree hierarchy like the framework
/// does, we flatten the structure and resolve/merge all the styles from parent
/// nodes.
class
FlatTextSpan
extend
s
ParagraphSpan
{
class
FlatTextSpan
implement
s
ParagraphSpan
{
/// Creates a [FlatTextSpan] with the given [style], representing the span of
/// text in the range between [start] and [end].
FlatTextSpan
({
...
...
@@ -266,10 +263,10 @@ class FlatTextSpan extends ParagraphSpan {
/// The resolved style of the span.
final
EngineTextStyle
style
;
/// The index of the beginning of the range of text represented by this span.
@override
final
int
start
;
/// The index of the end of the range of text represented by this span.
@override
final
int
end
;
String
textOf
(
CanvasParagraph
paragraph
)
{
...
...
@@ -279,6 +276,31 @@ class FlatTextSpan extends ParagraphSpan {
}
}
class
PlaceholderSpan
extends
ParagraphPlaceholder
implements
ParagraphSpan
{
PlaceholderSpan
(
int
index
,
double
width
,
double
height
,
ui
.
PlaceholderAlignment
alignment
,
{
required
double
baselineOffset
,
required
ui
.
TextBaseline
baseline
,
})
:
start
=
index
,
end
=
index
,
super
(
width
,
height
,
alignment
,
baselineOffset:
baselineOffset
,
baseline:
baseline
,
);
@override
final
int
start
;
@override
final
int
end
;
}
/// Represents a node in the tree of text styles pushed to [ui.ParagraphBuilder].
///
/// The [ui.ParagraphBuilder.pushText] and [ui.ParagraphBuilder.pop] operations
...
...
@@ -551,7 +573,8 @@ class CanvasParagraphBuilder implements ui.ParagraphBuilder {
_placeholderCount
++;
_placeholderScales
.
add
(
scale
);
_spans
.
add
(
ParagraphPlaceholder
(
_spans
.
add
(
PlaceholderSpan
(
_plainTextBuffer
.
length
,
width
*
scale
,
height
*
scale
,
alignment
,
...
...
lib/web_ui/lib/src/engine/text/layout_service.dart
0 → 100644
浏览文件 @
6a5971da
// Copyright 2013 The Flutter Authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
// @dart = 2.12
part of
engine
;
/// Performs layout on a [CanvasParagraph].
///
/// It uses a [html.CanvasElement] to measure text.
class
TextLayoutService
{
TextLayoutService
(
this
.
paragraph
);
final
CanvasParagraph
paragraph
;
final
html
.
CanvasRenderingContext2D
context
=
html
.
CanvasElement
().
context2D
;
// *** Results of layout *** //
// Look at the Paragraph class for documentation of the following properties.
double
width
=
-
1.0
;
double
height
=
0.0
;
double
longestLine
=
0.0
;
double
minIntrinsicWidth
=
0.0
;
double
maxIntrinsicWidth
=
0.0
;
double
alphabeticBaseline
=
-
1.0
;
double
ideographicBaseline
=
-
1.0
;
bool
didExceedMaxLines
=
false
;
final
List
<
EngineLineMetrics
>
lines
=
<
EngineLineMetrics
>[];
// *** Convenient shortcuts used during layout *** //
int
?
get
maxLines
=>
paragraph
.
paragraphStyle
.
_maxLines
;
bool
get
unlimitedLines
=>
maxLines
==
null
;
bool
get
hasEllipsis
=>
paragraph
.
paragraphStyle
.
_ellipsis
!=
null
;
/// Performs the layout on a paragraph given the [constraints].
///
/// The function starts by resetting all layout-related properties. Then it
/// starts looping through the paragraph to calculate all layout metrics.
///
/// It uses a [Spanometer] to perform measurements within spans of the
/// paragraph. It also uses [LineBuilders] to generate [EngineLineMetrics] as
/// it iterates through the paragraph.
///
/// The main loop keeps going until:
///
/// 1. The end of the paragraph is reached (i.e. LineBreakType.endOfText).
/// 2. Enough lines have been computed to satisfy [maxLines].
/// 3. An ellipsis is appended because of an overflow.
void
performLayout
(
ui
.
ParagraphConstraints
constraints
)
{
final
int
spanCount
=
paragraph
.
spans
.
length
;
// Reset results from previous layout.
width
=
constraints
.
width
;
height
=
0.0
;
longestLine
=
0.0
;
minIntrinsicWidth
=
0.0
;
maxIntrinsicWidth
=
0.0
;
didExceedMaxLines
=
false
;
lines
.
clear
();
final
Spanometer
spanometer
=
Spanometer
(
paragraph
,
context
);
int
spanIndex
=
0
;
ParagraphSpan
span
=
paragraph
.
spans
[
0
];
LineBuilder
currentLine
=
LineBuilder
.
first
(
paragraph
,
spanometer
);
LineBuilder
maxIntrinsicLine
=
LineBuilder
.
first
(
paragraph
,
spanometer
);
// The only way to exit this while loop is by hitting the `break;` statement
// when we reach the `endOfText` line break.
while
(
true
)
{
// *********************************************** //
// *** HANDLE HARD LINE BREAKS AND END OF TEXT *** //
// *********************************************** //
if
(
currentLine
.
end
.
isHard
)
{
if
(
currentLine
.
isNotEmpty
)
{
lines
.
add
(
currentLine
.
build
());
}
if
(
currentLine
.
end
.
type
==
LineBreakType
.
endOfText
)
{
break
;
}
else
{
currentLine
=
currentLine
.
nextLine
();
}
}
// ********************************* //
// *** THE MAIN MEASUREMENT PART *** //
// ********************************* //
if
(
span
is
PlaceholderSpan
)
{
spanometer
.
currentSpan
=
null
;
final
double
lineWidth
=
currentLine
.
width
+
span
.
width
;
// TODO(mdebbar): Consider how placeholders affect min/max intrinsics.
if
(
lineWidth
<=
constraints
.
width
)
{
// The placeholder fits on the current line.
// TODO(mdebbar):
// (1) adjust the current line's height to fit the placeholder.
// (2) update accumulated line width.
}
else
{
// The placeholder can't fit on the current line.
// TODO(mdebbar):
// (1) create a line.
// (2) adjust the new line's height to fit the placeholder.
// (3) update `lineStart`, etc.
}
}
else
if
(
span
is
FlatTextSpan
)
{
spanometer
.
currentSpan
=
span
;
final
LineBreakResult
nextBreak
=
currentLine
.
findNextBreak
(
span
.
end
);
final
double
additionalWidth
=
currentLine
.
getAdditionalWidthTo
(
nextBreak
);
// For the purpose of max intrinsic width, we don't care if the line
// fits within the constraints or not. So we always extend it.
if
(
maxIntrinsicLine
.
end
!=
nextBreak
)
{
maxIntrinsicLine
.
extendTo
(
nextBreak
);
}
if
(
currentLine
.
width
+
additionalWidth
<=
constraints
.
width
)
{
// TODO(mdebbar): Handle the case when `nextBreak` is just a span end
// that shouldn't extend the line yet.
// The line can extend to `nextBreak` without overflowing.
currentLine
.
extendTo
(
nextBreak
);
}
else
{
// The chunk of text can't fit into the current line.
final
bool
isLastLine
=
(
hasEllipsis
&&
unlimitedLines
)
||
lines
.
length
+
1
==
maxLines
;
if
(
isLastLine
&&
hasEllipsis
)
{
// We've reached the line that requires an ellipsis to be appended
// to it.
// TODO(mdebbar): Remove this line and implement overflow ellipsis.
currentLine
.
extendTo
(
nextBreak
);
}
else
if
(
currentLine
.
isEmpty
)
{
// The current line is still empty, which means we are dealing
// with a single block of text that doesn't fit in a single line.
// We need to force-break it.
// TODO(mdebbar): Remove this line and implement force-breaking.
currentLine
.
extendTo
(
nextBreak
);
}
else
{
// Normal line break.
lines
.
add
(
currentLine
.
build
());
currentLine
=
currentLine
.
nextLine
();
}
}
}
else
{
throw
UnimplementedError
(
'Unknown span type:
${span.runtimeType}
'
);
}
// ************************************************ //
// *** LONGEST LINE && MAX/MIN INTRINSIC WIDTHS *** //
// ************************************************ //
if
(
longestLine
<
currentLine
.
width
)
{
longestLine
=
currentLine
.
width
;
}
if
(
minIntrinsicWidth
<
currentLine
.
widthOfLastExtension
)
{
minIntrinsicWidth
=
currentLine
.
widthOfLastExtension
;
}
if
(
maxIntrinsicLine
.
end
.
isHard
)
{
// Max intrinsic width includes the width of trailing spaces.
if
(
maxIntrinsicWidth
<
maxIntrinsicLine
.
widthIncludingSpace
)
{
maxIntrinsicWidth
=
maxIntrinsicLine
.
widthIncludingSpace
;
}
maxIntrinsicLine
=
maxIntrinsicLine
.
nextLine
();
}
// ********************************************* //
// *** ADVANCE TO THE NEXT SPAN IF NECESSARY *** //
// ********************************************* //
// Only go to the next span if we've reached the end of this span.
if
(
currentLine
.
end
.
index
>=
span
.
end
&&
spanIndex
<
spanCount
-
1
)
{
span
=
paragraph
.
spans
[++
spanIndex
];
}
}
}
}
/// Builds instances of [EngineLineMetrics] for the given [paragraph].
///
/// Usage of this class starts by calling [LineBuilder.first] to start building
/// the first line of the paragraph.
///
/// Then new line breaks can be found by calling [LineBuilder.findNextBreak].
///
/// The line can be extended one or more times before it's built by calling
/// [LineBuilder.build] which generates the [EngineLineMetrics] instace.
///
/// To start building the next line, simply call [LineBuilder.nextLine] which
/// creates a new [LineBuilder] that can be extended and built and so on.
class
LineBuilder
{
LineBuilder
.
_
(
this
.
paragraph
,
this
.
spanometer
,
{
required
this
.
start
,
required
this
.
lineNumber
,
})
:
end
=
start
;
/// Creates a [LineBuilder] for the first line in a paragraph.
factory
LineBuilder
.
first
(
CanvasParagraph
paragraph
,
Spanometer
spanometer
)
{
return
LineBuilder
.
_
(
paragraph
,
spanometer
,
lineNumber:
0
,
start:
LineBreakResult
.
sameIndex
(
0
,
LineBreakType
.
prohibited
),
);
}
final
CanvasParagraph
paragraph
;
final
Spanometer
spanometer
;
final
LineBreakResult
start
;
final
int
lineNumber
;
LineBreakResult
end
;
/// The width of the line so far, excluding trailing white space.
double
width
=
0.0
;
/// The width of trailing white space in the line.
double
widthOfTrailingSpace
=
0.0
;
/// The width of the line so far, including trailing white space.
double
get
widthIncludingSpace
=>
width
+
widthOfTrailingSpace
;
/// The width of the last extension to the line made via [extendTo].
double
widthOfLastExtension
=
0.0
;
bool
get
isEmpty
=>
start
==
end
;
bool
get
isNotEmpty
=>
!
isEmpty
;
/// Measures the width of text between the end of this line and [newEnd].
double
getAdditionalWidthTo
(
LineBreakResult
newEnd
)
{
// If the extension is all made of space characters, it shouldn't add
// anything to the width.
if
(
end
.
index
==
newEnd
.
indexWithoutTrailingSpaces
)
{
return
0.0
;
}
return
widthOfTrailingSpace
+
spanometer
.
measure
(
end
,
newEnd
);
}
/// Extends the line by setting a [newEnd].
void
extendTo
(
LineBreakResult
newEnd
)
{
// If the current end of the line is a hard break, the line shouldn't be
// extended any further.
assert
(
isEmpty
||
!
end
.
isHard
,
'Cannot extend a line that ends with a hard break.'
,
);
// TODO(mdebbar): Handle the case where the entire extension is made of spaces.
widthOfLastExtension
=
spanometer
.
measure
(
end
,
newEnd
);
final
double
additionalWidthIncludingSpace
=
spanometer
.
measureIncludingSpace
(
end
,
newEnd
);
// Add the width of previous trailing space.
width
+=
widthOfTrailingSpace
+
widthOfLastExtension
;
widthOfTrailingSpace
=
additionalWidthIncludingSpace
-
widthOfLastExtension
;
end
=
newEnd
;
}
/// Builds the [EngineLineMetrics] instance that represents this line.
EngineLineMetrics
build
()
{
final
String
text
=
paragraph
.
toPlainText
();
return
EngineLineMetrics
.
withText
(
text
.
substring
(
start
.
index
,
end
.
indexWithoutTrailingNewlines
),
startIndex:
start
.
index
,
endIndex:
end
.
index
,
endIndexWithoutNewlines:
end
.
indexWithoutTrailingNewlines
,
hardBreak:
end
.
isHard
,
width:
width
,
widthWithTrailingSpaces:
width
+
widthOfTrailingSpace
,
// TODO(mdebbar): Calculate actual align offset.
left:
0.0
,
lineNumber:
lineNumber
,
);
}
/// Finds the next line break after the end of this line.
LineBreakResult
findNextBreak
(
int
maxEnd
)
{
return
nextLineBreak
(
paragraph
.
toPlainText
(),
end
.
index
,
maxEnd:
maxEnd
);
}
/// Creates a new [LineBuilder] to build the next line in the paragraph.
LineBuilder
nextLine
()
{
return
LineBuilder
.
_
(
paragraph
,
spanometer
,
start:
end
,
lineNumber:
lineNumber
+
1
,
);
}
}
/// Responsible for taking measurements within spans of a paragraph.
///
/// Can't perform measurements across spans. To measure across spans, multiple
/// measurements have to be taken.
///
/// Before performing any measurement, the [currentSpan] has to be set. Once
/// it's set, the [Spanometer] updates the underlying [context] so that
/// subsequent measurements use the correct styles.
class
Spanometer
{
Spanometer
(
this
.
paragraph
,
this
.
context
);
final
CanvasParagraph
paragraph
;
final
html
.
CanvasRenderingContext2D
context
;
String
_cssFontString
=
''
;
double
?
get
letterSpacing
=>
_currentSpan
!.
style
.
_letterSpacing
;
FlatTextSpan
?
_currentSpan
;
set
currentSpan
(
FlatTextSpan
?
span
)
{
if
(
span
==
_currentSpan
)
{
return
;
}
_currentSpan
=
span
;
// No need to update css font string when `span` is null.
if
(
span
==
null
)
{
return
;
}
// Update the font string if it's different from the previous span.
final
String
cssFontString
=
span
.
style
.
cssFontString
;
if
(
_cssFontString
!=
cssFontString
)
{
_cssFontString
=
cssFontString
;
context
.
font
=
cssFontString
;
}
}
/// Measures the width of text between two line breaks.
///
/// Doesn't include the width of any trailing white space.
double
measure
(
LineBreakResult
start
,
LineBreakResult
end
)
{
return
_measure
(
start
.
index
,
end
.
indexWithoutTrailingSpaces
);
}
/// Measures the width of text between two line breaks.
///
/// Includes the width of trailing white space, if any.
double
measureIncludingSpace
(
LineBreakResult
start
,
LineBreakResult
end
)
{
return
_measure
(
start
.
index
,
end
.
indexWithoutTrailingNewlines
);
}
double
_measure
(
int
start
,
int
end
)
{
assert
(
_currentSpan
!=
null
);
final
FlatTextSpan
span
=
_currentSpan
!;
// Make sure the range is within the current span.
assert
(
start
>=
span
.
start
&&
start
<=
span
.
end
);
assert
(
end
>=
span
.
start
&&
end
<=
span
.
end
);
final
String
text
=
paragraph
.
toPlainText
();
return
_measureSubstring
(
context
,
text
,
start
,
end
,
letterSpacing:
letterSpacing
,
);
}
}
lib/web_ui/lib/src/engine/text/paragraph.dart
浏览文件 @
6a5971da
...
...
@@ -1589,7 +1589,7 @@ class DomParagraphBuilder implements ui.ParagraphBuilder {
/// Holds information for a placeholder in a paragraph.
///
/// [width], [height] and [baselineOffset] are expected to be already scaled.
class
ParagraphPlaceholder
extends
ParagraphSpan
{
class
ParagraphPlaceholder
{
ParagraphPlaceholder
(
this
.
width
,
this
.
height
,
...
...
lib/web_ui/test/text/layout_service_helper.dart
0 → 100644
浏览文件 @
6a5971da
// Copyright 2013 The Flutter Authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
// @dart = 2.12
import
'package:test/test.dart'
;
import
'package:ui/src/engine.dart'
;
TestLine
l
(
String
?
displayText
,
int
?
startIndex
,
int
?
endIndex
,
{
int
?
endIndexWithoutNewlines
,
bool
?
hardBreak
,
double
?
width
,
double
?
widthWithTrailingSpaces
,
double
?
left
,
})
{
return
TestLine
(
displayText:
displayText
,
startIndex:
startIndex
,
endIndex:
endIndex
,
endIndexWithoutNewlines:
endIndexWithoutNewlines
,
hardBreak:
hardBreak
,
width:
width
,
widthWithTrailingSpaces:
widthWithTrailingSpaces
,
left:
left
,
);
}
void
expectLines
(
CanvasParagraph
paragraph
,
List
<
TestLine
>
expectedLines
)
{
final
List
<
EngineLineMetrics
>
lines
=
paragraph
.
computeLineMetrics
()
as
List
<
EngineLineMetrics
>;
expect
(
lines
,
hasLength
(
expectedLines
.
length
));
for
(
int
i
=
0
;
i
<
lines
.
length
;
i
++)
{
final
EngineLineMetrics
line
=
lines
[
i
];
final
TestLine
expectedLine
=
expectedLines
[
i
];
expect
(
line
.
lineNumber
,
i
,
reason:
'
${i}
th line had the wrong `lineNumber`. Expected:
$i
. Actual:
${line.lineNumber}
'
,
);
if
(
expectedLine
.
displayText
!=
null
)
{
expect
(
line
.
displayText
,
expectedLine
.
displayText
,
reason:
'
${i}
th line had a different `displayText` value: "
${line.displayText}
" vs. "
${expectedLine.displayText}
"'
,
);
}
if
(
expectedLine
.
startIndex
!=
null
)
{
expect
(
line
.
startIndex
,
expectedLine
.
startIndex
,
reason:
'
${i}
th line had a different `startIndex` value: "
${line.startIndex}
" vs. "
${expectedLine.startIndex}
"'
,
);
}
if
(
expectedLine
.
endIndex
!=
null
)
{
expect
(
line
.
endIndex
,
expectedLine
.
endIndex
,
reason:
'
${i}
th line had a different `endIndex` value: "
${line.endIndex}
" vs. "
${expectedLine.endIndex}
"'
,
);
}
if
(
expectedLine
.
endIndexWithoutNewlines
!=
null
)
{
expect
(
line
.
endIndexWithoutNewlines
,
expectedLine
.
endIndexWithoutNewlines
,
reason:
'
${i}
th line had a different `endIndexWithoutNewlines` value: "
${line.endIndexWithoutNewlines}
" vs. "
${expectedLine.endIndexWithoutNewlines}
"'
,
);
}
if
(
expectedLine
.
hardBreak
!=
null
)
{
expect
(
line
.
hardBreak
,
expectedLine
.
hardBreak
,
reason:
'
${i}
th line had a different `hardBreak` value: "
${line.hardBreak}
" vs. "
${expectedLine.hardBreak}
"'
,
);
}
if
(
expectedLine
.
height
!=
null
)
{
expect
(
line
.
height
,
expectedLine
.
height
,
reason:
'
${i}
th line had a different `height` value: "
${line.height}
" vs. "
${expectedLine.height}
"'
,
);
}
if
(
expectedLine
.
width
!=
null
)
{
expect
(
line
.
width
,
expectedLine
.
width
,
reason:
'
${i}
th line had a different `width` value: "
${line.width}
" vs. "
${expectedLine.width}
"'
,
);
}
if
(
expectedLine
.
widthWithTrailingSpaces
!=
null
)
{
expect
(
line
.
widthWithTrailingSpaces
,
expectedLine
.
widthWithTrailingSpaces
,
reason:
'
${i}
th line had a different `widthWithTrailingSpaces` value: "
${line.widthWithTrailingSpaces}
" vs. "
${expectedLine.widthWithTrailingSpaces}
"'
,
);
}
if
(
expectedLine
.
left
!=
null
)
{
expect
(
line
.
left
,
expectedLine
.
left
,
reason:
'
${i}
th line had a different `left` value: "
${line.left}
" vs. "
${expectedLine.left}
"'
,
);
}
}
}
class
TestLine
{
TestLine
({
this
.
displayText
,
this
.
startIndex
,
this
.
endIndex
,
this
.
endIndexWithoutNewlines
,
this
.
hardBreak
,
this
.
height
,
this
.
width
,
this
.
widthWithTrailingSpaces
,
this
.
left
,
});
final
String
?
displayText
;
final
int
?
startIndex
;
final
int
?
endIndex
;
final
int
?
endIndexWithoutNewlines
;
final
bool
?
hardBreak
;
final
double
?
height
;
final
double
?
width
;
final
double
?
widthWithTrailingSpaces
;
final
double
?
left
;
}
\ No newline at end of file
lib/web_ui/test/text/layout_service_plain_test.dart
0 → 100644
浏览文件 @
6a5971da
此差异已折叠。
点击以展开。
编辑
预览
Markdown
is supported
0%
请重试
或
添加新附件
.
添加附件
取消
You are about to add
0
people
to the discussion. Proceed with caution.
先完成此消息的编辑!
取消
想要评论请
注册
或
登录