mirror of
https://github.com/bggRGjQaUbCoE/PiliPlus.git
synced 2026-06-02 09:08:17 +08:00
@@ -2,13 +2,15 @@
|
||||
// Use of this source code is governed by a BSD-style license that can be
|
||||
// found in the LICENSE file.
|
||||
|
||||
// ignore_for_file: prefer_initializing_formals
|
||||
|
||||
import 'dart:async';
|
||||
import 'dart:math' as math;
|
||||
|
||||
import 'package:flutter/cupertino.dart';
|
||||
import 'package:flutter/foundation.dart';
|
||||
import 'package:flutter/gestures.dart';
|
||||
import 'package:flutter/material.dart';
|
||||
import 'package:flutter/material.dart' hide Slider;
|
||||
import 'package:flutter/rendering.dart';
|
||||
import 'package:flutter/scheduler.dart' show timeDilation;
|
||||
import 'package:flutter/services.dart';
|
||||
@@ -1019,6 +1021,7 @@ class _VerticalSliderState extends State<VerticalSlider>
|
||||
onChangeEnd: _handleDragEnd,
|
||||
state: this,
|
||||
semanticFormatterCallback: widget.semanticFormatterCallback,
|
||||
onDidGainAccessibilityFocus: handleDidGainAccessibilityFocus,
|
||||
hasFocus: _focused,
|
||||
hovering: _hovering,
|
||||
allowedInteraction: effectiveAllowedInteraction,
|
||||
@@ -1037,22 +1040,17 @@ class _VerticalSliderState extends State<VerticalSlider>
|
||||
child: result,
|
||||
);
|
||||
|
||||
return Semantics(
|
||||
label: widget.label,
|
||||
container: true,
|
||||
slider: true,
|
||||
onDidGainAccessibilityFocus: handleDidGainAccessibilityFocus,
|
||||
child: FocusableActionDetector(
|
||||
actions: _actionMap,
|
||||
shortcuts: shortcutMap,
|
||||
focusNode: focusNode,
|
||||
autofocus: widget.autofocus,
|
||||
enabled: _enabled,
|
||||
onShowFocusHighlight: _handleFocusHighlightChanged,
|
||||
onShowHoverHighlight: _handleHoverChanged,
|
||||
mouseCursor: effectiveMouseCursor,
|
||||
child: result,
|
||||
),
|
||||
return FocusableActionDetector(
|
||||
actions: _actionMap,
|
||||
shortcuts: shortcutMap,
|
||||
focusNode: focusNode,
|
||||
autofocus: widget.autofocus,
|
||||
enabled: _enabled,
|
||||
onShowFocusHighlight: _handleFocusHighlightChanged,
|
||||
onShowHoverHighlight: _handleHoverChanged,
|
||||
mouseCursor: effectiveMouseCursor,
|
||||
includeFocusSemantics: false,
|
||||
child: result,
|
||||
);
|
||||
}
|
||||
|
||||
@@ -1111,6 +1109,7 @@ class _SliderRenderObjectWidget extends LeafRenderObjectWidget {
|
||||
required this.onChangeEnd,
|
||||
required this.state,
|
||||
required this.semanticFormatterCallback,
|
||||
required this.onDidGainAccessibilityFocus,
|
||||
required this.hasFocus,
|
||||
required this.hovering,
|
||||
required this.allowedInteraction,
|
||||
@@ -1127,6 +1126,7 @@ class _SliderRenderObjectWidget extends LeafRenderObjectWidget {
|
||||
final ValueChanged<double>? onChangeStart;
|
||||
final ValueChanged<double>? onChangeEnd;
|
||||
final SemanticFormatterCallback? semanticFormatterCallback;
|
||||
final VoidCallback? onDidGainAccessibilityFocus;
|
||||
final _VerticalSliderState state;
|
||||
final bool hasFocus;
|
||||
final bool hovering;
|
||||
@@ -1148,6 +1148,7 @@ class _SliderRenderObjectWidget extends LeafRenderObjectWidget {
|
||||
state: state,
|
||||
textDirection: Directionality.of(context),
|
||||
semanticFormatterCallback: semanticFormatterCallback,
|
||||
onDidGainAccessibilityFocus: onDidGainAccessibilityFocus,
|
||||
platform: Theme.of(context).platform,
|
||||
hasFocus: hasFocus,
|
||||
hovering: hovering,
|
||||
@@ -1173,6 +1174,7 @@ class _SliderRenderObjectWidget extends LeafRenderObjectWidget {
|
||||
..onChangeEnd = onChangeEnd
|
||||
..textDirection = Directionality.of(context)
|
||||
..semanticFormatterCallback = semanticFormatterCallback
|
||||
..onDidGainAccessibilityFocus = onDidGainAccessibilityFocus
|
||||
..platform = Theme.of(context).platform
|
||||
..hasFocus = hasFocus
|
||||
..hovering = hovering
|
||||
@@ -1195,6 +1197,7 @@ class _RenderSlider extends RenderBox with RelayoutWhenSystemFontsChangeMixin {
|
||||
required TargetPlatform platform,
|
||||
required ValueChanged<double>? onChanged,
|
||||
required SemanticFormatterCallback? semanticFormatterCallback,
|
||||
required this.onDidGainAccessibilityFocus,
|
||||
required this.onChangeStart,
|
||||
required this.onChangeEnd,
|
||||
required _VerticalSliderState state,
|
||||
@@ -1292,6 +1295,7 @@ class _RenderSlider extends RenderBox with RelayoutWhenSystemFontsChangeMixin {
|
||||
late VerticalDragGestureRecognizer _drag;
|
||||
late TapGestureRecognizer _tap;
|
||||
bool _active = false;
|
||||
VoidCallback? onDidGainAccessibilityFocus;
|
||||
double _currentDragValue = 0.0;
|
||||
Rect? overlayRect;
|
||||
|
||||
@@ -1808,31 +1812,12 @@ class _RenderSlider extends RenderBox with RelayoutWhenSystemFontsChangeMixin {
|
||||
sliderTheme: _sliderTheme,
|
||||
isDiscrete: isDiscrete,
|
||||
);
|
||||
final double padding = _sliderTheme.trackShape!.isRounded
|
||||
? trackRect.width
|
||||
: 0.0;
|
||||
final double thumbPosition = isDiscrete
|
||||
? trackRect.left +
|
||||
visualPosition * (trackRect.width - padding) +
|
||||
padding / 2
|
||||
: trackRect.bottom - visualPosition * trackRect.height;
|
||||
// Apply padding to trackRect.left and trackRect.right if the track height is
|
||||
// greater than the thumb radius to ensure the thumb is drawn within the track.
|
||||
final Size thumbPreferredSize = _sliderTheme.thumbShape!.getPreferredSize(
|
||||
isInteractive,
|
||||
isDiscrete,
|
||||
);
|
||||
final double thumbPadding = (padding > thumbPreferredSize.width / 2
|
||||
? padding / 2
|
||||
: 0);
|
||||
final thumbCenter = Offset(
|
||||
trackRect.center.dx,
|
||||
clampDouble(
|
||||
thumbPosition,
|
||||
trackRect.top + thumbPadding,
|
||||
trackRect.bottom - thumbPadding,
|
||||
),
|
||||
|
||||
final Offset thumbCenter = _calcThumbCenter(
|
||||
trackRect: trackRect,
|
||||
visualPosition: visualPosition,
|
||||
);
|
||||
|
||||
if (isInteractive) {
|
||||
final Size overlaySize = sliderTheme.overlayShape!.getPreferredSize(
|
||||
isInteractive,
|
||||
@@ -1940,33 +1925,35 @@ class _RenderSlider extends RenderBox with RelayoutWhenSystemFontsChangeMixin {
|
||||
}
|
||||
}
|
||||
|
||||
if (isInteractive && label != null) {
|
||||
if ((shouldShowValueIndicatorWhenDragged &&
|
||||
!_valueIndicatorAnimation.isDismissed) ||
|
||||
shouldAlwaysShowValueIndicator) {
|
||||
_state.paintValueIndicator = (PaintingContext context, Offset offset) {
|
||||
if (attached && _labelPainter.text != null) {
|
||||
_sliderTheme.valueIndicatorShape!.paint(
|
||||
context,
|
||||
offset + thumbCenter,
|
||||
activationAnimation: shouldAlwaysShowValueIndicator
|
||||
? const AlwaysStoppedAnimation<double>(1)
|
||||
: _valueIndicatorAnimation,
|
||||
enableAnimation: shouldAlwaysShowValueIndicator
|
||||
? const AlwaysStoppedAnimation<double>(1)
|
||||
: _enableAnimation,
|
||||
isDiscrete: isDiscrete,
|
||||
labelPainter: _labelPainter,
|
||||
parentBox: this,
|
||||
sliderTheme: _sliderTheme,
|
||||
textDirection: _textDirection,
|
||||
value: _value,
|
||||
textScaleFactor: textScaleFactor,
|
||||
sizeWithOverflow: screenSize.isEmpty ? size : screenSize,
|
||||
);
|
||||
}
|
||||
};
|
||||
}
|
||||
if (isInteractive &&
|
||||
label != null &&
|
||||
((shouldShowValueIndicatorWhenDragged &&
|
||||
!_valueIndicatorAnimation.isDismissed) ||
|
||||
shouldAlwaysShowValueIndicator)) {
|
||||
_state.paintValueIndicator = (PaintingContext context, Offset offset) {
|
||||
if (attached && _labelPainter.text != null) {
|
||||
_sliderTheme.valueIndicatorShape?.paint(
|
||||
context,
|
||||
offset + thumbCenter,
|
||||
activationAnimation: shouldAlwaysShowValueIndicator
|
||||
? const AlwaysStoppedAnimation<double>(1)
|
||||
: _valueIndicatorAnimation,
|
||||
enableAnimation: shouldAlwaysShowValueIndicator
|
||||
? const AlwaysStoppedAnimation<double>(1)
|
||||
: _enableAnimation,
|
||||
isDiscrete: isDiscrete,
|
||||
labelPainter: _labelPainter,
|
||||
parentBox: this,
|
||||
sliderTheme: _sliderTheme,
|
||||
textDirection: _textDirection,
|
||||
value: _value,
|
||||
textScaleFactor: textScaleFactor,
|
||||
sizeWithOverflow: screenSize.isEmpty ? size : screenSize,
|
||||
);
|
||||
}
|
||||
};
|
||||
} else {
|
||||
_state.paintValueIndicator = null;
|
||||
}
|
||||
|
||||
_sliderTheme.thumbShape!.paint(
|
||||
@@ -1991,27 +1978,96 @@ class _RenderSlider extends RenderBox with RelayoutWhenSystemFontsChangeMixin {
|
||||
);
|
||||
}
|
||||
|
||||
/// Calculates the local coordinate center of the [Slider] thumb given its
|
||||
/// physical placement on the track from 0.0 (left) to 1.0 (right).
|
||||
///
|
||||
/// The [visualPosition] is provided by the caller so semantics can use the
|
||||
/// raw logical value while paint can use the smoothly animated value.
|
||||
Offset _calcThumbCenter({
|
||||
required Rect trackRect,
|
||||
required double visualPosition,
|
||||
}) {
|
||||
final double padding = _sliderTheme.trackShape!.isRounded
|
||||
? trackRect.width
|
||||
: 0.0;
|
||||
final double thumbPosition = isDiscrete
|
||||
? trackRect.left +
|
||||
visualPosition * (trackRect.width - padding) +
|
||||
padding / 2
|
||||
: trackRect.bottom - visualPosition * trackRect.height;
|
||||
// Apply padding to trackRect.left and trackRect.right if the track height is
|
||||
// greater than the thumb radius to ensure the thumb is drawn within the track.
|
||||
final Size thumbPreferredSize = _sliderTheme.thumbShape!.getPreferredSize(
|
||||
isInteractive,
|
||||
isDiscrete,
|
||||
);
|
||||
final double thumbPadding = padding > thumbPreferredSize.width / 2
|
||||
? padding / 2
|
||||
: 0;
|
||||
return Offset(
|
||||
trackRect.center.dx,
|
||||
clampDouble(
|
||||
thumbPosition,
|
||||
trackRect.top + thumbPadding,
|
||||
trackRect.bottom - thumbPadding,
|
||||
),
|
||||
);
|
||||
}
|
||||
|
||||
Offset get _semanticThumbCenter {
|
||||
final double visualPosition = switch (textDirection) {
|
||||
TextDirection.rtl => 1.0 - _value,
|
||||
TextDirection.ltr => _value,
|
||||
};
|
||||
return _calcThumbCenter(
|
||||
trackRect: _trackRect,
|
||||
visualPosition: visualPosition,
|
||||
);
|
||||
}
|
||||
|
||||
@override
|
||||
void assembleSemanticsNode(
|
||||
SemanticsNode node,
|
||||
SemanticsConfiguration config,
|
||||
Iterable<SemanticsNode> children,
|
||||
) {
|
||||
node
|
||||
..rect = Rect.fromCenter(
|
||||
center: _semanticThumbCenter,
|
||||
width: kMinInteractiveDimension,
|
||||
height: kMinInteractiveDimension,
|
||||
)
|
||||
..updateWith(config: config);
|
||||
}
|
||||
|
||||
@override
|
||||
void describeSemanticsConfiguration(SemanticsConfiguration config) {
|
||||
super.describeSemanticsConfiguration(config);
|
||||
|
||||
// The Slider widget has its own Focus widget with semantics information,
|
||||
// The Slider widget has its own Focus widget.
|
||||
// We mark the Focus widget with "includeFocusSemantics: false"
|
||||
// and we want that semantics node to collect the semantics information here
|
||||
// so that it's all in the same node: otherwise Talkback sees that the node
|
||||
// has focusable children, and it won't focus the Slider's Focus widget
|
||||
// because it thinks the Focus widget's node doesn't have anything to say
|
||||
// (which it doesn't, but this child does). Aggregating the semantic
|
||||
// information into one node means that Talkback will recognize that it has
|
||||
// something to say and focus it when it receives keyboard focus.
|
||||
// (See https://github.com/flutter/flutter/issues/57038 for context).
|
||||
// so that it's all in the same node.
|
||||
config
|
||||
..isSemanticBoundary = false
|
||||
..isEnabled = isInteractive
|
||||
..textDirection = textDirection;
|
||||
..isSemanticBoundary = true
|
||||
..isEnabled = isInteractive;
|
||||
if (label != null) {
|
||||
config.label = label!;
|
||||
}
|
||||
config
|
||||
..isSlider = true
|
||||
..isFocusable = isInteractive
|
||||
..isFocused = hasFocus;
|
||||
|
||||
if (onDidGainAccessibilityFocus != null) {
|
||||
config.onDidGainAccessibilityFocus = onDidGainAccessibilityFocus;
|
||||
}
|
||||
config.textDirection = textDirection;
|
||||
if (isInteractive) {
|
||||
config
|
||||
..onIncrease = increaseAction
|
||||
..onDecrease = decreaseAction;
|
||||
..onDecrease = decreaseAction
|
||||
..onFocus = onFocusAction;
|
||||
}
|
||||
|
||||
if (semanticFormatterCallback != null) {
|
||||
@@ -2036,6 +2092,17 @@ class _RenderSlider extends RenderBox with RelayoutWhenSystemFontsChangeMixin {
|
||||
double get _semanticActionUnit =>
|
||||
divisions != null ? 1.0 / divisions! : _adjustmentUnit;
|
||||
|
||||
void onFocusAction() {
|
||||
if (isInteractive) {
|
||||
if (!_state.mounted) {
|
||||
return;
|
||||
}
|
||||
if (!hasFocus) {
|
||||
_state.focusNode.requestFocus();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void increaseAction() {
|
||||
if (isInteractive) {
|
||||
onChangeStart!(currentValue);
|
||||
|
||||
Reference in New Issue
Block a user