Skip to content
体验新版
项目
组织
正在加载...
登录
切换导航
打开侧边栏
qq_34031325
engine
提交
f38913b7
E
engine
项目概览
qq_34031325
/
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,发现更多精彩内容 >>
未验证
提交
f38913b7
编写于
8月 26, 2019
作者:
Y
Yegor
提交者:
GitHub
8月 26, 2019
浏览文件
操作
浏览文件
下载
电子邮件补丁
差异文件
sync Flutter Web engine to the latest (#11421)
* sync Flutter Web engine to the latest
上级
82fcf325
变更
37
隐藏空白更改
内联
并排
Showing
37 changed file
with
802 addition
and
219 deletion
+802
-219
lib/web_ui/lib/src/engine/bitmap_canvas.dart
lib/web_ui/lib/src/engine/bitmap_canvas.dart
+20
-8
lib/web_ui/lib/src/engine/browser_detection.dart
lib/web_ui/lib/src/engine/browser_detection.dart
+54
-0
lib/web_ui/lib/src/engine/compositor/fonts.dart
lib/web_ui/lib/src/engine/compositor/fonts.dart
+1
-0
lib/web_ui/lib/src/engine/compositor/image.dart
lib/web_ui/lib/src/engine/compositor/image.dart
+1
-0
lib/web_ui/lib/src/engine/compositor/layer.dart
lib/web_ui/lib/src/engine/compositor/layer.dart
+8
-8
lib/web_ui/lib/src/engine/compositor/util.dart
lib/web_ui/lib/src/engine/compositor/util.dart
+2
-1
lib/web_ui/lib/src/engine/dom_renderer.dart
lib/web_ui/lib/src/engine/dom_renderer.dart
+15
-0
lib/web_ui/lib/src/engine/onscreen_logging.dart
lib/web_ui/lib/src/engine/onscreen_logging.dart
+9
-3
lib/web_ui/lib/src/engine/platform_views.dart
lib/web_ui/lib/src/engine/platform_views.dart
+1
-1
lib/web_ui/lib/src/engine/pointer_binding.dart
lib/web_ui/lib/src/engine/pointer_binding.dart
+5
-0
lib/web_ui/lib/src/engine/recording_canvas.dart
lib/web_ui/lib/src/engine/recording_canvas.dart
+12
-9
lib/web_ui/lib/src/engine/semantics/text_field.dart
lib/web_ui/lib/src/engine/semantics/text_field.dart
+1
-0
lib/web_ui/lib/src/engine/services/buffers.dart
lib/web_ui/lib/src/engine/services/buffers.dart
+4
-2
lib/web_ui/lib/src/engine/services/message_codecs.dart
lib/web_ui/lib/src/engine/services/message_codecs.dart
+16
-6
lib/web_ui/lib/src/engine/shader.dart
lib/web_ui/lib/src/engine/shader.dart
+5
-4
lib/web_ui/lib/src/engine/surface/backdrop_filter.dart
lib/web_ui/lib/src/engine/surface/backdrop_filter.dart
+6
-4
lib/web_ui/lib/src/engine/surface/clip.dart
lib/web_ui/lib/src/engine/surface/clip.dart
+34
-17
lib/web_ui/lib/src/engine/surface/offset.dart
lib/web_ui/lib/src/engine/surface/offset.dart
+6
-1
lib/web_ui/lib/src/engine/surface/opacity.dart
lib/web_ui/lib/src/engine/surface/opacity.dart
+6
-2
lib/web_ui/lib/src/engine/surface/picture.dart
lib/web_ui/lib/src/engine/surface/picture.dart
+50
-31
lib/web_ui/lib/src/engine/surface/platform_view.dart
lib/web_ui/lib/src/engine/surface/platform_view.dart
+3
-0
lib/web_ui/lib/src/engine/surface/scene.dart
lib/web_ui/lib/src/engine/surface/scene.dart
+6
-1
lib/web_ui/lib/src/engine/surface/surface.dart
lib/web_ui/lib/src/engine/surface/surface.dart
+15
-4
lib/web_ui/lib/src/engine/surface/transform.dart
lib/web_ui/lib/src/engine/surface/transform.dart
+9
-1
lib/web_ui/lib/src/engine/text/measurement.dart
lib/web_ui/lib/src/engine/text/measurement.dart
+26
-2
lib/web_ui/lib/src/engine/text/paragraph.dart
lib/web_ui/lib/src/engine/text/paragraph.dart
+22
-9
lib/web_ui/lib/src/engine/text/ruler.dart
lib/web_ui/lib/src/engine/text/ruler.dart
+12
-3
lib/web_ui/lib/src/engine/text_editing.dart
lib/web_ui/lib/src/engine/text_editing.dart
+224
-43
lib/web_ui/lib/src/engine/util.dart
lib/web_ui/lib/src/engine/util.dart
+40
-34
lib/web_ui/lib/src/ui/painting.dart
lib/web_ui/lib/src/ui/painting.dart
+5
-1
lib/web_ui/lib/src/ui/text.dart
lib/web_ui/lib/src/ui/text.dart
+8
-0
lib/web_ui/lib/src/ui/window.dart
lib/web_ui/lib/src/ui/window.dart
+24
-8
lib/web_ui/test/compositing_test.dart
lib/web_ui/test/compositing_test.dart
+3
-0
lib/web_ui/test/engine/semantics/accessibility_test.dart
lib/web_ui/test/engine/semantics/accessibility_test.dart
+1
-1
lib/web_ui/test/text_editing_test.dart
lib/web_ui/test/text_editing_test.dart
+100
-6
lib/web_ui/test/text_test.dart
lib/web_ui/test/text_test.dart
+29
-0
lib/web_ui/tool/unicode_sync_script.dart
lib/web_ui/tool/unicode_sync_script.dart
+19
-9
未找到文件。
lib/web_ui/lib/src/engine/bitmap_canvas.dart
浏览文件 @
f38913b7
...
...
@@ -141,7 +141,15 @@ class BitmapCanvas extends EngineCanvas with SaveStackTracking {
if
(
_ctx
!=
null
)
{
_ctx
.
restore
();
_ctx
.
clearRect
(
0
,
0
,
_widthInBitmapPixels
,
_heightInBitmapPixels
);
_ctx
.
font
=
''
;
try
{
_ctx
.
font
=
''
;
}
catch
(
e
)
{
// Firefox may explode here:
// https://bugzilla.mozilla.org/show_bug.cgi?id=941146
if
(!
_isNsErrorFailureException
(
e
))
{
rethrow
;
}
}
_initializeViewport
();
}
if
(
_canvas
!=
null
)
{
...
...
@@ -450,12 +458,10 @@ class BitmapCanvas extends EngineCanvas with SaveStackTracking {
_strokeOrFill
(
paint
);
}
void
_drawRRectPath
(
ui
.
RRect
rrect
,
{
bool
startNewPath
=
true
})
{
// TODO(mdebbar): there's a bug in this code, it doesn't correctly handle
// the case when the radius is greater than the width of the
// rect. When we fix that in houdini_painter.js, we need to
// fix it here too.
// To draw the rounded rectangle, perform the following 8 steps:
void
_drawRRectPath
(
ui
.
RRect
inputRRect
,
{
bool
startNewPath
=
true
})
{
// TODO(mdebbar): Backport the overlapping corners fix to houdini_painter.js
// To draw the rounded rectangle, perform the following steps:
// 0. Ensure border radius don't overlap
// 1. Flip left,right top,bottom since web doesn't support flipped
// coordinates with negative radii.
// 2. draw the line for the top
...
...
@@ -471,6 +477,9 @@ class BitmapCanvas extends EngineCanvas with SaveStackTracking {
// rounded rectangle (after the corner).
// TODO(het): Confirm that this is the end point in Flutter for RRect
// Ensure border radius curves never overlap
final
ui
.
RRect
rrect
=
inputRRect
.
scaleRadii
();
double
left
=
rrect
.
left
;
double
right
=
rrect
.
right
;
double
top
=
rrect
.
top
;
...
...
@@ -551,7 +560,10 @@ class BitmapCanvas extends EngineCanvas with SaveStackTracking {
);
}
void
_drawRRectPathReverse
(
ui
.
RRect
rrect
,
{
bool
startNewPath
=
true
})
{
void
_drawRRectPathReverse
(
ui
.
RRect
inputRRect
,
{
bool
startNewPath
=
true
})
{
// Ensure border radius curves never overlap
final
ui
.
RRect
rrect
=
inputRRect
.
scaleRadii
();
double
left
=
rrect
.
left
;
double
right
=
rrect
.
right
;
double
top
=
rrect
.
top
;
...
...
lib/web_ui/lib/src/engine/browser_detection.dart
浏览文件 @
f38913b7
...
...
@@ -38,3 +38,57 @@ BrowserEngine _detectBrowserEngine() {
return
BrowserEngine
.
unknown
;
}
/// Operating system where the current browser runs.
///
/// Taken from the navigator platform.
/// <https://developer.mozilla.org/en-US/docs/Web/API/NavigatorID/platform>
enum
OperatingSystem
{
/// iOS: <http://www.apple.com/ios/>
iOs
,
/// Android: <https://www.android.com/>
android
,
/// Linux: <https://www.linux.org/>
linux
,
/// Windows: <https://www.microsoft.com/windows/>
windows
,
/// MacOs: <https://www.apple.com/macos/>
macOs
,
/// We were unable to detect the current operating system.
unknown
,
}
/// Lazily initialized current operating system.
OperatingSystem
_operatingSystem
;
/// Returns the [OperatingSystem] the current browsers works on.
///
/// This is used to implement operating system specific behavior such as
/// soft keyboards.
OperatingSystem
get
operatingSystem
=>
_operatingSystem
??=
_detectOperatingSystem
();
OperatingSystem
_detectOperatingSystem
(
)
{
final
String
platform
=
html
.
window
.
navigator
.
platform
;
if
(
platform
.
startsWith
(
'Mac'
))
{
return
OperatingSystem
.
macOs
;
}
else
if
(
platform
.
toLowerCase
().
contains
(
'iphone'
)
||
platform
.
toLowerCase
().
contains
(
'ipad'
)
||
platform
.
toLowerCase
().
contains
(
'ipod'
))
{
return
OperatingSystem
.
iOs
;
}
else
if
(
platform
.
toLowerCase
().
contains
(
'android'
))
{
return
OperatingSystem
.
android
;
}
else
if
(
platform
.
startsWith
(
'Linux'
))
{
return
OperatingSystem
.
linux
;
}
else
if
(
platform
.
startsWith
(
'Win'
))
{
return
OperatingSystem
.
windows
;
}
else
{
return
OperatingSystem
.
unknown
;
}
}
lib/web_ui/lib/src/engine/compositor/fonts.dart
浏览文件 @
f38913b7
// 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.
part of
engine
;
class
SkiaFontCollection
{
...
...
lib/web_ui/lib/src/engine/compositor/image.dart
浏览文件 @
f38913b7
// 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.
part of
engine
;
/// Instantiates a [ui.Codec] backed by an `SkImage` from Skia.
...
...
lib/web_ui/lib/src/engine/compositor/layer.dart
浏览文件 @
f38913b7
...
...
@@ -181,7 +181,7 @@ class OpacityLayer extends ContainerLayer implements ui.OpacityEngineLayer {
@override
void
preroll
(
PrerollContext
prerollContext
,
Matrix4
matrix
)
{
Matrix4
childMatrix
=
Matrix4
.
copy
(
matrix
);
final
Matrix4
childMatrix
=
Matrix4
.
copy
(
matrix
);
childMatrix
.
translate
(
_offset
.
dx
,
_offset
.
dy
);
final
ui
.
Rect
childPaintBounds
=
prerollChildren
(
prerollContext
,
childMatrix
);
...
...
@@ -189,22 +189,22 @@ class OpacityLayer extends ContainerLayer implements ui.OpacityEngineLayer {
}
@override
void
paint
(
PaintContext
c
ontext
)
{
void
paint
(
PaintContext
paintC
ontext
)
{
assert
(
needsPainting
);
final
ui
.
Paint
paint
=
ui
.
Paint
();
paint
.
color
=
ui
.
Color
.
fromARGB
(
_alpha
,
0
,
0
,
0
);
c
ontext
.
canvas
.
save
();
c
ontext
.
canvas
.
translate
(
_offset
.
dx
,
_offset
.
dy
);
paintC
ontext
.
canvas
.
save
();
paintC
ontext
.
canvas
.
translate
(
_offset
.
dx
,
_offset
.
dy
);
final
ui
.
Rect
saveLayerBounds
=
paintBounds
.
shift
(-
_offset
);
c
ontext
.
canvas
.
saveLayer
(
saveLayerBounds
,
paint
);
paintChildren
(
c
ontext
);
paintC
ontext
.
canvas
.
saveLayer
(
saveLayerBounds
,
paint
);
paintChildren
(
paintC
ontext
);
// Restore twice: once for the translate and once for the saveLayer.
c
ontext
.
canvas
.
restore
();
c
ontext
.
canvas
.
restore
();
paintC
ontext
.
canvas
.
restore
();
paintC
ontext
.
canvas
.
restore
();
}
}
...
...
lib/web_ui/lib/src/engine/compositor/util.dart
浏览文件 @
f38913b7
// 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.
part of
engine
;
js
.
JsObject
makeSkRect
(
ui
.
Rect
rect
)
{
...
...
@@ -9,7 +10,7 @@ js.JsObject makeSkRect(ui.Rect rect) {
}
js
.
JsArray
<
double
>
makeSkPoint
(
ui
.
Offset
point
)
{
js
.
JsArray
<
double
>
skPoint
=
new
js
.
JsArray
<
double
>();
final
js
.
JsArray
<
double
>
skPoint
=
js
.
JsArray
<
double
>();
skPoint
.
length
=
2
;
skPoint
[
0
]
=
point
.
dx
;
skPoint
[
1
]
=
point
.
dy
;
...
...
lib/web_ui/lib/src/engine/dom_renderer.dart
浏览文件 @
f38913b7
...
...
@@ -309,6 +309,21 @@ flt-glass-pane * {
setElementStyle
(
bodyElement
,
'font'
,
defaultCssFont
);
setElementStyle
(
bodyElement
,
'color'
,
'red'
);
// TODO(flutter_web): send the location during the scroll for more frequent
// location updates from the framework. Remove spellcheck=false property.
/// The spell check is being disabled for now.
///
/// Flutter web is positioning the input box on top of editable widget.
/// This location is updated only in the paint phase of the widget.
/// It is wrong during the scroll. It is not important for text editing
/// since the content is already invisible. On the other hand, the red
/// indicator for spellcheck gets confusing due to the wrong positioning.
/// We are disabling spellcheck until the location starts getting updated
/// via scroll. This is possible since we can listen to the scroll on
/// Flutter.
/// See [HybridTextEditing].
bodyElement
.
spellcheck
=
false
;
for
(
html
.
Element
viewportMeta
in
html
.
document
.
head
.
querySelectorAll
(
'meta[name="viewport"]'
))
{
if
(
assertionsEnabled
)
{
...
...
lib/web_ui/lib/src/engine/onscreen_logging.dart
浏览文件 @
f38913b7
...
...
@@ -86,10 +86,14 @@ void _initialize() {
///
/// The `label` argument, if present, will be printed before the stack.
void
debugPrintStack
(
{
String
label
,
int
maxFrames
})
{
if
(
label
!=
null
)
print
(
label
);
if
(
label
!=
null
)
{
print
(
label
);
}
Iterable
<
String
>
lines
=
StackTrace
.
current
.
toString
().
trimRight
().
split
(
'
\n
'
);
if
(
maxFrames
!=
null
)
lines
=
lines
.
take
(
maxFrames
);
if
(
maxFrames
!=
null
)
{
lines
=
lines
.
take
(
maxFrames
);
}
print
(
defaultStackFilter
(
lines
).
join
(
'
\n
'
));
}
...
...
@@ -132,7 +136,9 @@ Iterable<String> defaultStackFilter(Iterable<String> frames) {
result
.
add
(
'(elided one frame from
${skipped.single}
)'
);
}
else
if
(
skipped
.
length
>
1
)
{
final
List
<
String
>
where
=
Set
<
String
>.
from
(
skipped
).
toList
()..
sort
();
if
(
where
.
length
>
1
)
where
[
where
.
length
-
1
]
=
'and
${where.last}
'
;
if
(
where
.
length
>
1
)
{
where
[
where
.
length
-
1
]
=
'and
${where.last}
'
;
}
if
(
where
.
length
>
2
)
{
result
.
add
(
'(elided
${skipped.length}
frames from
${where.join(", ")}
)'
);
}
else
{
...
...
lib/web_ui/lib/src/engine/platform_views.dart
浏览文件 @
f38913b7
...
...
@@ -56,7 +56,7 @@ void handlePlatformViewCall(
void
_createPlatformView
(
MethodCall
methodCall
,
ui
.
PlatformMessageResponseCallback
callback
)
{
final
Map
args
=
methodCall
.
arguments
;
final
Map
<
dynamic
,
dynamic
>
args
=
methodCall
.
arguments
;
final
int
id
=
args
[
'id'
];
final
String
viewType
=
args
[
'viewType'
];
// TODO(het): Use 'direction', 'width', and 'height'.
...
...
lib/web_ui/lib/src/engine/pointer_binding.dart
浏览文件 @
f38913b7
...
...
@@ -323,6 +323,11 @@ class TouchAdapter extends BaseAdapter {
event
.
preventDefault
();
_updateButtonDownState
(
_kPrimaryMouseButton
,
false
);
_callback
(
_convertEventToPointerData
(
ui
.
PointerChange
.
up
,
event
));
if
(
textEditing
.
needsKeyboard
&&
browserEngine
==
BrowserEngine
.
webkit
&&
operatingSystem
==
OperatingSystem
.
iOs
)
{
textEditing
.
editingElement
.
configureInputElementForIOS
();
}
});
_addEventListener
(
'touchcancel'
,
(
html
.
Event
event
)
{
...
...
lib/web_ui/lib/src/engine/recording_canvas.dart
浏览文件 @
f38913b7
...
...
@@ -61,8 +61,16 @@ class RecordingCanvas {
debugBuf
.
writeln
(
'--- End of command stream'
);
print
(
debugBuf
);
}
else
{
for
(
int
i
=
0
;
i
<
_commands
.
length
;
i
++)
{
_commands
[
i
].
apply
(
engineCanvas
);
try
{
for
(
int
i
=
0
;
i
<
_commands
.
length
;
i
++)
{
_commands
[
i
].
apply
(
engineCanvas
);
}
}
catch
(
e
)
{
// commands should never fail, but...
// https://bugzilla.mozilla.org/show_bug.cgi?id=941146
if
(!
_isNsErrorFailureException
(
e
))
{
rethrow
;
}
}
}
}
...
...
@@ -1446,13 +1454,8 @@ class _PaintBounds {
double
transformedPointBottom
=
bottom
;
if
(!
_currentMatrixIsIdentity
)
{
final
ui
.
Rect
transformedRect
=
localClipToGlobalClip
(
localLeft:
left
,
localTop:
top
,
localRight:
right
,
localBottom:
bottom
,
transform:
_currentMatrix
,
);
final
ui
.
Rect
transformedRect
=
transformLTRB
(
_currentMatrix
,
left
,
top
,
right
,
bottom
);
transformedPointLeft
=
transformedRect
.
left
;
transformedPointTop
=
transformedRect
.
top
;
transformedPointRight
=
transformedRect
.
right
;
...
...
lib/web_ui/lib/src/engine/semantics/text_field.dart
浏览文件 @
f38913b7
...
...
@@ -20,6 +20,7 @@ class TextField extends RoleManager {
?
html
.
TextAreaElement
()
:
html
.
InputElement
();
persistentTextEditingElement
=
PersistentTextEditingElement
(
textEditing
,
editableDomElement
,
onDomElementSwap:
_setupDomElement
,
);
...
...
lib/web_ui/lib/src/engine/services/buffers.dart
浏览文件 @
f38913b7
...
...
@@ -253,8 +253,10 @@ abstract class _TypedDataBuffer<E> extends ListBase<E> {
///
/// Grows the buffer if necessary, preserving existing data.
void
_ensureCapacity
(
int
requiredCapacity
)
{
if
(
requiredCapacity
<=
_buffer
.
length
)
return
;
var
newBuffer
=
_createBiggerBuffer
(
requiredCapacity
);
if
(
requiredCapacity
<=
_buffer
.
length
)
{
return
;
}
final
List
<
E
>
newBuffer
=
_createBiggerBuffer
(
requiredCapacity
);
newBuffer
.
setRange
(
0
,
_length
,
_buffer
);
_buffer
=
newBuffer
;
}
...
...
lib/web_ui/lib/src/engine/services/message_codecs.dart
浏览文件 @
f38913b7
...
...
@@ -271,7 +271,9 @@ class StandardMessageCodec implements MessageCodec<dynamic> {
@override
ByteData
encodeMessage
(
dynamic
message
)
{
if
(
message
==
null
)
return
null
;
if
(
message
==
null
)
{
return
null
;
}
final
WriteBuffer
buffer
=
WriteBuffer
();
writeValue
(
buffer
,
message
);
return
buffer
.
done
();
...
...
@@ -279,10 +281,14 @@ class StandardMessageCodec implements MessageCodec<dynamic> {
@override
dynamic
decodeMessage
(
ByteData
message
)
{
if
(
message
==
null
)
return
null
;
if
(
message
==
null
)
{
return
null
;
}
final
ReadBuffer
buffer
=
ReadBuffer
(
message
);
final
dynamic
result
=
readValue
(
buffer
);
if
(
buffer
.
hasRemaining
)
throw
const
FormatException
(
'Message corrupted'
);
if
(
buffer
.
hasRemaining
)
{
throw
const
FormatException
(
'Message corrupted'
);
}
return
result
;
}
...
...
@@ -350,7 +356,7 @@ class StandardMessageCodec implements MessageCodec<dynamic> {
writeValue
(
buffer
,
value
);
});
}
else
{
throw
new
ArgumentError
.
value
(
value
);
throw
ArgumentError
.
value
(
value
);
}
}
...
...
@@ -359,7 +365,9 @@ class StandardMessageCodec implements MessageCodec<dynamic> {
/// This method is intended for use by subclasses overriding
/// [readValueOfType].
dynamic
readValue
(
ReadBuffer
buffer
)
{
if
(!
buffer
.
hasRemaining
)
throw
const
FormatException
(
'Message corrupted'
);
if
(!
buffer
.
hasRemaining
)
{
throw
const
FormatException
(
'Message corrupted'
);
}
final
int
type
=
buffer
.
getUint8
();
return
readValueOfType
(
type
,
buffer
);
}
...
...
@@ -543,7 +551,9 @@ class StandardMethodCodec implements MethodCodec {
if
(
envelope
.
lengthInBytes
==
0
)
throw
const
FormatException
(
'Expected envelope, got nothing'
);
final
ReadBuffer
buffer
=
ReadBuffer
(
envelope
);
if
(
buffer
.
getUint8
()
==
0
)
return
messageCodec
.
readValue
(
buffer
);
if
(
buffer
.
getUint8
()
==
0
)
{
return
messageCodec
.
readValue
(
buffer
);
}
final
dynamic
errorCode
=
messageCodec
.
readValue
(
buffer
);
final
dynamic
errorMessage
=
messageCodec
.
readValue
(
buffer
);
final
dynamic
errorDetails
=
messageCodec
.
readValue
(
buffer
);
...
...
lib/web_ui/lib/src/engine/shader.dart
浏览文件 @
f38913b7
// 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.
part of
engine
;
bool
_offsetIsValid
(
ui
.
Offset
offset
)
{
...
...
@@ -46,7 +47,7 @@ class GradientSweep extends EngineGradient {
}
@override
Object
createPaintStyle
(
_
)
{
Object
createPaintStyle
(
html
.
CanvasRenderingContext2D
ctx
)
{
throw
UnimplementedError
();
}
...
...
@@ -135,7 +136,7 @@ class GradientLinear extends EngineGradient {
js
.
JsObject
createSkiaShader
()
{
assert
(
experimentalUseSkia
);
js
.
JsArray
<
num
>
jsColors
=
js
.
JsArray
<
num
>();
final
js
.
JsArray
<
num
>
jsColors
=
js
.
JsArray
<
num
>();
jsColors
.
length
=
colors
.
length
;
for
(
int
i
=
0
;
i
<
colors
.
length
;
i
++)
{
jsColors
[
i
]
=
colors
[
i
].
value
;
...
...
@@ -174,7 +175,7 @@ class GradientRadial extends EngineGradient {
final
Float64List
matrix4
;
@override
Object
createPaintStyle
(
_
)
{
Object
createPaintStyle
(
html
.
CanvasRenderingContext2D
ctx
)
{
throw
UnimplementedError
();
}
...
...
@@ -199,7 +200,7 @@ class GradientConical extends EngineGradient {
final
Float64List
matrix4
;
@override
Object
createPaintStyle
(
_
)
{
Object
createPaintStyle
(
html
.
CanvasRenderingContext2D
ctx
)
{
throw
UnimplementedError
();
}
...
...
lib/web_ui/lib/src/engine/surface/backdrop_filter.dart
浏览文件 @
f38913b7
...
...
@@ -24,6 +24,10 @@ class PersistedBackdropFilter extends PersistedContainerSurface
// Reference to transform last used to cache [_invertedTransform].
Matrix4
_previousTransform
;
@override
Matrix4
get
localTransformInverse
=>
_localTransformInverse
??=
Matrix4
.
identity
();
@override
void
adoptElements
(
PersistedBackdropFilter
oldSurface
)
{
super
.
adoptElements
(
oldSurface
);
...
...
@@ -64,10 +68,8 @@ class PersistedBackdropFilter extends PersistedContainerSurface
_invertedTransform
=
Matrix4
.
inverted
(
_transform
);
_previousTransform
=
_transform
;
}
final
ui
.
Rect
rect
=
localClipRectToGlobalClip
(
localClip:
ui
.
Rect
.
fromLTRB
(
0
,
0
,
ui
.
window
.
physicalSize
.
width
,
ui
.
window
.
physicalSize
.
height
),
transform:
_invertedTransform
);
final
ui
.
Rect
rect
=
transformLTRB
(
_invertedTransform
,
0
,
0
,
ui
.
window
.
physicalSize
.
width
,
ui
.
window
.
physicalSize
.
height
);
final
html
.
CssStyleDeclaration
filterElementStyle
=
_filterElement
.
style
;
filterElementStyle
..
position
=
'absolute'
...
...
lib/web_ui/lib/src/engine/surface/clip.dart
浏览文件 @
f38913b7
...
...
@@ -66,12 +66,15 @@ class PersistedClipRect extends PersistedContainerSurface
@override
void
recomputeTransformAndClip
()
{
_transform
=
parent
.
_transform
;
_globalClip
=
parent
.
_globalClip
.
intersect
(
localClipRectToGlobalClip
(
localClip:
rect
,
transform:
_transform
,
));
_localClipBounds
=
rect
;
_localTransformInverse
=
null
;
_projectedClip
=
null
;
}
@override
Matrix4
get
localTransformInverse
=>
_localTransformInverse
??=
Matrix4
.
identity
();
@override
html
.
Element
createElement
()
{
return
super
.
createElement
()..
setAttribute
(
'clip-type'
,
'rect'
);
...
...
@@ -114,12 +117,15 @@ class PersistedClipRRect extends PersistedContainerSurface
@override
void
recomputeTransformAndClip
()
{
_transform
=
parent
.
_transform
;
_globalClip
=
parent
.
_globalClip
.
intersect
(
localClipRectToGlobalClip
(
localClip:
rrect
.
outerRect
,
transform:
_transform
,
));
_localClipBounds
=
rrect
.
outerRect
;
_localTransformInverse
=
null
;
_projectedClip
=
null
;
}
@override
Matrix4
get
localTransformInverse
=>
_localTransformInverse
??=
Matrix4
.
identity
();
@override
html
.
Element
createElement
()
{
return
super
.
createElement
()..
setAttribute
(
'clip-type'
,
'rrect'
);
...
...
@@ -174,23 +180,23 @@ class PersistedPhysicalShape extends PersistedContainerSurface
final
ui
.
RRect
roundRect
=
path
.
webOnlyPathAsRoundedRect
;
if
(
roundRect
!=
null
)
{
_globalClip
=
parent
.
_globalClip
.
intersect
(
localClipRectToGlobalClip
(
localClip:
roundRect
.
outerRect
,
transform:
transform
,
));
_localClipBounds
=
roundRect
.
outerRect
;
}
else
{
final
ui
.
Rect
rect
=
path
.
webOnlyPathAsRect
;
if
(
rect
!=
null
)
{
_globalClip
=
parent
.
_globalClip
.
intersect
(
localClipRectToGlobalClip
(
localClip:
rect
,
transform:
transform
,
));
_localClipBounds
=
rect
;
}
else
{
_
globalClip
=
parent
.
_globalClip
;
_
localClipBounds
=
null
;
}
}
_localTransformInverse
=
null
;
_projectedClip
=
null
;
}
@override
Matrix4
get
localTransformInverse
=>
_localTransformInverse
??=
Matrix4
.
identity
();
void
_applyColor
()
{
rootElement
.
style
.
backgroundColor
=
color
.
toCssString
();
}
...
...
@@ -338,6 +344,16 @@ class PersistedClipPath extends PersistedContainerSurface
return
defaultCreateElement
(
'flt-clippath'
);
}
@override
void
recomputeTransformAndClip
()
{
super
.
recomputeTransformAndClip
();
_localClipBounds
??=
clipPath
.
getBounds
();
}
@override
Matrix4
get
localTransformInverse
=>
_localTransformInverse
??=
Matrix4
.
identity
();
@override
void
apply
()
{
if
(
clipPath
==
null
)
{
...
...
@@ -364,6 +380,7 @@ class PersistedClipPath extends PersistedContainerSurface
void
update
(
PersistedClipPath
oldSurface
)
{
super
.
update
(
oldSurface
);
if
(
oldSurface
.
clipPath
!=
clipPath
)
{
_localClipBounds
=
null
;
oldSurface
.
_clipElement
?.
remove
();
apply
();
}
else
{
...
...
lib/web_ui/lib/src/engine/surface/offset.dart
浏览文件 @
f38913b7
...
...
@@ -22,9 +22,14 @@ class PersistedOffset extends PersistedContainerSurface
_transform
=
_transform
.
clone
();
_transform
.
translate
(
dx
,
dy
);
}
_globalClip
=
parent
.
_globalClip
;
_projectedClip
=
null
;
_localTransformInverse
=
null
;
}
@override
Matrix4
get
localTransformInverse
=>
_localTransformInverse
??=
Matrix4
.
translationValues
(-
dx
,
-
dy
,
0
);
@override
html
.
Element
createElement
()
{
return
defaultCreateElement
(
'flt-offset'
)..
style
.
transformOrigin
=
'0 0 0'
;
...
...
lib/web_ui/lib/src/engine/surface/opacity.dart
浏览文件 @
f38913b7
...
...
@@ -24,10 +24,14 @@ class PersistedOpacity extends PersistedContainerSurface
_transform
=
_transform
.
clone
();
_transform
.
translate
(
dx
,
dy
);
}
_
globalClip
=
parent
.
_globalClip
;
_localTransformInverse
=
null
;
_
projectedClip
=
null
;
}
@override
Matrix4
get
localTransformInverse
=>
_localTransformInverse
??=
Matrix4
.
translationValues
(-
offset
.
dx
,
-
offset
.
dy
,
0
);
@override
html
.
Element
createElement
()
{
return
defaultCreateElement
(
'flt-opacity'
)..
style
.
transformOrigin
=
'0 0 0'
;
...
...
lib/web_ui/lib/src/engine/surface/picture.dart
浏览文件 @
f38913b7
...
...
@@ -100,6 +100,10 @@ class PersistedHoudiniPicture extends PersistedPicture {
return
existingSurface
.
picture
==
picture
?
0.0
:
1.0
;
}
@override
Matrix4
get
localTransformInverse
=>
_localTransformInverse
??=
Matrix4
.
identity
();
static
void
_registerCssPainter
()
{
_cssPainterRegistered
=
true
;
final
dynamic
css
=
js_util
.
getProperty
(
html
.
window
,
'CSS'
);
...
...
@@ -186,6 +190,9 @@ class PersistedStandardPicture extends PersistedPicture {
}
}
@override
Matrix4
get
localTransformInverse
=>
null
;
@override
int
get
bitmapPixelCount
{
if
(
_canvas
is
!
BitmapCanvas
)
{
...
...
@@ -358,7 +365,6 @@ abstract class PersistedPicture extends PersistedLeafSurface {
_transform
=
_transform
.
clone
();
_transform
.
translate
(
dx
,
dy
);
}
_globalClip
=
parent
.
_globalClip
;
_computeExactCullRects
();
}
...
...
@@ -389,39 +395,52 @@ abstract class PersistedPicture extends PersistedLeafSurface {
void
_computeExactCullRects
()
{
assert
(
transform
!=
null
);
assert
(
localPaintBounds
!=
null
);
final
ui
.
Rect
globalPaintBounds
=
localClipRectToGlobalClip
(
localClip:
localPaintBounds
,
transform:
transform
);
// The exact cull rect required in screen coordinates.
ui
.
Rect
tightGlobalCullRect
=
globalPaintBounds
.
intersect
(
_globalClip
);
// The exact cull rect required in local coordinates.
ui
.
Rect
tightLocalCullRect
;
if
(
tightGlobalCullRect
.
width
<=
0
||
tightGlobalCullRect
.
height
<=
0
)
{
tightGlobalCullRect
=
ui
.
Rect
.
zero
;
tightLocalCullRect
=
ui
.
Rect
.
zero
;
}
else
{
final
Matrix4
invertedTransform
=
Matrix4
.
fromFloat64List
(
Float64List
(
16
));
// TODO(yjbanov): When we move to our own vector math library, rewrite
// this to check for the case of simple transform before
// inverting. Inversion of simple transforms can be made
// much cheaper.
final
double
det
=
invertedTransform
.
copyInverse
(
transform
);
if
(
det
==
0
)
{
// Determinant is zero, which means the transform is not invertible.
tightGlobalCullRect
=
ui
.
Rect
.
zero
;
tightLocalCullRect
=
ui
.
Rect
.
zero
;
}
else
{
tightLocalCullRect
=
localClipRectToGlobalClip
(
localClip:
tightGlobalCullRect
,
transform:
invertedTransform
);
if
(
parent
.
_projectedClip
==
null
)
{
// Compute and cache chain of clipping bounds on parent of picture since
// parent may include multiple pictures so it can be reused by all
// child pictures.
ui
.
Rect
bounds
;
PersistedSurface
parentSurface
=
parent
;
final
Matrix4
clipTransform
=
Matrix4
.
identity
();
while
(
parentSurface
!=
null
)
{
final
ui
.
Rect
localClipBounds
=
parentSurface
.
_localClipBounds
;
if
(
localClipBounds
!=
null
)
{
if
(
bounds
==
null
)
{
bounds
=
transformRect
(
clipTransform
,
localClipBounds
);
}
else
{
bounds
=
bounds
.
intersect
(
transformRect
(
clipTransform
,
localClipBounds
));
}
}
final
Matrix4
localInverse
=
parentSurface
.
localTransformInverse
;
if
(
localInverse
!=
null
&&
!
localInverse
.
isIdentity
())
{
clipTransform
.
multiply
(
localInverse
);
}
parentSurface
=
parentSurface
.
parent
;
}
if
(
bounds
!=
null
&&
(
bounds
.
width
<=
0
||
bounds
.
height
<=
0
))
{
bounds
=
ui
.
Rect
.
zero
;
}
// Cache projected clip on parent.
parent
.
_projectedClip
=
bounds
;
}
// Intersect localPaintBounds with parent projected clip to calculate
// and cache [_exactLocalCullRect].
if
(
parent
.
_projectedClip
==
null
)
{
_exactLocalCullRect
=
localPaintBounds
;
}
else
{
_exactLocalCullRect
=
localPaintBounds
.
intersect
(
parent
.
_projectedClip
);
}
if
(
_exactLocalCullRect
.
width
<=
0
||
_exactLocalCullRect
.
height
<=
0
)
{
_exactLocalCullRect
=
ui
.
Rect
.
zero
;
_exactGlobalCullRect
=
ui
.
Rect
.
zero
;
}
else
{
assert
(()
{
_exactGlobalCullRect
=
transformRect
(
transform
,
_exactLocalCullRect
);
return
true
;
}());
}
assert
(
tightLocalCullRect
!=
null
);
_exactLocalCullRect
=
tightLocalCullRect
;
_exactGlobalCullRect
=
tightGlobalCullRect
;
}
bool
_computeOptimalCullRect
(
PersistedPicture
oldSurface
)
{
...
...
lib/web_ui/lib/src/engine/surface/platform_view.dart
浏览文件 @
f38913b7
...
...
@@ -53,6 +53,9 @@ class PersistedPlatformView extends PersistedLeafSurface {
return
_hostElement
;
}
@override
Matrix4
get
localTransformInverse
=>
null
;
@override
void
apply
()
{
_hostElement
.
style
...
...
lib/web_ui/lib/src/engine/surface/scene.dart
浏览文件 @
f38913b7
...
...
@@ -18,9 +18,14 @@ class PersistedScene extends PersistedContainerSurface {
// update this code when we add add2app support.
final
double
screenWidth
=
html
.
window
.
innerWidth
.
toDouble
();
final
double
screenHeight
=
html
.
window
.
innerHeight
.
toDouble
();
_globalClip
=
ui
.
Rect
.
fromLTRB
(
0
,
0
,
screenWidth
,
screenHeight
);
_localClipBounds
=
ui
.
Rect
.
fromLTRB
(
0
,
0
,
screenWidth
,
screenHeight
);
_localTransformInverse
=
Matrix4
.
identity
();
_projectedClip
=
null
;
}
@override
Matrix4
get
localTransformInverse
=>
_localTransformInverse
;
@override
html
.
Element
createElement
()
{
return
defaultCreateElement
(
'flt-scene'
);
...
...
lib/web_ui/lib/src/engine/surface/surface.dart
浏览文件 @
f38913b7
...
...
@@ -799,8 +799,15 @@ abstract class PersistedSurface implements ui.EngineLayer {
/// the clip added by this layer (if any).
///
/// The value is update by [recomputeTransformAndClip].
ui
.
Rect
get
globalClip
=>
_globalClip
;
ui
.
Rect
_globalClip
;
ui
.
Rect
_projectedClip
;
/// Bounds of clipping performed by this layer.
ui
.
Rect
_localClipBounds
;
// Cached inverse of transform on this node. Unlike transform, this
// Matrix only contains local transform (not chain multiplied since root).
Matrix4
_localTransformInverse
;
Matrix4
get
localTransformInverse
;
/// Recomputes [transform] and [globalClip] fields.
///
...
...
@@ -812,7 +819,9 @@ abstract class PersistedSurface implements ui.EngineLayer {
@protected
void
recomputeTransformAndClip
()
{
_transform
=
parent
.
_transform
;
_globalClip
=
parent
.
_globalClip
;
_localClipBounds
=
null
;
_localTransformInverse
=
null
;
_projectedClip
=
null
;
}
/// Performs computations before [build], [update], or [retain] are called.
...
...
@@ -925,7 +934,9 @@ abstract class PersistedContainerSurface extends PersistedSurface {
@override
void
recomputeTransformAndClip
()
{
_transform
=
parent
.
_transform
;
_globalClip
=
parent
.
_globalClip
;
_localClipBounds
=
null
;
_localTransformInverse
=
null
;
_projectedClip
=
null
;
}
@override
...
...
lib/web_ui/lib/src/engine/surface/transform.dart
浏览文件 @
f38913b7
...
...
@@ -15,7 +15,15 @@ class PersistedTransform extends PersistedContainerSurface
@override
void
recomputeTransformAndClip
()
{
_transform
=
parent
.
_transform
.
multiplied
(
Matrix4
.
fromFloat64List
(
matrix4
));
_globalClip
=
parent
.
_globalClip
;
_localTransformInverse
=
null
;
_projectedClip
=
null
;
}
@override
Matrix4
get
localTransformInverse
{
_localTransformInverse
??=
Matrix4
.
tryInvert
(
Matrix4
.
fromFloat64List
(
matrix4
));
return
_localTransformInverse
;
}
@override
...
...
lib/web_ui/lib/src/engine/text/measurement.dart
浏览文件 @
f38913b7
...
...
@@ -82,12 +82,30 @@ class RulerManager {
_rulerHost
?.
remove
();
}
// Evicts all rulers from the cache.
void
_evictAllRulers
()
{
_rulers
.
forEach
((
ParagraphGeometricStyle
style
,
ParagraphRuler
ruler
)
{
ruler
.
dispose
();
});
_rulers
=
<
ParagraphGeometricStyle
,
ParagraphRuler
>{};
}
/// If [window._isPhysicalSizeActuallyEmpty], evicts all rulers from the cache.
/// If ruler cache size exceeds [rulerCacheCapacity], evicts those rulers that
/// were used the least.
///
/// Resets hit counts back to zero.
@visibleForTesting
void
cleanUpRulerCache
()
{
// Measurements performed (and cached) inside a hidden iframe (with
// display:none) are wrong.
// Evict all rulers, so text gets re-measured when the iframe becomes
// visible.
// see: https://github.com/flutter/flutter/issues/36341
if
(
window
.
physicalSize
.
isEmpty
)
{
_evictAllRulers
();
return
;
}
if
(
_rulers
.
length
>
rulerCacheCapacity
)
{
final
List
<
ParagraphRuler
>
sortedByUsage
=
_rulers
.
values
.
toList
();
sortedByUsage
.
sort
((
ParagraphRuler
a
,
ParagraphRuler
b
)
{
...
...
@@ -174,7 +192,13 @@ abstract class TextMeasurementService {
// TODO(flutter_web): https://github.com/flutter/flutter/issues/33523
// When the canvas-based implementation is complete and passes all the
// tests, get rid of [_experimentalEnableCanvasImplementation].
if
(
enableExperimentalCanvasImplementation
&&
// We need to check [window.physicalSize.isEmpty] because some canvas
// commands don't work as expected when they run inside a hidden iframe
// (with display:none)
// Skip using canvas measurements until the iframe becomes visible.
// see: https://github.com/flutter/flutter/issues/36341
if
(!
window
.
physicalSize
.
isEmpty
&&
enableExperimentalCanvasImplementation
&&
_canUseCanvasMeasurement
(
paragraph
))
{
return
canvasInstance
;
}
...
...
@@ -564,7 +588,7 @@ class CanvasTextMeasurementService extends TextMeasurementService {
ui
.
TextPosition
getTextPositionForOffset
(
EngineParagraph
paragraph
,
ui
.
ParagraphConstraints
constraints
,
ui
.
Offset
offset
)
{
// TODO(flutter_web): implement.
return
new
ui
.
TextPosition
(
offset:
0
);
return
const
ui
.
TextPosition
(
offset:
0
);
}
}
...
...
lib/web_ui/lib/src/engine/text/paragraph.dart
浏览文件 @
f38913b7
...
...
@@ -957,7 +957,7 @@ class EngineParagraphBuilder implements ui.ParagraphBuilder {
final
dynamic
op
=
_ops
[
i
];
if
(
op
is
EngineTextStyle
)
{
final
html
.
SpanElement
span
=
domRenderer
.
createElement
(
'span'
);
_applyTextStyleToElement
(
element:
span
,
style:
op
);
_applyTextStyleToElement
(
element:
span
,
style:
op
,
isSpan:
true
);
if
(
op
.
_background
!=
null
)
{
_applyTextBackgroundToElement
(
element:
span
,
style:
op
);
}
...
...
@@ -997,8 +997,11 @@ String fontWeightToCss(ui.FontWeight fontWeight) {
if
(
fontWeight
==
null
)
{
return
null
;
}
return
fontWeightIndexToCss
(
fontWeightIndex:
fontWeight
.
index
);
}
switch
(
fontWeight
.
index
)
{
String
fontWeightIndexToCss
(
{
int
fontWeightIndex
=
3
})
{
switch
(
fontWeightIndex
)
{
case
0
:
return
'100'
;
case
1
:
...
...
@@ -1021,7 +1024,7 @@ String fontWeightToCss(ui.FontWeight fontWeight) {
assert
(()
{
throw
AssertionError
(
'Failed to convert font weight
$fontWeight
to CSS.'
,
'Failed to convert font weight
$fontWeight
Index
to CSS.'
,
);
}());
...
...
@@ -1043,7 +1046,7 @@ void _applyParagraphStyleToElement({
final
html
.
CssStyleDeclaration
cssStyle
=
element
.
style
;
if
(
previousStyle
==
null
)
{
if
(
style
.
_textAlign
!=
null
)
{
cssStyle
.
textAlign
=
_
textAlignToCssValue
(
cssStyle
.
textAlign
=
textAlignToCssValue
(
style
.
_textAlign
,
style
.
_textDirection
??
ui
.
TextDirection
.
ltr
);
}
if
(
style
.
_lineHeight
!=
null
)
{
...
...
@@ -1067,7 +1070,7 @@ void _applyParagraphStyleToElement({
}
}
else
{
if
(
style
.
_textAlign
!=
previousStyle
.
_textAlign
)
{
cssStyle
.
textAlign
=
_
textAlignToCssValue
(
cssStyle
.
textAlign
=
textAlignToCssValue
(
style
.
_textAlign
,
style
.
_textDirection
??
ui
.
TextDirection
.
ltr
);
}
if
(
style
.
_lineHeight
!=
style
.
_lineHeight
)
{
...
...
@@ -1098,10 +1101,13 @@ void _applyParagraphStyleToElement({
/// corresponding CSS equivalents.
///
/// If [previousStyle] is not null, updates only the mismatching attributes.
/// If [isSpan] is true, the text element is a span within richtext and
/// should not assign effectiveFontFamily if fontFamily was not specified.
void
_applyTextStyleToElement
(
{
@required
html
.
HtmlElement
element
,
@required
EngineTextStyle
style
,
EngineTextStyle
previousStyle
,
bool
isSpan
=
false
,
})
{
assert
(
element
!=
null
);
assert
(
style
!=
null
);
...
...
@@ -1122,8 +1128,16 @@ void _applyTextStyleToElement({
cssStyle
.
fontStyle
=
style
.
_fontStyle
==
ui
.
FontStyle
.
normal
?
'normal'
:
'italic'
;
}
if
(
style
.
_effectiveFontFamily
!=
null
)
{
cssStyle
.
fontFamily
=
style
.
_effectiveFontFamily
;
// For test environment use effectiveFontFamily since we need to
// consistently use Ahem font.
if
(
isSpan
&&
!
ui
.
debugEmulateFlutterTesterEnvironment
)
{
if
(
style
.
_fontFamily
!=
null
)
{
cssStyle
.
fontFamily
=
style
.
_fontFamily
;
}
}
else
{
if
(
style
.
_effectiveFontFamily
!=
null
)
{
cssStyle
.
fontFamily
=
style
.
_effectiveFontFamily
;
}
}
if
(
style
.
_letterSpacing
!=
null
)
{
cssStyle
.
letterSpacing
=
'
${style._letterSpacing}
px'
;
...
...
@@ -1265,8 +1279,7 @@ String _textDirectionToCssValue(ui.TextDirection textDirection) {
/// ```css
/// text-align: right;
/// ```
String
_textAlignToCssValue
(
ui
.
TextAlign
align
,
ui
.
TextDirection
textDirection
)
{
String
textAlignToCssValue
(
ui
.
TextAlign
align
,
ui
.
TextDirection
textDirection
)
{
switch
(
align
)
{
case
ui
.
TextAlign
.
left
:
return
'left'
;
...
...
lib/web_ui/lib/src/engine/text/ruler.dart
浏览文件 @
f38913b7
...
...
@@ -191,7 +191,14 @@ class TextDimensions {
// match the style set on the `element`. Setting text as plain string is
// faster because it doesn't change the DOM structure or CSS attributes,
// and therefore doesn't trigger style recalculations in the browser.
_element
.
text
=
plainText
;
if
(
plainText
.
endsWith
(
'
\n
'
))
{
// On the web the last newline is ignored. To be consistent with
// native engine implementation we add extra newline to get correct
// height measurement.
_element
.
text
=
'
$plainText
\n
'
;
}
else
{
_element
.
text
=
plainText
;
}
}
else
{
// Rich text: deeply copy contents. This is the slow case that should be
// avoided if fast layout performance is desired.
...
...
@@ -425,8 +432,10 @@ class ParagraphRuler {
minIntrinsicDimensions
.
_element
.
style
..
flex
=
'0'
..
display
=
'inline'
// Preserve whitespaces.
..
whiteSpace
=
'pre-wrap'
;
// Preserve newlines, wrap text, remove end of line spaces.
// Not using pre-wrap here since end of line space hang measurement
// changed in Chrome 77 Beta.
..
whiteSpace
=
'pre-line'
;
_minIntrinsicHost
.
append
(
minIntrinsicDimensions
.
_element
);
rulerManager
.
addHostElement
(
_minIntrinsicHost
);
...
...
lib/web_ui/lib/src/engine/text_editing.dart
浏览文件 @
f38913b7
...
...
@@ -9,51 +9,38 @@ const bool _debugVisibleTextEditing = false;
void
_emptyCallback
(
dynamic
_
)
{}
void
_styleEditingElement
(
html
.
HtmlElement
domElement
)
{
/// These style attributes are constant throughout the life time of an input
/// element.
///
/// They are assigned once during the creation of the dom element.
void
_setStaticStyleAttributes
(
html
.
HtmlElement
domElement
)
{
domElement
.
style
..
position
=
'fixed'
..
whiteSpace
=
'pre'
;
..
whiteSpace
=
'pre'
..
alignContent
=
'center'
..
position
=
'absolute'
..
padding
=
'0'
..
opacity
=
'1'
;
if
(
_debugVisibleTextEditing
)
{
domElement
.
style
..
bottom
=
'0'
..
right
=
'0'
..
font
=
'24px sans-serif'
..
color
=
'purple'
..
backgroundColor
=
'pink'
;
}
else
{
domElement
.
style
..
overflow
=
'hidden'
..
transform
=
'translate(-99999px, -99999px)'
// width and height can't be zero because then the element would stop
// receiving edits when its content is empty.
..
width
=
'1px'
..
height
=
'1px'
;
}
if
(
browserEngine
==
BrowserEngine
.
webkit
)
{
// TODO(flutter_web): Remove once webkit issue of paragraphs incorrectly
// rendering (shifting up) is resolved. Temporarily force relayout
// a frame after input is created.
html
.
window
.
animationFrame
.
then
((
num
_
)
{
domElement
.
style
..
position
=
'absolute'
..
bottom
=
'0'
..
right
=
'0'
;
});
..
color
=
'transparent'
..
backgroundColor
=
'transparent'
..
background
=
'transparent'
..
border
=
'none'
..
resize
=
'none'
..
cursor
=
'none'
..
textShadow
=
'transparent'
..
outline
=
'none'
;
/// This property makes the cursor transparent in mobile browsers where
/// cursor = 'none' does not work.
domElement
.
style
.
setProperty
(
'caret-color'
,
'transparent'
);
}
}
html
.
InputElement
_createInputElement
(
)
{
final
html
.
InputElement
input
=
html
.
InputElement
();
_styleEditingElement
(
input
);
return
input
;
}
html
.
TextAreaElement
_createTextAreaElement
(
)
{
final
html
.
TextAreaElement
textarea
=
html
.
TextAreaElement
();
_styleEditingElement
(
textarea
);
return
textarea
;
}
/// The current text and selection state of a text field.
class
EditingState
{
EditingState
({
this
.
text
,
this
.
baseOffset
=
0
,
this
.
extentOffset
=
0
});
...
...
@@ -213,8 +200,9 @@ class TextEditingElement {
/// Creates a non-persistent [TextEditingElement].
///
/// See [TextEditingElement.persistent] to understand what persistent mode is.
TextEditingElement
();
TextEditingElement
(
this
.
owner
);
final
HybridTextEditing
owner
;
bool
_enabled
=
false
;
html
.
HtmlElement
domElement
;
...
...
@@ -230,6 +218,26 @@ class TextEditingElement {
return
type
;
}
/// On iOS, sets the location of the input element after focusing on it.
///
/// On iOS, keyboard causes scrolling in the UI. This scrolling does not
/// trigger an event. In order to position the input element correctly, it is
/// important we set it's final location after focusing on it (after keyboard
/// is up).
///
/// This method is called in the end of the 'touchend' event, therefore it is
/// called after the editing state is set.
void
configureInputElementForIOS
()
{
if
(
browserEngine
!=
BrowserEngine
.
webkit
||
operatingSystem
!=
OperatingSystem
.
iOs
)
{
// Only relevant on Safari.
return
;
}
if
(
domElement
!=
null
)
{
owner
.
setStyle
(
domElement
);
}
}
/// Enables the element so it can be used to edit text.
///
/// Register [callback] so that it gets invoked whenever any change occurs in
...
...
@@ -296,11 +304,11 @@ class TextEditingElement {
void
_initDomElement
(
InputConfiguration
inputConfig
)
{
switch
(
inputConfig
.
inputType
)
{
case
InputType
.
text
:
domElement
=
_
createInputElement
();
domElement
=
owner
.
createInputElement
();
break
;
case
InputType
.
multiline
:
domElement
=
_
createTextAreaElement
();
domElement
=
owner
.
createTextAreaElement
();
break
;
default
:
...
...
@@ -465,9 +473,11 @@ class PersistentTextEditingElement extends TextEditingElement {
/// [domElement] so the caller can insert it before calling
/// [PersistentTextEditingElement.enable].
PersistentTextEditingElement
(
HybridTextEditing
owner
,
html
.
HtmlElement
domElement
,
{
@required
html
.
VoidCallback
onDomElementSwap
,
})
:
_onDomElementSwap
=
onDomElementSwap
{
})
:
_onDomElementSwap
=
onDomElementSwap
,
super
(
owner
)
{
// Make sure the dom element is of a type that we support for text editing.
assert
(
_getTypeFromElement
(
domElement
)
!=
null
);
this
.
domElement
=
domElement
;
...
...
@@ -527,7 +537,12 @@ final HybridTextEditing textEditing = HybridTextEditing();
class
HybridTextEditing
{
/// The default HTML element used to manage editing state when a custom
/// element is not provided via [useCustomEditableElement].
TextEditingElement
_defaultEditingElement
=
TextEditingElement
();
TextEditingElement
_defaultEditingElement
;
/// Private constructor so this class can be a singleton.
HybridTextEditing
()
{
_defaultEditingElement
=
TextEditingElement
(
this
);
}
/// The HTML element used to manage editing state.
///
...
...
@@ -548,7 +563,7 @@ class HybridTextEditing {
/// Use [stopUsingCustomEditableElement] to switch back to default element.
void
useCustomEditableElement
(
TextEditingElement
customEditingElement
)
{
if
(
_isEditing
&&
customEditingElement
!=
_customEditingElement
)
{
_
stopEditing
();
stopEditing
();
}
_customEditingElement
=
customEditingElement
;
}
...
...
@@ -560,7 +575,15 @@ class HybridTextEditing {
}
int
_clientId
;
/// Flag which shows if there is an ongoing editing.
///
/// Also used to define if a keyboard is needed.
bool
_isEditing
=
false
;
/// Flag indicating if the flutter framework requested a keyboard.
bool
get
needsKeyboard
=>
_isEditing
;
Map
<
String
,
dynamic
>
_configuration
;
/// All "flutter/textinput" platform messages should be sent to this method.
...
...
@@ -568,6 +591,11 @@ class HybridTextEditing {
final
MethodCall
call
=
const
JSONMethodCodec
().
decodeMethodCall
(
data
);
switch
(
call
.
method
)
{
case
'TextInput.setClient'
:
final
bool
clientIdChanged
=
_clientId
!=
null
&&
_clientId
!=
call
.
arguments
[
0
];
if
(
clientIdChanged
&&
_isEditing
)
{
stopEditing
();
}
_clientId
=
call
.
arguments
[
0
];
_configuration
=
call
.
arguments
[
1
];
break
;
...
...
@@ -583,10 +611,18 @@ class HybridTextEditing {
}
break
;
case
'TextInput.setEditingLocationSize'
:
_setLocation
(
call
.
arguments
);
break
;
case
'TextInput.setStyle'
:
_setFontStyle
(
call
.
arguments
);
break
;
case
'TextInput.clearClient'
:
case
'TextInput.hide'
:
if
(
_isEditing
)
{
_
stopEditing
();
stopEditing
();
}
break
;
}
...
...
@@ -601,12 +637,66 @@ class HybridTextEditing {
);
}
void
_
stopEditing
()
{
void
stopEditing
()
{
assert
(
_isEditing
);
_isEditing
=
false
;
editingElement
.
disable
();
}
_EditingStyle
_editingStyle
;
_EditingStyle
get
editingStyle
=>
_editingStyle
;
/// Use the font size received from Flutter if set.
String
font
()
{
assert
(
_editingStyle
!=
null
);
return
'
${_editingStyle.fontWeight}
${_editingStyle.fontSize}
px
${_editingStyle.fontFamily}
'
;
}
void
_setFontStyle
(
Map
<
String
,
dynamic
>
style
)
{
assert
(
style
.
containsKey
(
'fontSize'
));
assert
(
style
.
containsKey
(
'fontFamily'
));
assert
(
style
.
containsKey
(
'textAlignIndex'
));
final
int
textAlignIndex
=
style
.
remove
(
'textAlignIndex'
);
/// Converts integer value coming as fontWeightValue from TextInput.setStyle
/// to its CSS equivalent value.
/// Converts index of TextAlign to enum value.
_editingStyle
=
_EditingStyle
(
textDirection:
style
.
containsKey
(
'textDirection'
)
?
style
.
remove
(
'textDirection'
)
:
ui
.
TextDirection
.
ltr
,
fontSize:
style
.
remove
(
'fontSize'
),
textAlign:
ui
.
TextAlign
.
values
[
textAlignIndex
],
fontFamily:
style
.
remove
(
'fontFamily'
),
fontWeight:
fontWeightIndexToCss
(
fontWeightIndex:
style
.
remove
(
'fontWeightValue'
)),
);
}
/// Location of the editable text on the page as a rectangle.
// TODO(flutter_web): investigate if transform matrix can be used instead of
// a rectangle.
_EditingLocationAndSize
_editingLocationAndSize
;
_EditingLocationAndSize
get
editingLocationAndSize
=>
_editingLocationAndSize
;
void
_setLocation
(
Map
<
String
,
dynamic
>
editingLocationAndSize
)
{
assert
(
editingLocationAndSize
.
containsKey
(
'top'
));
assert
(
editingLocationAndSize
.
containsKey
(
'left'
));
assert
(
editingLocationAndSize
.
containsKey
(
'width'
));
assert
(
editingLocationAndSize
.
containsKey
(
'height'
));
_editingLocationAndSize
=
_EditingLocationAndSize
(
top:
editingLocationAndSize
.
remove
(
'top'
),
left:
editingLocationAndSize
.
remove
(
'left'
),
width:
editingLocationAndSize
.
remove
(
'width'
),
height:
editingLocationAndSize
.
remove
(
'height'
));
if
(
editingElement
.
domElement
!=
null
)
{
_setDynamicStyleAttributes
(
editingElement
.
domElement
);
}
}
void
_syncEditingStateToFlutter
(
EditingState
editingState
)
{
ui
.
window
.
onPlatformMessage
(
'flutter/textinput'
,
...
...
@@ -619,4 +709,95 @@ class HybridTextEditing {
_emptyCallback
,
);
}
/// These style attributes are dynamic throughout the life time of an input
/// element.
///
/// They are changed depending on the messages coming from method calls:
/// "TextInput.setStyle", "TextInput.setEditingLocationSize".
void
_setDynamicStyleAttributes
(
html
.
HtmlElement
domElement
)
{
if
(
_editingLocationAndSize
!=
null
&&
!(
browserEngine
==
BrowserEngine
.
webkit
&&
operatingSystem
==
OperatingSystem
.
iOs
))
{
setStyle
(
domElement
);
}
}
/// Set style to the native dom element used for text editing.
///
/// It will be located exactly in the same place with the editable widgets,
/// however it's contents and cursor will be invisible.
///
/// Users can interact with the element and use the functionalities of the
/// right-click menu. Such as copy,paste, cut, select, translate...
void
setStyle
(
html
.
HtmlElement
domElement
)
{
domElement
.
style
..
top
=
'
${_editingLocationAndSize.top}
px'
..
left
=
'
${_editingLocationAndSize.left}
px'
..
width
=
'
${_editingLocationAndSize.width}
px'
..
height
=
'
${_editingLocationAndSize.height}
px'
;
if
(
_debugVisibleTextEditing
)
{
domElement
.
style
.
font
=
'24px sans-serif'
;
}
else
{
domElement
.
style
..
textAlign
=
_editingStyle
.
align
..
font
=
font
();
}
}
html
.
InputElement
createInputElement
()
{
final
html
.
InputElement
input
=
html
.
InputElement
();
_setStaticStyleAttributes
(
input
);
_setDynamicStyleAttributes
(
input
);
return
input
;
}
html
.
TextAreaElement
createTextAreaElement
()
{
final
html
.
TextAreaElement
textarea
=
html
.
TextAreaElement
();
_setStaticStyleAttributes
(
textarea
);
_setDynamicStyleAttributes
(
textarea
);
return
textarea
;
}
}
/// Information on the font and alignment of a text editing element.
///
/// This information is received via TextInput.setStyle message.
class
_EditingStyle
{
_EditingStyle
({
@required
this
.
textDirection
,
@required
this
.
fontSize
,
@required
this
.
textAlign
,
@required
this
.
fontFamily
,
this
.
fontWeight
,
});
/// This information will be used for changing the style of the hidden input
/// element, which will match it's size to the size of the editable widget.
final
double
fontSize
;
final
String
fontWeight
;
final
String
fontFamily
;
final
ui
.
TextAlign
textAlign
;
final
ui
.
TextDirection
textDirection
;
String
get
align
=>
textAlignToCssValue
(
textAlign
,
textDirection
);
}
/// Information on the location and size of the editing element.
///
/// This information is received via "TextInput.setEditingLocationSize"
/// message. Framework currently sends this information on paint.
// TODO(flutter_web): send the location during the scroll for more frequent
// updates from the framework.
class
_EditingLocationAndSize
{
_EditingLocationAndSize
(
{
@required
this
.
top
,
@required
this
.
left
,
@required
this
.
width
,
@required
this
.
height
});
final
double
top
;
final
double
left
;
final
double
width
;
final
double
height
;
}
lib/web_ui/lib/src/engine/util.dart
浏览文件 @
f38913b7
...
...
@@ -110,38 +110,25 @@ bool get assertionsEnabled {
return
k
;
}
/// Converts a rectangular clip specified in local coordinates to screen
/// coordinates given the effective [transform].
/// Transforms a [ui.Rect] given the effective [transform].
///
/// The resulting
clip is a rectangle
aligned to the pixel grid, i.e. two of
/// The resulting
rect is
aligned to the pixel grid, i.e. two of
/// its sides are vertical and two are horizontal. In the presence of rotations
/// the rectangle is inflated such that it fits the rotated rectangle.
ui
.
Rect
localClipRectToGlobalClip
(
{
ui
.
Rect
localClip
,
Matrix4
transform
})
{
return
localClipToGlobalClip
(
localLeft:
localClip
.
left
,
localTop:
localClip
.
top
,
localRight:
localClip
.
right
,
localBottom:
localClip
.
bottom
,
transform:
transform
,
);
ui
.
Rect
transformRect
(
Matrix4
transform
,
ui
.
Rect
rect
)
{
return
transformLTRB
(
transform
,
rect
.
left
,
rect
.
top
,
rect
.
right
,
rect
.
bottom
);
}
/// Converts a rectangular clip specified in local coordinates to screen
/// coordinates given the effective [transform].
/// Transforms a rectangle given the effective [transform].
///
/// This is the same as [localClipRectToGlobalClip], except that the local clip
/// rect is specified in terms of left, top, right, and bottom edge offsets.
ui
.
Rect
localClipToGlobalClip
(
{
double
localLeft
,
double
localTop
,
double
localRight
,
double
localBottom
,
Matrix4
transform
,
})
{
assert
(
localLeft
!=
null
);
assert
(
localTop
!=
null
);
assert
(
localRight
!=
null
);
assert
(
localBottom
!=
null
);
/// This is the same as [transformRect], except that the rect is specified
/// in terms of left, top, right, and bottom edge offsets.
ui
.
Rect
transformLTRB
(
Matrix4
transform
,
double
left
,
double
top
,
double
right
,
double
bottom
)
{
assert
(
left
!=
null
);
assert
(
top
!=
null
);
assert
(
right
!=
null
);
assert
(
bottom
!=
null
);
// Construct a matrix where each row represents a vector pointing at
// one of the four corners of the (left, top, right, bottom) rectangle.
...
...
@@ -162,23 +149,23 @@ ui.Rect localClipToGlobalClip({
final
Float64List
pointData
=
Float64List
(
16
);
// Row 0: top-left
pointData
[
0
]
=
l
ocalL
eft
;
pointData
[
4
]
=
localT
op
;
pointData
[
0
]
=
left
;
pointData
[
4
]
=
t
op
;
pointData
[
12
]
=
1
;
// Row 1: top-right
pointData
[
1
]
=
localR
ight
;
pointData
[
5
]
=
localT
op
;
pointData
[
1
]
=
r
ight
;
pointData
[
5
]
=
t
op
;
pointData
[
13
]
=
1
;
// Row 2: bottom-left
pointData
[
2
]
=
l
ocalL
eft
;
pointData
[
6
]
=
localB
ottom
;
pointData
[
2
]
=
left
;
pointData
[
6
]
=
b
ottom
;
pointData
[
14
]
=
1
;
// Row 3: bottom-right
pointData
[
3
]
=
localR
ight
;
pointData
[
7
]
=
localB
ottom
;
pointData
[
3
]
=
r
ight
;
pointData
[
7
]
=
b
ottom
;
pointData
[
15
]
=
1
;
final
Matrix4
pointMatrix
=
Matrix4
.
fromFloat64List
(
pointData
);
...
...
@@ -232,3 +219,22 @@ String _pathToSvgClipPath(ui.Path path,
sb
.
write
(
'"></path></clipPath></defs></svg'
);
return
sb
.
toString
();
}
/// Determines if the (dynamic) exception passed in is a NS_ERROR_FAILURE
/// (from Firefox).
///
/// NS_ERROR_FAILURE (0x80004005) is the most general of all the (Firefox)
/// errors and occurs for all errors for which a more specific error code does
/// not apply. (https://developer.mozilla.org/en-US/docs/Mozilla/Errors)
///
/// Other browsers do not throw this exception.
///
/// In Flutter, this exception happens when we try to perform some operations on
/// a Canvas when the application is rendered in a display:none iframe.
///
/// We need this in [BitmapCanvas] and [RecordingCanvas] to swallow this
/// Firefox exception without interfering with others (potentially useful
/// for the programmer).
bool
_isNsErrorFailureException
(
dynamic
e
)
{
return
js_util
.
getProperty
(
e
,
'name'
)
==
'NS_ERROR_FAILURE'
;
}
lib/web_ui/lib/src/ui/painting.dart
浏览文件 @
f38913b7
...
...
@@ -4,6 +4,7 @@
part of
ui
;
// ignore: unused_element, Used in Shader assert.
bool
_offsetIsValid
(
Offset
offset
)
{
assert
(
offset
!=
null
,
'Offset argument was null.'
);
assert
(!
offset
.
dx
.
isNaN
&&
!
offset
.
dy
.
isNaN
,
...
...
@@ -11,6 +12,7 @@ bool _offsetIsValid(Offset offset) {
return
true
;
}
// ignore: unused_element, Used in Shader assert.
bool
_matrix4IsValid
(
Float64List
matrix4
)
{
assert
(
matrix4
!=
null
,
'Matrix4 argument was null.'
);
assert
(
matrix4
.
length
==
16
,
'Matrix4 must have 16 entries.'
);
...
...
@@ -1430,7 +1432,9 @@ class ColorFilter {
@override
bool
operator
==(
dynamic
other
)
{
if
(
other
is
!
ColorFilter
)
return
false
;
if
(
other
is
!
ColorFilter
)
{
return
false
;
}
final
ColorFilter
typedOther
=
other
;
return
_color
==
typedOther
.
_color
&&
_blendMode
==
typedOther
.
_blendMode
;
}
...
...
lib/web_ui/lib/src/ui/text.dart
浏览文件 @
f38913b7
...
...
@@ -473,10 +473,13 @@ abstract class TextStyle {
List
<
FontFeature
>
fontFeatures
,
})
=
engine
.
EngineTextStyle
;
@override
int
get
hashCode
;
@override
bool
operator
==(
dynamic
other
);
@override
String
toString
();
}
...
...
@@ -551,10 +554,13 @@ abstract class ParagraphStyle {
Locale
locale
,
})
=
engine
.
EngineParagraphStyle
;
@override
bool
operator
==(
dynamic
other
);
@override
int
get
hashCode
;
@override
String
toString
();
}
...
...
@@ -605,8 +611,10 @@ abstract class StrutStyle {
bool
forceStrutHeight
,
})
=
engine
.
EngineStrutStyle
;
@override
int
get
hashCode
;
@override
bool
operator
==(
dynamic
other
);
}
...
...
lib/web_ui/lib/src/ui/window.dart
浏览文件 @
f38913b7
...
...
@@ -496,8 +496,12 @@ class Locale {
@override
String
toString
()
{
final
StringBuffer
out
=
StringBuffer
(
languageCode
);
if
(
scriptCode
!=
null
)
out
.
write
(
'_
$scriptCode
'
);
if
(
_countryCode
!=
null
)
out
.
write
(
'_
$countryCode
'
);
if
(
scriptCode
!=
null
)
{
out
.
write
(
'_
$scriptCode
'
);
}
if
(
_countryCode
!=
null
)
{
out
.
write
(
'_
$countryCode
'
);
}
return
out
.
toString
();
}
...
...
@@ -1098,17 +1102,29 @@ class AccessibilityFeatures {
@override
String
toString
()
{
final
List
<
String
>
features
=
<
String
>[];
if
(
accessibleNavigation
)
features
.
add
(
'accessibleNavigation'
);
if
(
invertColors
)
features
.
add
(
'invertColors'
);
if
(
disableAnimations
)
features
.
add
(
'disableAnimations'
);
if
(
boldText
)
features
.
add
(
'boldText'
);
if
(
reduceMotion
)
features
.
add
(
'reduceMotion'
);
if
(
accessibleNavigation
)
{
features
.
add
(
'accessibleNavigation'
);
}
if
(
invertColors
)
{
features
.
add
(
'invertColors'
);
}
if
(
disableAnimations
)
{
features
.
add
(
'disableAnimations'
);
}
if
(
boldText
)
{
features
.
add
(
'boldText'
);
}
if
(
reduceMotion
)
{
features
.
add
(
'reduceMotion'
);
}
return
'AccessibilityFeatures
$features
'
;
}
@override
bool
operator
==(
dynamic
other
)
{
if
(
other
.
runtimeType
!=
runtimeType
)
return
false
;
if
(
other
.
runtimeType
!=
runtimeType
)
{
return
false
;
}
final
AccessibilityFeatures
typedOther
=
other
;
return
_index
==
typedOther
.
_index
;
}
...
...
lib/web_ui/test/compositing_test.dart
浏览文件 @
f38913b7
...
...
@@ -274,6 +274,9 @@ class MockPersistedPicture extends PersistedPicture {
return
identical
(
existingSurface
.
picture
,
picture
)
?
0.0
:
1.0
;
}
@override
Matrix4
get
localTransformInverse
=>
null
;
@override
void
build
()
{
super
.
build
();
...
...
lib/web_ui/test/engine/semantics/accessibility_test.dart
浏览文件 @
f38913b7
...
...
@@ -27,7 +27,7 @@ void main() {
'is after a delay'
,
()
{
// Set the a11y announcement's duration on DOM to half seconds.
accessibilityAnnouncements
.
durationA11yMessageIsOnDom
=
Duration
(
milliseconds:
500
);
const
Duration
(
milliseconds:
500
);
// Initially there is no accessibility-element
expect
(
document
.
getElementById
(
'accessibility-element'
),
isNull
);
...
...
lib/web_ui/test/text_editing_test.dart
浏览文件 @
f38913b7
...
...
@@ -42,7 +42,7 @@ void trackEditingState(EditingState editingState) {
void
main
(
)
{
group
(
'
$TextEditingElement
'
,
()
{
setUp
(()
{
editingElement
=
TextEditingElement
();
editingElement
=
TextEditingElement
(
HybridTextEditing
()
);
});
tearDown
(()
{
...
...
@@ -193,7 +193,8 @@ void main() {
// A regular <span> shouldn't be accepted.
final
HtmlElement
span
=
SpanElement
();
expect
(
()
=>
PersistentTextEditingElement
(
span
,
onDomElementSwap:
null
),
()
=>
PersistentTextEditingElement
(
HybridTextEditing
(),
span
,
onDomElementSwap:
null
),
throwsAssertionError
,
);
});
...
...
@@ -203,7 +204,8 @@ void main() {
// re-acquiring focus shouldn't happen in persistent mode.
final
InputElement
input
=
InputElement
();
final
PersistentTextEditingElement
persistentEditingElement
=
PersistentTextEditingElement
(
input
,
onDomElementSwap:
()
{});
PersistentTextEditingElement
(
HybridTextEditing
(),
input
,
onDomElementSwap:
()
{});
expect
(
document
.
activeElement
,
document
.
body
);
document
.
body
.
append
(
input
);
...
...
@@ -221,7 +223,8 @@ void main() {
test
(
'Does not dispose and recreate dom elements in persistent mode'
,
()
{
final
InputElement
input
=
InputElement
();
final
PersistentTextEditingElement
persistentEditingElement
=
PersistentTextEditingElement
(
input
,
onDomElementSwap:
()
{});
PersistentTextEditingElement
(
HybridTextEditing
(),
input
,
onDomElementSwap:
()
{});
// The DOM element should've been eagerly created.
expect
(
input
,
isNotNull
);
...
...
@@ -254,7 +257,8 @@ void main() {
test
(
'Refocuses when setting editing state'
,
()
{
final
InputElement
input
=
InputElement
();
final
PersistentTextEditingElement
persistentEditingElement
=
PersistentTextEditingElement
(
input
,
onDomElementSwap:
()
{});
PersistentTextEditingElement
(
HybridTextEditing
(),
input
,
onDomElementSwap:
()
{});
document
.
body
.
append
(
input
);
persistentEditingElement
.
enable
(
singlelineConfig
,
...
...
@@ -274,7 +278,8 @@ void main() {
test
(
'Works in multi-line mode'
,
()
{
final
TextAreaElement
textarea
=
TextAreaElement
();
final
PersistentTextEditingElement
persistentEditingElement
=
PersistentTextEditingElement
(
textarea
,
onDomElementSwap:
()
{});
PersistentTextEditingElement
(
HybridTextEditing
(),
textarea
,
onDomElementSwap:
()
{});
expect
(
persistentEditingElement
.
domElement
,
textarea
);
expect
(
document
.
activeElement
,
document
.
body
);
...
...
@@ -321,6 +326,7 @@ void main() {
});
tearDown
(()
{
// TODO(mdebbar): clean-up stuff that HybridTextEditing registered on the page
spy
.
deactivate
();
});
...
...
@@ -389,6 +395,40 @@ void main() {
expect
(
spy
.
messages
,
isEmpty
);
});
test
(
'setClient, setEditingState, show, setClient'
,
()
{
final
MethodCall
setClient
=
MethodCall
(
'TextInput.setClient'
,
<
dynamic
>[
123
,
flutterSinglelineConfig
]);
textEditing
.
handleTextInput
(
codec
.
encodeMethodCall
(
setClient
));
const
MethodCall
setEditingState
=
MethodCall
(
'TextInput.setEditingState'
,
<
String
,
dynamic
>{
'text'
:
'abcd'
,
'selectionBase'
:
2
,
'selectionExtent'
:
3
,
});
textEditing
.
handleTextInput
(
codec
.
encodeMethodCall
(
setEditingState
));
// Editing shouldn't have started yet.
expect
(
document
.
activeElement
,
document
.
body
);
const
MethodCall
show
=
MethodCall
(
'TextInput.show'
);
textEditing
.
handleTextInput
(
codec
.
encodeMethodCall
(
show
));
checkInputEditingState
(
textEditing
.
editingElement
.
domElement
,
'abcd'
,
2
,
3
);
final
MethodCall
setClient2
=
MethodCall
(
'TextInput.setClient'
,
<
dynamic
>[
567
,
flutterSinglelineConfig
]);
textEditing
.
handleTextInput
(
codec
.
encodeMethodCall
(
setClient2
));
// Receiving another client via setClient should stop editing, hence
// should remove the previous active element.
expect
(
document
.
activeElement
,
document
.
body
);
// Confirm that [HybridTextEditing] didn't send any messages.
expect
(
spy
.
messages
,
isEmpty
);
});
test
(
'setClient, setEditingState, show, setEditingState, clearClient'
,
()
{
final
MethodCall
setClient
=
MethodCall
(
'TextInput.setClient'
,
<
dynamic
>[
123
,
flutterSinglelineConfig
]);
...
...
@@ -424,6 +464,60 @@ void main() {
expect
(
spy
.
messages
,
isEmpty
);
});
test
(
'setClient, setLocationSize, setStyle, setEditingState, show, clearClient'
,
()
{
final
MethodCall
setClient
=
MethodCall
(
'TextInput.setClient'
,
<
dynamic
>[
123
,
flutterSinglelineConfig
]);
textEditing
.
handleTextInput
(
codec
.
encodeMethodCall
(
setClient
));
const
MethodCall
setLocationSize
=
MethodCall
(
'TextInput.setEditingLocationSize'
,
<
String
,
dynamic
>{
'top'
:
0
,
'left'
:
0
,
'width'
:
150
,
'height'
:
50
,
});
textEditing
.
handleTextInput
(
codec
.
encodeMethodCall
(
setLocationSize
));
const
MethodCall
setStyle
=
MethodCall
(
'TextInput.setStyle'
,
<
String
,
dynamic
>{
'fontSize'
:
12
,
'fontFamily'
:
'sans-serif'
,
'textAlignIndex'
:
4
,
'fontWeightValue'
:
4
,
});
textEditing
.
handleTextInput
(
codec
.
encodeMethodCall
(
setStyle
));
const
MethodCall
setEditingState
=
MethodCall
(
'TextInput.setEditingState'
,
<
String
,
dynamic
>{
'text'
:
'abcd'
,
'selectionBase'
:
2
,
'selectionExtent'
:
3
,
});
textEditing
.
handleTextInput
(
codec
.
encodeMethodCall
(
setEditingState
));
const
MethodCall
show
=
MethodCall
(
'TextInput.show'
);
textEditing
.
handleTextInput
(
codec
.
encodeMethodCall
(
show
));
checkInputEditingState
(
textEditing
.
editingElement
.
domElement
,
'abcd'
,
2
,
3
);
// Check if the location and styling is correct.
expect
(
textEditing
.
editingElement
.
domElement
.
getBoundingClientRect
(),
Rectangle
<
double
>.
fromPoints
(
const
Point
<
double
>(
0.0
,
0.0
),
const
Point
<
double
>(
150.0
,
50.0
)));
expect
(
textEditing
.
editingElement
.
domElement
.
style
.
font
,
'500 12px sans-serif'
);
const
MethodCall
clearClient
=
MethodCall
(
'TextInput.clearClient'
);
textEditing
.
handleTextInput
(
codec
.
encodeMethodCall
(
clearClient
));
// Confirm that [HybridTextEditing] didn't send any messages.
expect
(
spy
.
messages
,
isEmpty
);
});
test
(
'Syncs the editing state back to Flutter'
,
()
{
final
MethodCall
setClient
=
MethodCall
(
'TextInput.setClient'
,
<
dynamic
>[
123
,
flutterSinglelineConfig
]);
...
...
lib/web_ui/test/text_test.dart
浏览文件 @
f38913b7
...
...
@@ -2,6 +2,8 @@
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
import
'dart:html'
;
import
'package:test/test.dart'
;
import
'package:ui/ui.dart'
;
...
...
@@ -216,4 +218,31 @@ void main() async {
expect
(
paragraph
.
getPositionForOffset
(
const
Offset
(
150
,
0
)).
offset
,
thirdSpanStartPosition
);
});
// Regression test for https://github.com/flutter/flutter/issues/38972
test
(
'should not set fontFamily to effectiveFontFamily for spans in rich text'
,
()
{
final
ParagraphBuilder
builder
=
ParagraphBuilder
(
ParagraphStyle
(
fontFamily:
'Roboto'
,
fontStyle:
FontStyle
.
normal
,
fontWeight:
FontWeight
.
normal
,
fontSize:
15.0
,
));
builder
.
pushStyle
(
TextStyle
(
fontFamily:
'Menlo'
,
fontWeight:
FontWeight
.
bold
));
const
String
firstSpanText
=
'abc'
;
builder
.
addText
(
firstSpanText
);
builder
.
pushStyle
(
TextStyle
(
fontSize:
30.0
,
fontWeight:
FontWeight
.
normal
));
const
String
secondSpanText
=
'def'
;
builder
.
addText
(
secondSpanText
);
final
EngineParagraph
paragraph
=
builder
.
build
();
paragraph
.
layout
(
const
ParagraphConstraints
(
width:
800.0
));
expect
(
paragraph
.
plainText
,
isNull
);
final
List
<
SpanElement
>
spans
=
paragraph
.
paragraphElement
.
querySelectorAll
(
'span'
);
expect
(
spans
[
0
].
style
.
fontFamily
,
'Ahem'
);
// The nested span here should not set it's family to default sans-serif.
expect
(
spans
[
1
].
style
.
fontFamily
,
'Ahem'
);
});
}
lib/web_ui/tool/unicode_sync_script.dart
浏览文件 @
f38913b7
...
...
@@ -68,7 +68,7 @@ void main(List<String> arguments) async {
final
String
propertiesFile
=
arguments
[
0
];
final
String
codegenFile
=
path
.
join
(
path
.
dirname
(
Platform
.
script
.
toFilePath
()),
'../lib/src/text/word_break_properties.dart'
,
'../lib/src/
engine/
text/word_break_properties.dart'
,
);
WordBreakPropertiesSyncer
(
propertiesFile
,
codegenFile
).
perform
();
}
...
...
@@ -103,13 +103,17 @@ class WordBreakPropertiesSyncer extends PropertiesSyncer {
@override
String
template
(
List
<
String
>
header
,
List
<
PropertyTuple
>
data
)
{
return
'''
// 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.
// AUTO-GENERATED FILE.
// Generated by:
bin
/unicode_sync_script.dart
// Generated by:
tool
/unicode_sync_script.dart
//
// Source:
//
${header.join('\n// ')}
import '
unicode_range
.
dart
'
;
part of engine
;
CharProperty getCharProperty(String text, int index) {
if (index < 0 || index >= text.length) {
...
...
@@ -123,7 +127,7 @@ enum CharProperty {
}
const UnicodePropertyLookup<CharProperty> lookup =
UnicodePropertyLookup<CharProperty>([
UnicodePropertyLookup<CharProperty>(
<UnicodeRange<CharProperty>>
[
${getLookupEntries(data).join(',\n ')}
]);
'''
;
...
...
@@ -150,17 +154,23 @@ const UnicodePropertyLookup<CharProperty> lookup =
String
generateLookupEntry
(
PropertyTuple
tuple
)
{
final
String
propertyStr
=
'CharProperty.
${normalizePropertyName(tuple.property)}
'
;
return
'UnicodeRange(
${toHex(tuple.start)}
,
${toHex(tuple.end)}
,
$propertyStr
)'
;
return
'UnicodeRange
<CharProperty>
(
${toHex(tuple.start)}
,
${toHex(tuple.end)}
,
$propertyStr
)'
;
}
}
/// Example:
/// UnicodeRange(0x01C4, 0x0293, CharProperty.ALetter),
/// UnicodeRange(0x0294, 0x0294, CharProperty.ALetter),
/// UnicodeRange(0x0295, 0x02AF, CharProperty.ALetter),
///
/// ```
/// UnicodeRange<CharProperty>(0x01C4, 0x0293, CharProperty.ALetter),
/// UnicodeRange<CharProperty>(0x0294, 0x0294, CharProperty.ALetter),
/// UnicodeRange<CharProperty>(0x0295, 0x02AF, CharProperty.ALetter),
/// ```
///
/// will get combined into:
/// UnicodeRange(0x01C4, 0x02AF, CharProperty.ALetter)
///
/// ```
/// UnicodeRange<CharProperty>(0x01C4, 0x02AF, CharProperty.ALetter)
/// ```
List
<
PropertyTuple
>
combineAdjacentRanges
(
List
<
PropertyTuple
>
data
)
{
final
List
<
PropertyTuple
>
result
=
<
PropertyTuple
>[
data
.
first
];
for
(
int
i
=
1
;
i
<
data
.
length
;
i
++)
{
...
...
编辑
预览
Markdown
is supported
0%
请重试
或
添加新附件
.
添加附件
取消
You are about to add
0
people
to the discussion. Proceed with caution.
先完成此消息的编辑!
取消
想要评论请
注册
或
登录