Skip to content
体验新版
项目
组织
正在加载...
登录
切换导航
打开侧边栏
weixin_43355755
engine
提交
df30ffef
E
engine
项目概览
weixin_43355755
/
engine
与 Fork 源项目一致
从无法访问的项目Fork
通知
1
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,体验更适合开发者的 AI 搜索 >>
未验证
提交
df30ffef
编写于
12月 08, 2020
作者:
M
Mouad Debbar
提交者:
GitHub
12月 08, 2020
浏览文件
操作
浏览文件
下载
电子邮件补丁
差异文件
[web] Fix edge cases when force-breaking lines (#22910)
上级
8a95546a
变更
2
隐藏空白更改
内联
并排
Showing
2 changed file
with
77 addition
and
34 deletion
+77
-34
lib/web_ui/lib/src/engine/text/layout_service.dart
lib/web_ui/lib/src/engine/text/layout_service.dart
+38
-12
lib/web_ui/test/text/layout_service_plain_test.dart
lib/web_ui/test/text/layout_service_plain_test.dart
+39
-22
未找到文件。
lib/web_ui/lib/src/engine/text/layout_service.dart
浏览文件 @
df30ffef
...
...
@@ -89,12 +89,13 @@ class TextLayoutService {
if
(
currentLine
.
end
.
isHard
)
{
if
(
currentLine
.
isNotEmpty
)
{
lines
.
add
(
currentLine
.
build
());
if
(
currentLine
.
end
.
type
!=
LineBreakType
.
endOfText
)
{
currentLine
=
currentLine
.
nextLine
();
}
}
if
(
currentLine
.
end
.
type
==
LineBreakType
.
endOfText
)
{
break
;
}
else
{
currentLine
=
currentLine
.
nextLine
();
}
}
...
...
@@ -400,13 +401,27 @@ class LineBuilder {
})
{
if
(
ellipsis
==
null
)
{
final
double
availableWidth
=
maxWidth
-
widthIncludingSpace
;
final
LineBreakResul
t
breakingPoint
=
spanometer
.
forceBreak
(
final
in
t
breakingPoint
=
spanometer
.
forceBreak
(
end
.
index
,
nextBreak
.
indexWithoutTrailingSpaces
,
availableWidth:
availableWidth
,
allowEmpty:
allowEmpty
,
);
extendTo
(
breakingPoint
);
// This condition can be true in the following case:
// 1. Next break is only one character away, with zero or many spaces. AND
// 2. There isn't enough width to fit the single character. AND
// 3. `allowEmpty` is false.
if
(
breakingPoint
==
nextBreak
.
indexWithoutTrailingSpaces
)
{
// In this case, we just extend to `nextBreak` instead of creating a new
// artifical break. It's safe (and better) to do so, because we don't
// want the trailing white space to go to the next line.
extendTo
(
nextBreak
);
}
else
{
extendTo
(
LineBreakResult
.
sameIndex
(
breakingPoint
,
LineBreakType
.
prohibited
),
);
}
return
;
}
...
...
@@ -428,20 +443,20 @@ class LineBuilder {
// After the loop ends, two things are correct:
// 1. All remaining segments in `_segments` can fit within constraints.
// 2. Adding `segmentToBreak` causes the line to overflow.
while
(
_segments
.
isNotEmpty
&&
width
>
availableWidth
)
{
while
(
_segments
.
isNotEmpty
&&
width
IncludingSpace
>
availableWidth
)
{
segmentToBreak
=
_popSegment
();
}
spanometer
.
currentSpan
=
segmentToBreak
.
span
as
FlatTextSpan
;
final
double
availableWidthForSegment
=
availableWidth
-
widthIncludingSpace
;
final
LineBreakResul
t
breakingPoint
=
spanometer
.
forceBreak
(
final
in
t
breakingPoint
=
spanometer
.
forceBreak
(
segmentToBreak
.
start
.
index
,
segmentToBreak
.
end
.
index
WithoutTrailingSpaces
,
segmentToBreak
.
end
.
index
,
availableWidth:
availableWidthForSegment
,
allowEmpty:
allowEmpty
,
);
extendTo
(
breakingPoint
);
extendTo
(
LineBreakResult
.
sameIndex
(
breakingPoint
,
LineBreakType
.
prohibited
)
);
}
/// Builds the [EngineLineMetrics] instance that represents this line.
...
...
@@ -544,7 +559,19 @@ class Spanometer {
return
_measureSubstring
(
context
,
text
,
0
,
text
.
length
);
}
LineBreakResult
forceBreak
(
/// In a continuous, unbreakable block of text from [start] to [end], finds
/// the point where text should be broken to fit in the given [availableWidth].
///
/// The [start] and [end] indices have to be within the same text span.
///
/// When [allowEmpty] is true, the result is guaranteed to be at least one
/// character after [start]. But if [allowEmpty] is false and there isn't
/// enough [availableWidth] to fit the first character, then [start] is
/// returned.
///
/// See also:
/// - [LineBuilder.forceBreak].
int
forceBreak
(
int
start
,
int
end
,
{
required
double
availableWidth
,
...
...
@@ -559,8 +586,7 @@ class Spanometer {
assert
(
end
>=
span
.
start
&&
end
<=
span
.
end
);
if
(
availableWidth
<=
0.0
)
{
return
LineBreakResult
.
sameIndex
(
allowEmpty
?
start
:
start
+
1
,
LineBreakType
.
prohibited
);
return
allowEmpty
?
start
:
start
+
1
;
}
int
low
=
start
;
...
...
@@ -580,7 +606,7 @@ class Spanometer {
if
(
low
==
start
&&
!
allowEmpty
)
{
low
++;
}
return
LineBreakResult
.
sameIndex
(
low
,
LineBreakType
.
prohibited
)
;
return
low
;
}
double
_measure
(
int
start
,
int
end
)
{
...
...
lib/web_ui/test/text/layout_service_plain_test.dart
浏览文件 @
df30ffef
...
...
@@ -11,7 +11,6 @@ import 'package:ui/ui.dart' as ui;
import
'layout_service_helper.dart'
;
const
bool
skipForceBreak
=
true
;
const
bool
skipTextAlign
=
true
;
const
bool
skipWordSpacing
=
true
;
...
...
@@ -147,6 +146,17 @@ void testMain() async {
l
(
'k lm'
,
10
,
14
,
hardBreak:
true
,
width:
40.0
,
left:
0.0
),
]);
// Constraints enough only for "abcdef" but not for the trailing space.
paragraph
=
plain
(
ahemStyle
,
'abcdef gh'
)..
layout
(
constrain
(
60.0
));
expect
(
paragraph
.
maxIntrinsicWidth
,
90
);
expect
(
paragraph
.
minIntrinsicWidth
,
60
);
expect
(
paragraph
.
width
,
60
);
// expect(paragraph.height, 20);
expectLines
(
paragraph
,
[
l
(
'abcdef '
,
0
,
7
,
hardBreak:
false
,
width:
60.0
,
left:
0.0
),
l
(
'gh'
,
7
,
9
,
hardBreak:
true
,
width:
20.0
,
left:
0.0
),
]);
// Constraints aren't enough even for a single character. In this case,
// we show a minimum of one character per line.
paragraph
=
plain
(
ahemStyle
,
'AA'
)..
layout
(
constrain
(
8.0
));
...
...
@@ -183,7 +193,7 @@ void testMain() async {
l
(
'A'
,
2
,
4
,
hardBreak:
true
,
width:
10.0
,
left:
0.0
),
l
(
''
,
4
,
4
,
hardBreak:
true
,
width:
0.0
,
left:
0.0
),
]);
}
,
skip:
skipForceBreak
);
});
test
(
'uses multi-line for text that contains new-line'
,
()
{
final
CanvasParagraph
paragraph
=
plain
(
ahemStyle
,
'12
\n
34'
)
...
...
@@ -315,16 +325,14 @@ void testMain() async {
l
(
'defg'
,
8
,
12
,
hardBreak:
true
,
width:
40.0
,
left:
0.0
),
]);
if
(!
skipForceBreak
)
{
// Very long text.
paragraph
=
plain
(
ahemStyle
,
'AAAAAAAAAAAA'
)..
layout
(
constrain
(
50.0
));
expect
(
paragraph
.
minIntrinsicWidth
,
120
);
expectLines
(
paragraph
,
[
l
(
'AAAAA'
,
0
,
5
,
hardBreak:
false
,
width:
50.0
,
left:
0.0
),
l
(
'AAAAA'
,
5
,
10
,
hardBreak:
false
,
width:
50.0
,
left:
0.0
),
l
(
'AA'
,
10
,
12
,
hardBreak:
true
,
width:
20.0
,
left:
0.0
),
]);
}
// Very long text.
paragraph
=
plain
(
ahemStyle
,
'AAAAAAAAAAAA'
)..
layout
(
constrain
(
50.0
));
expect
(
paragraph
.
minIntrinsicWidth
,
120
);
expectLines
(
paragraph
,
[
l
(
'AAAAA'
,
0
,
5
,
hardBreak:
false
,
width:
50.0
,
left:
0.0
),
l
(
'AAAAA'
,
5
,
10
,
hardBreak:
false
,
width:
50.0
,
left:
0.0
),
l
(
'AA'
,
10
,
12
,
hardBreak:
true
,
width:
20.0
,
left:
0.0
),
]);
});
test
(
'maxIntrinsicWidth'
,
()
{
...
...
@@ -372,16 +380,14 @@ void testMain() async {
l
(
'def '
,
5
,
11
,
hardBreak:
true
,
width:
30.0
,
left:
0.0
),
]);
if
(!
skipForceBreak
)
{
// Very long text.
paragraph
=
plain
(
ahemStyle
,
'AAAAAAAAAAAA'
)..
layout
(
constrain
(
50.0
));
expect
(
paragraph
.
maxIntrinsicWidth
,
120
);
expectLines
(
paragraph
,
[
l
(
'AAAAA'
,
0
,
5
,
hardBreak:
false
,
width:
50.0
,
left:
0.0
),
l
(
'AAAAA'
,
5
,
10
,
hardBreak:
false
,
width:
50.0
,
left:
0.0
),
l
(
'AA'
,
10
,
12
,
hardBreak:
true
,
width:
20.0
,
left:
0.0
),
]);
}
// Very long text.
paragraph
=
plain
(
ahemStyle
,
'AAAAAAAAAAAA'
)..
layout
(
constrain
(
50.0
));
expect
(
paragraph
.
maxIntrinsicWidth
,
120
);
expectLines
(
paragraph
,
[
l
(
'AAAAA'
,
0
,
5
,
hardBreak:
false
,
width:
50.0
,
left:
0.0
),
l
(
'AAAAA'
,
5
,
10
,
hardBreak:
false
,
width:
50.0
,
left:
0.0
),
l
(
'AA'
,
10
,
12
,
hardBreak:
true
,
width:
20.0
,
left:
0.0
),
]);
});
test
(
'respects text overflow'
,
()
{
...
...
@@ -418,6 +424,17 @@ void testMain() async {
l
(
'AA...'
,
4
,
6
,
hardBreak:
false
,
width:
50.0
,
left:
0.0
),
]);
// Constraints only enough to fit "AA" with the ellipsis, but not the
// trailing white space.
final
CanvasParagraph
trailingSpace
=
plain
(
overflowStyle
,
'AA AAA'
)
..
layout
(
constrain
(
50.0
));
expect
(
trailingSpace
.
minIntrinsicWidth
,
30
);
expect
(
trailingSpace
.
maxIntrinsicWidth
,
60
);
// expect(trailingSpace.height, 10);
expectLines
(
trailingSpace
,
[
l
(
'AA...'
,
0
,
2
,
hardBreak:
false
,
width:
50.0
,
left:
0.0
),
]);
// Tiny constraints.
final
CanvasParagraph
paragraph
=
plain
(
overflowStyle
,
'AAAA'
)
..
layout
(
constrain
(
30.0
));
...
...
编辑
预览
Markdown
is supported
0%
请重试
或
添加新附件
.
添加附件
取消
You are about to add
0
people
to the discussion. Proceed with caution.
先完成此消息的编辑!
取消
想要评论请
注册
或
登录