Skip to content
Projects
Groups
Snippets
Help
Loading...
Help
Submit feedback
Sign in
Toggle navigation
F
Front-End
Project
Project
Details
Activity
Releases
Cycle Analytics
Repository
Repository
Files
Commits
Branches
Tags
Contributors
Graph
Compare
Charts
Issues
0
Issues
0
List
Board
Labels
Milestones
Merge Requests
0
Merge Requests
0
CI / CD
CI / CD
Pipelines
Jobs
Schedules
Charts
Wiki
Wiki
Snippets
Snippets
Members
Members
Collapse sidebar
Close sidebar
Activity
Graph
Charts
Create a new issue
Jobs
Commits
Issue Boards
Open sidebar
abdullh.alsoleman
Front-End
Commits
b2c710dd
Commit
b2c710dd
authored
Feb 10, 2016
by
Ian Hickson
Browse files
Options
Browse Files
Download
Plain Diff
Merge pull request #1744 from Hixie/tap-drag-target
Tapping through drag targets.
parents
a9a445b1
4d5e4067
Changes
4
Hide whitespace changes
Inline
Side-by-side
Showing
4 changed files
with
189 additions
and
53 deletions
+189
-53
proxy_box.dart
packages/flutter/lib/src/rendering/proxy_box.dart
+70
-47
basic.dart
packages/flutter/lib/src/widgets/basic.dart
+16
-5
drag_target.dart
packages/flutter/lib/src/widgets/drag_target.dart
+1
-0
draggable_test.dart
packages/flutter/test/widget/draggable_test.dart
+102
-1
No files found.
packages/flutter/lib/src/rendering/proxy_box.dart
View file @
b2c710dd
...
...
@@ -91,6 +91,61 @@ class RenderProxyBox extends RenderBox with RenderObjectWithChildMixin<RenderBox
}
}
/// How to behave during hit tests.
enum
HitTestBehavior
{
/// Targets that defer to their children receive events within their bounds
/// only if one of their children is hit by the hit test.
deferToChild
,
/// Opaque targets can be hit by hit tests, causing them to both receive
/// events within their bounds and prevent targets visually behind them from
/// also receiving events.
opaque
,
/// Translucent targets both receive events within their bounds and permit
/// targets visually behind them to also receive events.
translucent
,
}
/// A RenderProxyBox subclass that allows you to customize the
/// hit-testing behavior.
abstract
class
RenderProxyBoxWithHitTestBehavior
extends
RenderProxyBox
{
RenderProxyBoxWithHitTestBehavior
({
this
.
behavior
:
HitTestBehavior
.
deferToChild
,
RenderBox
child
})
:
super
(
child
);
HitTestBehavior
behavior
;
bool
hitTest
(
HitTestResult
result
,
{
Point
position
})
{
bool
hitTarget
=
false
;
if
(
position
.
x
>=
0.0
&&
position
.
x
<
size
.
width
&&
position
.
y
>=
0.0
&&
position
.
y
<
size
.
height
)
{
hitTarget
=
hitTestChildren
(
result
,
position:
position
)
||
hitTestSelf
(
position
);
if
(
hitTarget
||
behavior
==
HitTestBehavior
.
translucent
)
result
.
add
(
new
BoxHitTestEntry
(
this
,
position
));
}
return
hitTarget
;
}
bool
hitTestSelf
(
Point
position
)
=>
behavior
==
HitTestBehavior
.
opaque
;
void
debugDescribeSettings
(
List
<
String
>
settings
)
{
super
.
debugDescribeSettings
(
settings
);
switch
(
behavior
)
{
case
HitTestBehavior
.
translucent
:
settings
.
add
(
'behavior: translucent'
);
break
;
case
HitTestBehavior
.
opaque
:
settings
.
add
(
'behavior: opaque'
);
break
;
case
HitTestBehavior
.
deferToChild
:
settings
.
add
(
'behavior: defer-to-child'
);
break
;
}
}
}
/// Imposes additional constraints on its child.
///
/// A render constrained box proxies most functions in the render box protocol
...
...
@@ -1231,51 +1286,21 @@ typedef void PointerMoveEventListener(PointerMoveEvent event);
typedef
void
PointerUpEventListener
(
PointerUpEvent
event
);
typedef
void
PointerCancelEventListener
(
PointerCancelEvent
event
);
/// How to behave during hit tests.
enum
HitTestBehavior
{
/// Targets that defer to their children receive events within their bounds
/// only if one of their children is hit by the hit test.
deferToChild
,
/// Opaque targets can be hit by hit tests, causing them to both receive
/// events within their bounds and prevent targets visually behind them from
/// also receiving events.
opaque
,
/// Translucent targets both receive events within their bounds and permit
/// targets visually behind them to also receive events.
translucent
,
}
/// Invokes the callbacks in response to pointer events.
class
RenderPointerListener
extends
RenderProxyBox
{
class
RenderPointerListener
extends
RenderProxyBox
WithHitTestBehavior
{
RenderPointerListener
({
this
.
onPointerDown
,
this
.
onPointerMove
,
this
.
onPointerUp
,
this
.
onPointerCancel
,
this
.
behavior
:
HitTestBehavior
.
deferToChild
,
HitTestBehavior
behavior:
HitTestBehavior
.
deferToChild
,
RenderBox
child
})
:
super
(
child
);
})
:
super
(
behavior:
behavior
,
child:
child
);
PointerDownEventListener
onPointerDown
;
PointerMoveEventListener
onPointerMove
;
PointerUpEventListener
onPointerUp
;
PointerCancelEventListener
onPointerCancel
;
HitTestBehavior
behavior
;
bool
hitTest
(
HitTestResult
result
,
{
Point
position
})
{
bool
hitTarget
=
false
;
if
(
position
.
x
>=
0.0
&&
position
.
x
<
size
.
width
&&
position
.
y
>=
0.0
&&
position
.
y
<
size
.
height
)
{
hitTarget
=
hitTestChildren
(
result
,
position:
position
)
||
hitTestSelf
(
position
);
if
(
hitTarget
||
behavior
==
HitTestBehavior
.
translucent
)
result
.
add
(
new
BoxHitTestEntry
(
this
,
position
));
}
return
hitTarget
;
}
bool
hitTestSelf
(
Point
position
)
=>
behavior
==
HitTestBehavior
.
opaque
;
void
handleEvent
(
PointerEvent
event
,
HitTestEntry
entry
)
{
if
(
onPointerDown
!=
null
&&
event
is
PointerDownEvent
)
...
...
@@ -1302,17 +1327,6 @@ class RenderPointerListener extends RenderProxyBox {
if
(
listeners
.
isEmpty
)
listeners
.
add
(
'<none>'
);
settings
.
add
(
'listeners:
${listeners.join(", ")}
'
);
switch
(
behavior
)
{
case
HitTestBehavior
.
translucent
:
settings
.
add
(
'behavior: translucent'
);
break
;
case
HitTestBehavior
.
opaque
:
settings
.
add
(
'behavior: opaque'
);
break
;
case
HitTestBehavior
.
deferToChild
:
settings
.
add
(
'behavior: defer-to-child'
);
break
;
}
}
}
...
...
@@ -1392,12 +1406,21 @@ class RenderIgnorePointer extends RenderProxyBox {
}
}
/// Holds opaque meta data in the render tree
class
RenderMetaData
extends
RenderProxyBox
{
RenderMetaData
({
RenderBox
child
,
this
.
metaData
})
:
super
(
child
);
/// Holds opaque meta data in the render tree.
class
RenderMetaData
extends
RenderProxyBoxWithHitTestBehavior
{
RenderMetaData
({
this
.
metaData
,
HitTestBehavior
behavior:
HitTestBehavior
.
deferToChild
,
RenderBox
child
})
:
super
(
behavior:
behavior
,
child:
child
);
/// Opaque meta data ignored by the render tree
dynamic
metaData
;
void
debugDescribeSettings
(
List
<
String
>
settings
)
{
super
.
debugDescribeSettings
(
settings
);
settings
.
add
(
'metaData:
$metaData
'
);
}
}
/// Listens for the specified gestures from the semantics server (e.g.
...
...
packages/flutter/lib/src/widgets/basic.dart
View file @
b2c710dd
...
...
@@ -2193,20 +2193,31 @@ class ExcludeSemantics extends OneChildRenderObjectWidget {
}
class
MetaData
extends
OneChildRenderObjectWidget
{
MetaData
({
Key
key
,
Widget
child
,
this
.
metaData
})
:
super
(
key:
key
,
child:
child
);
MetaData
({
Key
key
,
Widget
child
,
this
.
metaData
,
this
.
behavior
:
HitTestBehavior
.
deferToChild
})
:
super
(
key:
key
,
child:
child
);
final
dynamic
metaData
;
final
HitTestBehavior
behavior
;
RenderMetaData
createRenderObject
()
=>
new
RenderMetaData
(
metaData:
metaData
);
RenderMetaData
createRenderObject
()
=>
new
RenderMetaData
(
metaData:
metaData
,
behavior:
behavior
);
void
updateRenderObject
(
RenderMetaData
renderObject
,
MetaData
oldWidget
)
{
renderObject
.
metaData
=
metaData
;
renderObject
..
metaData
=
metaData
..
behavior
=
behavior
;
}
void
debugFillDescription
(
List
<
String
>
description
)
{
super
.
debugFillDescription
(
description
);
description
.
add
(
'
$metaData
'
);
description
.
add
(
'behavior:
$behavior
'
);
description
.
add
(
'metaData:
$metaData
'
);
}
}
...
...
packages/flutter/lib/src/widgets/drag_target.dart
View file @
b2c710dd
...
...
@@ -288,6 +288,7 @@ class _DragTargetState<T> extends State<DragTarget<T>> {
Widget
build
(
BuildContext
context
)
{
return
new
MetaData
(
metaData:
this
,
behavior:
HitTestBehavior
.
translucent
,
child:
config
.
builder
(
context
,
new
UnmodifiableListView
<
T
>(
_candidateData
),
new
UnmodifiableListView
<
dynamic
>(
_rejectedData
)
...
...
packages/flutter/test/widget/draggable_test.dart
View file @
b2c710dd
...
...
@@ -11,7 +11,7 @@ void main() {
testWidgets
((
WidgetTester
tester
)
{
TestPointer
pointer
=
new
TestPointer
(
7
);
List
accepted
=
[];
List
<
dynamic
>
accepted
=
<
dynamic
>
[];
tester
.
pumpWidget
(
new
MaterialApp
(
routes:
<
String
,
RouteBuilder
>{
...
...
@@ -70,4 +70,105 @@ void main() {
expect
(
tester
.
findText
(
'Target'
),
isNotNull
);
});
});
test
(
'Drag and drop - dragging over button'
,
()
{
testWidgets
((
WidgetTester
tester
)
{
TestPointer
pointer
=
new
TestPointer
(
7
);
List
<
String
>
events
=
<
String
>[];
Point
firstLocation
,
secondLocation
;
tester
.
pumpWidget
(
new
MaterialApp
(
routes:
<
String
,
RouteBuilder
>{
'/'
:
(
RouteArguments
args
)
{
return
new
Column
(
children:
<
Widget
>[
new
Draggable
(
data:
1
,
child:
new
Text
(
'Source'
),
feedback:
new
Text
(
'Dragging'
)
),
new
Stack
(
children:
<
Widget
>[
new
GestureDetector
(
behavior:
HitTestBehavior
.
opaque
,
onTap:
()
{
events
.
add
(
'tap'
);
},
child:
new
Container
(
child:
new
Text
(
'Button'
)
)
),
new
DragTarget
(
builder:
(
context
,
data
,
rejects
)
{
return
new
IgnorePointer
(
child:
new
Container
(
child:
new
Text
(
'Target'
)
)
);
},
onAccept:
(
data
)
{
events
.
add
(
'drop'
);
}
),
]
),
]);
},
}
));
expect
(
events
,
isEmpty
);
expect
(
tester
.
findText
(
'Source'
),
isNotNull
);
expect
(
tester
.
findText
(
'Dragging'
),
isNull
);
expect
(
tester
.
findText
(
'Target'
),
isNotNull
);
expect
(
tester
.
findText
(
'Button'
),
isNotNull
);
// taps (we check both to make sure the test is consistent)
expect
(
events
,
isEmpty
);
tester
.
tap
(
tester
.
findText
(
'Button'
));
expect
(
events
,
equals
(<
String
>[
'tap'
]));
events
.
clear
();
expect
(
events
,
isEmpty
);
tester
.
tap
(
tester
.
findText
(
'Target'
));
expect
(
events
,
equals
(<
String
>[
'tap'
]));
events
.
clear
();
// drag and drop
firstLocation
=
tester
.
getCenter
(
tester
.
findText
(
'Source'
));
tester
.
dispatchEvent
(
pointer
.
down
(
firstLocation
),
firstLocation
);
tester
.
pump
();
secondLocation
=
tester
.
getCenter
(
tester
.
findText
(
'Target'
));
tester
.
dispatchEvent
(
pointer
.
move
(
secondLocation
),
firstLocation
);
tester
.
pump
();
expect
(
events
,
isEmpty
);
tester
.
dispatchEvent
(
pointer
.
up
(),
firstLocation
);
tester
.
pump
();
expect
(
events
,
equals
(<
String
>[
'drop'
]));
events
.
clear
();
// drag and tap and drop
firstLocation
=
tester
.
getCenter
(
tester
.
findText
(
'Source'
));
tester
.
dispatchEvent
(
pointer
.
down
(
firstLocation
),
firstLocation
);
tester
.
pump
();
secondLocation
=
tester
.
getCenter
(
tester
.
findText
(
'Target'
));
tester
.
dispatchEvent
(
pointer
.
move
(
secondLocation
),
firstLocation
);
tester
.
pump
();
expect
(
events
,
isEmpty
);
tester
.
tap
(
tester
.
findText
(
'Button'
));
tester
.
tap
(
tester
.
findText
(
'Target'
));
tester
.
dispatchEvent
(
pointer
.
up
(),
firstLocation
);
tester
.
pump
();
expect
(
events
,
equals
(<
String
>[
'tap'
,
'tap'
,
'drop'
]));
events
.
clear
();
});
});
}
Write
Preview
Markdown
is supported
0%
Try again
or
attach a new file
Attach a file
Cancel
You are about to add
0
people
to the discussion. Proceed with caution.
Finish editing this message first!
Cancel
Please
register
or
sign in
to comment