// Copyright 2014 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. import 'dart:ui'; import 'package:flutter/foundation.dart'; import 'package:flutter/widgets.dart'; import 'colors.dart'; // The minimum padding from all edges of the selection toolbar to all edges of // the screen. const double _kToolbarScreenPadding = 8.0; // 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(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 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(0xB2FFFFFF), darkColor: Color(0xB2303030), ); /// A macOS-style text selection toolbar. /// /// Typically displays buttons for text manipulation, e.g. copying and pasting /// text. /// /// Tries to position itself as closely as possible to [anchor] while remaining /// fully inside the viewport. /// /// See also: /// /// * [CupertinoAdaptiveTextSelectionToolbar], where this is used to build the /// toolbar for desktop platforms. /// * [AdaptiveTextSelectionToolbar], where this is used to build the toolbar on /// macOS. /// * [DesktopTextSelectionToolbar], which is similar but builds a /// Material-style desktop toolbar. class CupertinoDesktopTextSelectionToolbar extends StatelessWidget { /// Creates a const instance of CupertinoTextSelectionToolbar. const CupertinoDesktopTextSelectionToolbar({ super.key, required this.anchor, 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; /// {@macro flutter.material.TextSelectionToolbar.children} /// /// See also: /// * [CupertinoDesktopTextSelectionToolbarButton], which builds a default /// macOS-style text selection toolbar text button. final List<Widget> children; // Builds a toolbar just like the default Mac toolbar, with the right color // background, padding, and rounded corners. static Widget _defaultToolbarBuilder(BuildContext context, Widget child) { return Container( width: _kToolbarWidth, clipBehavior: Clip.hardEdge, decoration: const BoxDecoration( boxShadow: _kToolbarShadow, borderRadius: BorderRadius.all(_kToolbarBorderRadius), ), 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, ), ), ), ); } @override Widget build(BuildContext context) { assert(debugCheckHasMediaQuery(context)); final double paddingAbove = MediaQuery.paddingOf(context).top + _kToolbarScreenPadding; final Offset localAdjustment = Offset(_kToolbarScreenPadding, paddingAbove); return Padding( padding: EdgeInsets.fromLTRB( _kToolbarScreenPadding, paddingAbove, _kToolbarScreenPadding, _kToolbarScreenPadding, ), child: CustomSingleChildLayout( delegate: DesktopTextSelectionToolbarLayoutDelegate( anchor: anchor - localAdjustment, ), child: _defaultToolbarBuilder( context, Column( mainAxisSize: MainAxisSize.min, children: children, ), ), ), ); } }