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
f86b9220
Unverified
Commit
f86b9220
authored
Apr 21, 2023
by
Luccas Clezar
Committed by
GitHub
Apr 21, 2023
Browse files
Options
Browse Files
Download
Email Patches
Plain Diff
Update Cupertino desktop text selection toolbar (#121829)
Visual fidelity of the right-click context menu on MacOS.
parent
8a815c1d
Changes
7
Hide whitespace changes
Inline
Side-by-side
Showing
7 changed files
with
278 additions
and
66 deletions
+278
-66
desktop_text_selection.dart
...ges/flutter/lib/src/cupertino/desktop_text_selection.dart
+0
-1
desktop_text_selection_toolbar.dart
...ter/lib/src/cupertino/desktop_text_selection_toolbar.dart
+80
-24
desktop_text_selection_toolbar_button.dart
.../src/cupertino/desktop_text_selection_toolbar_button.dart
+43
-38
text_selection_toolbar_button.dart
...tter/lib/src/cupertino/text_selection_toolbar_button.dart
+2
-0
adaptive_text_selection_toolbar.dart
...ter/lib/src/material/adaptive_text_selection_toolbar.dart
+0
-1
desktop_text_selection_toolbar_button_test.dart
...cupertino/desktop_text_selection_toolbar_button_test.dart
+43
-1
desktop_text_selection_toolbar_test.dart
...r/test/cupertino/desktop_text_selection_toolbar_test.dart
+110
-1
No files found.
packages/flutter/lib/src/cupertino/desktop_text_selection.dart
View file @
f86b9220
...
...
@@ -182,7 +182,6 @@ class _CupertinoDesktopTextSelectionControlsToolbarState extends State<_Cupertin
}
items
.
add
(
CupertinoDesktopTextSelectionToolbarButton
.
text
(
context:
context
,
onPressed:
onPressed
,
text:
text
,
));
...
...
packages/flutter/lib/src/cupertino/desktop_text_selection_toolbar.dart
View file @
f86b9220
...
...
@@ -2,6 +2,9 @@
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
import
'dart:ui'
;
import
'package:flutter/foundation.dart'
;
import
'package:flutter/widgets.dart'
;
import
'colors.dart'
;
...
...
@@ -10,23 +13,33 @@ import 'colors.dart';
// the screen.
const
double
_kToolbarScreenPadding
=
8.0
;
// These values were measured from a screenshot of TextEdit on macOS 10.15.7 on
// a Macbook Pro.
// These values were measured from a screenshot of the native context menu on
// macOS 13.2 on a Macbook Pro.
const
double
_kToolbarSaturationBoost
=
3
;
const
double
_kToolbarBlurSigma
=
20
;
const
double
_kToolbarWidth
=
222.0
;
const
Radius
_kToolbarBorderRadius
=
Radius
.
circular
(
4.0
);
const
EdgeInsets
_kToolbarPadding
=
EdgeInsets
.
symmetric
(
vertical:
3.0
,
);
const
Radius
_kToolbarBorderRadius
=
Radius
.
circular
(
8.0
);
const
EdgeInsets
_kToolbarPadding
=
EdgeInsets
.
all
(
6.0
);
const
List
<
BoxShadow
>
_kToolbarShadow
=
<
BoxShadow
>[
BoxShadow
(
color:
Color
.
fromARGB
(
60
,
0
,
0
,
0
),
blurRadius:
10.0
,
spreadRadius:
0.5
,
offset:
Offset
(
0.0
,
4.0
),
),
];
// These values were measured from a screenshot of TextEdit on macOS 10.16 on a
// Macbook Pro.
const
CupertinoDynamicColor
_kToolbarBorderColor
=
CupertinoDynamicColor
.
withBrightness
(
color:
Color
(
0xFFBBBBBB
),
darkColor:
Color
(
0xFF505152
),
// These values were measured from a screenshot of the native context menu on
// macOS 13.2 on a Macbook Pro.
const
CupertinoDynamicColor
_kToolbarBorderColor
=
CupertinoDynamicColor
.
withBrightness
(
color:
Color
(
0xFFB8B8B8
),
darkColor:
Color
(
0xFF5B5B5B
),
);
const
CupertinoDynamicColor
_kToolbarBackgroundColor
=
CupertinoDynamicColor
.
withBrightness
(
color:
Color
(
0xffECE8E6
),
darkColor:
Color
(
0xff302928
),
const
CupertinoDynamicColor
_kToolbarBackgroundColor
=
CupertinoDynamicColor
.
withBrightness
(
color:
Color
(
0xB2FFFFFF
),
darkColor:
Color
(
0xB2303030
),
);
/// A macOS-style text selection toolbar.
...
...
@@ -53,6 +66,23 @@ class CupertinoDesktopTextSelectionToolbar extends StatelessWidget {
required
this
.
children
,
})
:
assert
(
children
.
length
>
0
);
/// Creates a 5x5 matrix that increases saturation when used with [ColorFilter.matrix].
///
/// The numbers were taken from this comment:
/// [Cupertino blurs should boost saturation](https://github.com/flutter/flutter/issues/29483#issuecomment-477334981).
static
List
<
double
>
_matrixWithSaturation
(
double
saturation
)
{
final
double
r
=
0.213
*
(
1
-
saturation
);
final
double
g
=
0.715
*
(
1
-
saturation
);
final
double
b
=
0.072
*
(
1
-
saturation
);
return
<
double
>[
r
+
saturation
,
g
,
b
,
0
,
0
,
//
r
,
g
+
saturation
,
b
,
0
,
0
,
//
r
,
g
,
b
+
saturation
,
0
,
0
,
//
0
,
0
,
0
,
1
,
0
,
//
];
}
/// {@macro flutter.material.DesktopTextSelectionToolbar.anchor}
final
Offset
anchor
;
...
...
@@ -68,16 +98,41 @@ class CupertinoDesktopTextSelectionToolbar extends StatelessWidget {
static
Widget
_defaultToolbarBuilder
(
BuildContext
context
,
Widget
child
)
{
return
Container
(
width:
_kToolbarWidth
,
decoration:
BoxDecoration
(
color:
_kToolbarBackgroundColor
.
resolveFrom
(
context
),
border:
Border
.
all
(
color:
_kToolbarBorderColor
.
resolveFrom
(
context
),
),
borderRadius:
const
BorderRadius
.
all
(
_kToolbarBorderRadius
),
clipBehavior:
Clip
.
hardEdge
,
decoration:
const
BoxDecoration
(
boxShadow:
_kToolbarShadow
,
borderRadius:
BorderRadius
.
all
(
_kToolbarBorderRadius
),
),
child:
Padding
(
padding:
_kToolbarPadding
,
child:
child
,
child:
BackdropFilter
(
// Flutter web doesn't support ImageFilter.compose on CanvasKit yet
// (https://github.com/flutter/flutter/issues/120123).
filter:
kIsWeb
?
ImageFilter
.
blur
(
sigmaX:
_kToolbarBlurSigma
,
sigmaY:
_kToolbarBlurSigma
,
)
:
ImageFilter
.
compose
(
outer:
ColorFilter
.
matrix
(
_matrixWithSaturation
(
_kToolbarSaturationBoost
),
),
inner:
ImageFilter
.
blur
(
sigmaX:
_kToolbarBlurSigma
,
sigmaY:
_kToolbarBlurSigma
,
),
),
child:
DecoratedBox
(
decoration:
BoxDecoration
(
color:
_kToolbarBackgroundColor
.
resolveFrom
(
context
),
border:
Border
.
all
(
color:
_kToolbarBorderColor
.
resolveFrom
(
context
),
),
borderRadius:
const
BorderRadius
.
all
(
_kToolbarBorderRadius
),
),
child:
Padding
(
padding:
_kToolbarPadding
,
child:
child
,
),
),
),
);
}
...
...
@@ -86,7 +141,8 @@ class CupertinoDesktopTextSelectionToolbar extends StatelessWidget {
Widget
build
(
BuildContext
context
)
{
assert
(
debugCheckHasMediaQuery
(
context
));
final
double
paddingAbove
=
MediaQuery
.
paddingOf
(
context
).
top
+
_kToolbarScreenPadding
;
final
double
paddingAbove
=
MediaQuery
.
paddingOf
(
context
).
top
+
_kToolbarScreenPadding
;
final
Offset
localAdjustment
=
Offset
(
_kToolbarScreenPadding
,
paddingAbove
);
return
Padding
(
...
...
packages/flutter/lib/src/cupertino/desktop_text_selection_toolbar_button.dart
View file @
f86b9220
...
...
@@ -10,8 +10,8 @@ import 'colors.dart';
import
'text_selection_toolbar_button.dart'
;
import
'theme.dart'
;
// These values were measured from a screenshot of
TextEdit on MacOS 10.15.7
on
// a Macbook Pro.
// These values were measured from a screenshot of
the native context menu
on
//
macOS 13.2 on
a Macbook Pro.
const
TextStyle
_kToolbarButtonFontStyle
=
TextStyle
(
inherit:
false
,
fontSize:
14.0
,
...
...
@@ -19,13 +19,13 @@ const TextStyle _kToolbarButtonFontStyle = TextStyle(
fontWeight:
FontWeight
.
w400
,
);
// This value was measured from a screenshot of
TextEdit on MacOS 10.15.7 on a
// Macbook Pro.
// This value was measured from a screenshot of
the native context menu on
//
macOS 13.2 on a
Macbook Pro.
const
EdgeInsets
_kToolbarButtonPadding
=
EdgeInsets
.
fromLTRB
(
20
.0
,
0
.0
,
20
.0
,
3
.0
,
8
.0
,
2
.0
,
8
.0
,
5
.0
,
);
/// A button in the style of the Mac context menu buttons.
...
...
@@ -37,26 +37,17 @@ class CupertinoDesktopTextSelectionToolbarButton extends StatefulWidget {
super
.
key
,
required
this
.
onPressed
,
required
Widget
this
.
child
,
})
:
buttonItem
=
null
;
})
:
buttonItem
=
null
,
text
=
null
;
/// Create an instance of [CupertinoDesktopTextSelectionToolbarButton] whose child is
/// a [Text] widget styled like the default Mac context menu button.
CupertinoDesktopTextSelectionToolbarButton
.
text
({
const
CupertinoDesktopTextSelectionToolbarButton
.
text
({
super
.
key
,
required
BuildContext
context
,
required
this
.
onPressed
,
required
String
text
,
})
:
buttonItem
=
null
,
child
=
Text
(
text
,
overflow:
TextOverflow
.
ellipsis
,
style:
_kToolbarButtonFontStyle
.
copyWith
(
color:
const
CupertinoDynamicColor
.
withBrightness
(
color:
CupertinoColors
.
black
,
darkColor:
CupertinoColors
.
white
,
).
resolveFrom
(
context
),
),
);
required
this
.
text
,
})
:
buttonItem
=
null
,
child
=
null
;
/// Create an instance of [CupertinoDesktopTextSelectionToolbarButton] from
/// the given [ContextMenuButtonItem].
...
...
@@ -65,8 +56,9 @@ class CupertinoDesktopTextSelectionToolbarButton extends StatefulWidget {
CupertinoDesktopTextSelectionToolbarButton
.
buttonItem
({
super
.
key
,
required
ContextMenuButtonItem
this
.
buttonItem
,
})
:
onPressed
=
buttonItem
.
onPressed
,
child
=
null
;
})
:
onPressed
=
buttonItem
.
onPressed
,
text
=
null
,
child
=
null
;
/// {@macro flutter.cupertino.CupertinoTextSelectionToolbarButton.onPressed}
final
VoidCallback
?
onPressed
;
...
...
@@ -77,11 +69,16 @@ class CupertinoDesktopTextSelectionToolbarButton extends StatefulWidget {
/// {@macro flutter.cupertino.CupertinoTextSelectionToolbarButton.onPressed}
final
ContextMenuButtonItem
?
buttonItem
;
/// {@macro flutter.cupertino.CupertinoTextSelectionToolbarButton.text}
final
String
?
text
;
@override
State
<
CupertinoDesktopTextSelectionToolbarButton
>
createState
()
=>
_CupertinoDesktopTextSelectionToolbarButtonState
();
State
<
CupertinoDesktopTextSelectionToolbarButton
>
createState
()
=>
_CupertinoDesktopTextSelectionToolbarButtonState
();
}
class
_CupertinoDesktopTextSelectionToolbarButtonState
extends
State
<
CupertinoDesktopTextSelectionToolbarButton
>
{
class
_CupertinoDesktopTextSelectionToolbarButtonState
extends
State
<
CupertinoDesktopTextSelectionToolbarButton
>
{
bool
_isHovered
=
false
;
void
_onEnter
(
PointerEnterEvent
event
)
{
...
...
@@ -98,16 +95,24 @@ class _CupertinoDesktopTextSelectionToolbarButtonState extends State<CupertinoDe
@override
Widget
build
(
BuildContext
context
)
{
final
Widget
child
=
widget
.
child
??
Text
(
CupertinoTextSelectionToolbarButton
.
getButtonLabel
(
context
,
widget
.
buttonItem
!),
overflow:
TextOverflow
.
ellipsis
,
style:
_kToolbarButtonFontStyle
.
copyWith
(
color:
const
CupertinoDynamicColor
.
withBrightness
(
color:
CupertinoColors
.
black
,
darkColor:
CupertinoColors
.
white
,
).
resolveFrom
(
context
),
),
);
final
Widget
child
=
widget
.
child
??
Text
(
widget
.
text
??
CupertinoTextSelectionToolbarButton
.
getButtonLabel
(
context
,
widget
.
buttonItem
!,
),
overflow:
TextOverflow
.
ellipsis
,
style:
_kToolbarButtonFontStyle
.
copyWith
(
color:
_isHovered
?
CupertinoTheme
.
of
(
context
).
primaryContrastingColor
:
const
CupertinoDynamicColor
.
withBrightness
(
color:
CupertinoColors
.
black
,
darkColor:
CupertinoColors
.
white
,
).
resolveFrom
(
context
),
),
);
return
SizedBox
(
width:
double
.
infinity
,
child:
MouseRegion
(
...
...
@@ -115,7 +120,7 @@ class _CupertinoDesktopTextSelectionToolbarButtonState extends State<CupertinoDe
onExit:
_onExit
,
child:
CupertinoButton
(
alignment:
Alignment
.
centerLeft
,
borderRadius:
null
,
borderRadius:
const
BorderRadius
.
all
(
Radius
.
circular
(
4.0
))
,
color:
_isHovered
?
CupertinoTheme
.
of
(
context
).
primaryColor
:
null
,
minSize:
0.0
,
onPressed:
widget
.
onPressed
,
...
...
packages/flutter/lib/src/cupertino/text_selection_toolbar_button.dart
View file @
f86b9220
...
...
@@ -83,8 +83,10 @@ class CupertinoTextSelectionToolbarButton extends StatelessWidget {
/// {@endtemplate}
final
ContextMenuButtonItem
?
buttonItem
;
/// {@template flutter.cupertino.CupertinoTextSelectionToolbarButton.text}
/// The text used in the button's label when using
/// [CupertinoTextSelectionToolbarButton.text].
/// {@endtemplate}
final
String
?
text
;
/// Returns the default button label String for the button of the given
...
...
packages/flutter/lib/src/material/adaptive_text_selection_toolbar.dart
View file @
f86b9220
...
...
@@ -271,7 +271,6 @@ class AdaptiveTextSelectionToolbar extends StatelessWidget {
case
TargetPlatform
.
macOS
:
return
buttonItems
.
map
((
ContextMenuButtonItem
buttonItem
)
{
return
CupertinoDesktopTextSelectionToolbarButton
.
text
(
context:
context
,
onPressed:
buttonItem
.
onPressed
,
text:
getButtonLabel
(
context
,
buttonItem
),
);
...
...
packages/flutter/test/cupertino/desktop_text_selection_toolbar_button_test.dart
View file @
f86b9220
...
...
@@ -3,6 +3,7 @@
// found in the LICENSE file.
import
'package:flutter/cupertino.dart'
;
import
'package:flutter/gestures.dart'
;
import
'package:flutter_test/flutter_test.dart'
;
void
main
(
)
{
...
...
@@ -29,6 +30,46 @@ void main() {
expect
(
pressed
,
true
);
});
testWidgets
(
'keeps contrast with background on hover'
,
(
WidgetTester
tester
)
async
{
await
tester
.
pumpWidget
(
CupertinoApp
(
home:
Center
(
child:
CupertinoDesktopTextSelectionToolbarButton
.
text
(
text:
'Tap me'
,
onPressed:
()
{},
),
),
),
);
final
BuildContext
context
=
tester
.
element
(
find
.
byType
(
CupertinoDesktopTextSelectionToolbarButton
));
// The Text color is a CupertinoDynamicColor so we have to compare the color
// values instead of just comparing the colors themselves.
expect
(
(
tester
.
firstWidget
(
find
.
text
(
'Tap me'
))
as
Text
).
style
!.
color
!.
value
,
CupertinoColors
.
black
.
value
,
);
// Hover gesture
final
TestGesture
gesture
=
await
tester
.
createGesture
(
kind:
PointerDeviceKind
.
mouse
);
await
gesture
.
addPointer
(
location:
Offset
.
zero
);
addTearDown
(
gesture
.
removePointer
);
await
tester
.
pump
();
await
gesture
.
moveTo
(
tester
.
getCenter
(
find
.
byType
(
CupertinoDesktopTextSelectionToolbarButton
)));
await
tester
.
pumpAndSettle
();
// The color here should be a standard Color, there's no need to use value.
expect
(
(
tester
.
firstWidget
(
find
.
text
(
'Tap me'
))
as
Text
).
style
!.
color
,
CupertinoTheme
.
of
(
context
).
primaryContrastingColor
,
);
});
testWidgets
(
'pressedOpacity defaults to 0.1'
,
(
WidgetTester
tester
)
async
{
await
tester
.
pumpWidget
(
CupertinoApp
(
...
...
@@ -49,7 +90,8 @@ void main() {
expect
(
opacity
.
opacity
.
value
,
1.0
);
// Make a "down" gesture on the button.
final
Offset
center
=
tester
.
getCenter
(
find
.
byType
(
CupertinoDesktopTextSelectionToolbarButton
));
final
Offset
center
=
tester
.
getCenter
(
find
.
byType
(
CupertinoDesktopTextSelectionToolbarButton
));
final
TestGesture
gesture
=
await
tester
.
startGesture
(
center
);
await
tester
.
pumpAndSettle
();
...
...
packages/flutter/test/cupertino/desktop_text_selection_toolbar_test.dart
View file @
f86b9220
...
...
@@ -2,12 +2,120 @@
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
import
'dart:ui'
;
import
'package:flutter/cupertino.dart'
;
import
'package:flutter/foundation.dart'
;
import
'package:flutter_test/flutter_test.dart'
;
void
main
(
)
{
TestWidgetsFlutterBinding
.
ensureInitialized
();
testWidgets
(
'has correct backdrop filters'
,
(
WidgetTester
tester
)
async
{
await
tester
.
pumpWidget
(
CupertinoApp
(
home:
Center
(
child:
CupertinoDesktopTextSelectionToolbar
(
anchor:
Offset
.
zero
,
children:
<
Widget
>[
CupertinoDesktopTextSelectionToolbarButton
(
child:
const
Text
(
'Tap me'
),
onPressed:
()
{},
),
],
),
),
),
);
final
BackdropFilter
toolbarFilter
=
tester
.
firstWidget
<
BackdropFilter
>(
find
.
descendant
(
of:
find
.
byType
(
CupertinoDesktopTextSelectionToolbar
),
matching:
find
.
byType
(
BackdropFilter
),
),
);
expect
(
toolbarFilter
.
filter
.
runtimeType
,
// _ComposeImageFilter is internal so we can't test if its filters are
// for blur and saturation, but checking if it's a _ComposeImageFilter
// should be enough. Outer and inner parameters don't matter, we just need
// a new _ComposeImageFilter to get its runtimeType.
//
// As web doesn't support ImageFilter.compose, we use just blur when
// kIsWeb.
kIsWeb
?
ImageFilter
.
blur
().
runtimeType
:
ImageFilter
.
compose
(
outer:
ImageFilter
.
blur
(),
inner:
ImageFilter
.
blur
(),
).
runtimeType
,
);
});
testWidgets
(
'has shadow'
,
(
WidgetTester
tester
)
async
{
await
tester
.
pumpWidget
(
CupertinoApp
(
home:
Center
(
child:
CupertinoDesktopTextSelectionToolbar
(
anchor:
Offset
.
zero
,
children:
<
Widget
>[
CupertinoDesktopTextSelectionToolbarButton
(
child:
const
Text
(
'Tap me'
),
onPressed:
()
{},
),
],
),
),
),
);
final
DecoratedBox
decoratedBox
=
tester
.
firstWidget
<
DecoratedBox
>(
find
.
descendant
(
of:
find
.
byType
(
CupertinoDesktopTextSelectionToolbar
),
matching:
find
.
byType
(
DecoratedBox
),
),
);
expect
(
(
decoratedBox
.
decoration
as
BoxDecoration
).
boxShadow
,
isNotNull
,
);
});
testWidgets
(
'is translucent'
,
(
WidgetTester
tester
)
async
{
await
tester
.
pumpWidget
(
CupertinoApp
(
home:
Center
(
child:
CupertinoDesktopTextSelectionToolbar
(
anchor:
Offset
.
zero
,
children:
<
Widget
>[
CupertinoDesktopTextSelectionToolbarButton
(
child:
const
Text
(
'Tap me'
),
onPressed:
()
{},
),
],
),
),
),
);
final
DecoratedBox
decoratedBox
=
tester
.
widgetList
<
DecoratedBox
>(
find
.
descendant
(
of:
find
.
byType
(
CupertinoDesktopTextSelectionToolbar
),
matching:
find
.
byType
(
DecoratedBox
),
),
)
// The second DecoratedBox should be the one with color.
.
elementAt
(
1
);
expect
(
(
decoratedBox
.
decoration
as
BoxDecoration
).
color
!.
opacity
,
lessThan
(
1.0
),
);
});
testWidgets
(
'positions itself at the anchor'
,
(
WidgetTester
tester
)
async
{
// An arbitrary point on the screen to position at.
const
Offset
anchor
=
Offset
(
30.0
,
40.0
);
...
...
@@ -29,7 +137,8 @@ void main() {
);
expect
(
tester
.
getTopLeft
(
find
.
byType
(
CupertinoDesktopTextSelectionToolbarButton
)),
tester
.
getTopLeft
(
find
.
byType
(
CupertinoDesktopTextSelectionToolbarButton
)),
// Greater than due to padding internal to the toolbar.
greaterThan
(
anchor
),
);
...
...
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