mirror of
https://github.com/HChaZZY/PiliPlus.git
synced 2025-12-06 09:13:48 +08:00
345 lines
12 KiB
Dart
345 lines
12 KiB
Dart
import 'dart:ui' as ui;
|
|
|
|
import 'package:PiliPlus/common/widgets/text/paragraph.dart';
|
|
import 'package:flutter/material.dart';
|
|
import 'package:flutter/rendering.dart' hide RenderParagraph;
|
|
|
|
/// A paragraph of rich text.
|
|
///
|
|
/// {@youtube 560 315 https://www.youtube.com/watch?v=rykDVh-QFfw}
|
|
///
|
|
/// The [RichText] widget displays text that uses multiple different styles. The
|
|
/// text to display is described using a tree of [TextSpan] objects, each of
|
|
/// which has an associated style that is used for that subtree. The text might
|
|
/// break across multiple lines or might all be displayed on the same line
|
|
/// depending on the layout constraints.
|
|
///
|
|
/// Text displayed in a [RichText] widget must be explicitly styled. When
|
|
/// picking which style to use, consider using [DefaultTextStyle.of] the current
|
|
/// [BuildContext] to provide defaults. For more details on how to style text in
|
|
/// a [RichText] widget, see the documentation for [TextStyle].
|
|
///
|
|
/// Consider using the [Text] widget to integrate with the [DefaultTextStyle]
|
|
/// automatically. When all the text uses the same style, the default constructor
|
|
/// is less verbose. The [Text.rich] constructor allows you to style multiple
|
|
/// spans with the default text style while still allowing specified styles per
|
|
/// span.
|
|
///
|
|
/// {@tool snippet}
|
|
///
|
|
/// This sample demonstrates how to mix and match text with different text
|
|
/// styles using the [RichText] Widget. It displays the text "Hello bold world,"
|
|
/// emphasizing the word "bold" using a bold font weight.
|
|
///
|
|
/// 
|
|
///
|
|
/// ```dart
|
|
/// RichText(
|
|
/// text: TextSpan(
|
|
/// text: 'Hello ',
|
|
/// style: DefaultTextStyle.of(context).style,
|
|
/// children: const <TextSpan>[
|
|
/// TextSpan(text: 'bold', style: TextStyle(fontWeight: FontWeight.bold)),
|
|
/// TextSpan(text: ' world!'),
|
|
/// ],
|
|
/// ),
|
|
/// )
|
|
/// ```
|
|
/// {@end-tool}
|
|
///
|
|
/// ## Selections
|
|
///
|
|
/// To make this [RichText] Selectable, the [RichText] needs to be in the
|
|
/// subtree of a [SelectionArea] or [SelectableRegion] and a
|
|
/// [SelectionRegistrar] needs to be assigned to the
|
|
/// [RichText.selectionRegistrar]. One can use
|
|
/// [SelectionContainer.maybeOf] to get the [SelectionRegistrar] from a
|
|
/// context. This enables users to select the text in [RichText]s with mice or
|
|
/// touch events.
|
|
///
|
|
/// The [selectionColor] also needs to be set if the selection is enabled to
|
|
/// draw the selection highlights.
|
|
///
|
|
/// {@tool snippet}
|
|
///
|
|
/// This sample demonstrates how to assign a [SelectionRegistrar] for RichTexts
|
|
/// in the SelectionArea subtree.
|
|
///
|
|
/// 
|
|
///
|
|
/// ```dart
|
|
/// RichText(
|
|
/// text: const TextSpan(text: 'Hello'),
|
|
/// selectionRegistrar: SelectionContainer.maybeOf(context),
|
|
/// selectionColor: const Color(0xAF6694e8),
|
|
/// )
|
|
/// ```
|
|
/// {@end-tool}
|
|
///
|
|
/// See also:
|
|
///
|
|
/// * [TextStyle], which discusses how to style text.
|
|
/// * [TextSpan], which is used to describe the text in a paragraph.
|
|
/// * [Text], which automatically applies the ambient styles described by a
|
|
/// [DefaultTextStyle] to a single string.
|
|
/// * [Text.rich], a const text widget that provides similar functionality
|
|
/// as [RichText]. [Text.rich] will inherit [TextStyle] from [DefaultTextStyle].
|
|
/// * [SelectableRegion], which provides an overview of the selection system.
|
|
class RichText extends MultiChildRenderObjectWidget {
|
|
/// Creates a paragraph of rich text.
|
|
///
|
|
/// The [maxLines] property may be null (and indeed defaults to null), but if
|
|
/// it is not null, it must be greater than zero.
|
|
///
|
|
/// The [textDirection], if null, defaults to the ambient [Directionality],
|
|
/// which in that case must not be null.
|
|
RichText({
|
|
super.key,
|
|
required this.text,
|
|
this.textAlign = TextAlign.start,
|
|
this.textDirection,
|
|
this.softWrap = true,
|
|
this.overflow = TextOverflow.clip,
|
|
@Deprecated(
|
|
'Use textScaler instead. '
|
|
'Use of textScaleFactor was deprecated in preparation for the upcoming nonlinear text scaling support. '
|
|
'This feature was deprecated after v3.12.0-2.0.pre.',
|
|
)
|
|
double textScaleFactor = 1.0,
|
|
TextScaler textScaler = TextScaler.noScaling,
|
|
this.maxLines,
|
|
this.locale,
|
|
this.strutStyle,
|
|
this.textWidthBasis = TextWidthBasis.parent,
|
|
this.textHeightBehavior,
|
|
this.selectionRegistrar,
|
|
this.selectionColor,
|
|
this.onShowMore,
|
|
}) : assert(maxLines == null || maxLines > 0),
|
|
assert(selectionRegistrar == null || selectionColor != null),
|
|
assert(
|
|
textScaleFactor == 1.0 || identical(textScaler, TextScaler.noScaling),
|
|
'Use textScaler instead.',
|
|
),
|
|
textScaler = _effectiveTextScalerFrom(textScaler, textScaleFactor),
|
|
super(
|
|
children: WidgetSpan.extractFromInlineSpan(
|
|
text,
|
|
_effectiveTextScalerFrom(textScaler, textScaleFactor),
|
|
),
|
|
);
|
|
|
|
static TextScaler _effectiveTextScalerFrom(
|
|
TextScaler textScaler,
|
|
double textScaleFactor,
|
|
) {
|
|
return switch ((textScaler, textScaleFactor)) {
|
|
(final TextScaler scaler, 1.0) => scaler,
|
|
(TextScaler.noScaling, final double textScaleFactor) => TextScaler.linear(
|
|
textScaleFactor,
|
|
),
|
|
(final TextScaler scaler, _) => scaler,
|
|
};
|
|
}
|
|
|
|
/// The text to display in this widget.
|
|
final InlineSpan text;
|
|
|
|
/// How the text should be aligned horizontally.
|
|
final TextAlign textAlign;
|
|
|
|
/// The directionality of the text.
|
|
///
|
|
/// This decides how [textAlign] values like [TextAlign.start] and
|
|
/// [TextAlign.end] are interpreted.
|
|
///
|
|
/// This is also used to disambiguate how to render bidirectional text. For
|
|
/// example, if the [text] is an English phrase followed by a Hebrew phrase,
|
|
/// in a [TextDirection.ltr] context the English phrase will be on the left
|
|
/// and the Hebrew phrase to its right, while in a [TextDirection.rtl]
|
|
/// context, the English phrase will be on the right and the Hebrew phrase on
|
|
/// its left.
|
|
///
|
|
/// Defaults to the ambient [Directionality], if any. If there is no ambient
|
|
/// [Directionality], then this must not be null.
|
|
final TextDirection? textDirection;
|
|
|
|
/// Whether the text should break at soft line breaks.
|
|
///
|
|
/// If false, the glyphs in the text will be positioned as if there was unlimited horizontal space.
|
|
final bool softWrap;
|
|
|
|
/// How visual overflow should be handled.
|
|
final TextOverflow overflow;
|
|
|
|
/// Deprecated. Will be removed in a future version of Flutter. Use
|
|
/// [textScaler] instead.
|
|
///
|
|
/// The number of font pixels for each logical pixel.
|
|
///
|
|
/// For example, if the text scale factor is 1.5, text will be 50% larger than
|
|
/// the specified font size.
|
|
@Deprecated(
|
|
'Use textScaler instead. '
|
|
'Use of textScaleFactor was deprecated in preparation for the upcoming nonlinear text scaling support. '
|
|
'This feature was deprecated after v3.12.0-2.0.pre.',
|
|
)
|
|
double get textScaleFactor => textScaler.textScaleFactor;
|
|
|
|
/// {@macro flutter.painting.textPainter.textScaler}
|
|
final TextScaler textScaler;
|
|
|
|
/// An optional maximum number of lines for the text to span, wrapping if necessary.
|
|
/// If the text exceeds the given number of lines, it will be truncated according
|
|
/// to [overflow].
|
|
///
|
|
/// If this is 1, text will not wrap. Otherwise, text will be wrapped at the
|
|
/// edge of the box.
|
|
final int? maxLines;
|
|
|
|
/// Used to select a font when the same Unicode character can
|
|
/// be rendered differently, depending on the locale.
|
|
///
|
|
/// It's rarely necessary to set this property. By default its value
|
|
/// is inherited from the enclosing app with `Localizations.localeOf(context)`.
|
|
///
|
|
/// See [RenderParagraph.locale] for more information.
|
|
final Locale? locale;
|
|
|
|
/// {@macro flutter.painting.textPainter.strutStyle}
|
|
final StrutStyle? strutStyle;
|
|
|
|
/// {@macro flutter.painting.textPainter.textWidthBasis}
|
|
final TextWidthBasis textWidthBasis;
|
|
|
|
/// {@macro dart.ui.textHeightBehavior}
|
|
final ui.TextHeightBehavior? textHeightBehavior;
|
|
|
|
/// The [SelectionRegistrar] this rich text is subscribed to.
|
|
///
|
|
/// If this is set, [selectionColor] must be non-null.
|
|
final SelectionRegistrar? selectionRegistrar;
|
|
|
|
/// The color to use when painting the selection.
|
|
///
|
|
/// This is ignored if [selectionRegistrar] is null.
|
|
///
|
|
/// See the section on selections in the [RichText] top-level API
|
|
/// documentation for more details on enabling selection in [RichText]
|
|
/// widgets.
|
|
final Color? selectionColor;
|
|
|
|
final VoidCallback? onShowMore;
|
|
|
|
@override
|
|
RenderParagraph createRenderObject(BuildContext context) {
|
|
assert(textDirection != null || debugCheckHasDirectionality(context));
|
|
return RenderParagraph(
|
|
text,
|
|
textAlign: textAlign,
|
|
textDirection: textDirection ?? Directionality.of(context),
|
|
softWrap: softWrap,
|
|
overflow: overflow,
|
|
textScaler: textScaler,
|
|
maxLines: maxLines,
|
|
strutStyle: strutStyle,
|
|
textWidthBasis: textWidthBasis,
|
|
textHeightBehavior: textHeightBehavior,
|
|
locale: locale ?? Localizations.maybeLocaleOf(context),
|
|
registrar: selectionRegistrar,
|
|
selectionColor: selectionColor,
|
|
primary: Theme.of(context).colorScheme.primary,
|
|
onShowMore: onShowMore,
|
|
);
|
|
}
|
|
|
|
@override
|
|
void updateRenderObject(BuildContext context, RenderParagraph renderObject) {
|
|
assert(textDirection != null || debugCheckHasDirectionality(context));
|
|
renderObject
|
|
..text = text
|
|
..textAlign = textAlign
|
|
..textDirection = textDirection ?? Directionality.of(context)
|
|
..softWrap = softWrap
|
|
..overflow = overflow
|
|
..textScaler = textScaler
|
|
..maxLines = maxLines
|
|
..strutStyle = strutStyle
|
|
..textWidthBasis = textWidthBasis
|
|
..textHeightBehavior = textHeightBehavior
|
|
..locale = locale ?? Localizations.maybeLocaleOf(context)
|
|
..registrar = selectionRegistrar
|
|
..selectionColor = selectionColor
|
|
..primary = Theme.of(context).colorScheme.primary
|
|
..onShowMore = onShowMore;
|
|
}
|
|
|
|
@override
|
|
void debugFillProperties(DiagnosticPropertiesBuilder properties) {
|
|
super.debugFillProperties(properties);
|
|
properties.add(
|
|
EnumProperty<TextAlign>(
|
|
'textAlign',
|
|
textAlign,
|
|
defaultValue: TextAlign.start,
|
|
),
|
|
);
|
|
properties.add(
|
|
EnumProperty<TextDirection>(
|
|
'textDirection',
|
|
textDirection,
|
|
defaultValue: null,
|
|
),
|
|
);
|
|
properties.add(
|
|
FlagProperty(
|
|
'softWrap',
|
|
value: softWrap,
|
|
ifTrue: 'wrapping at box width',
|
|
ifFalse: 'no wrapping except at line break characters',
|
|
showName: true,
|
|
),
|
|
);
|
|
properties.add(
|
|
EnumProperty<TextOverflow>(
|
|
'overflow',
|
|
overflow,
|
|
defaultValue: TextOverflow.clip,
|
|
),
|
|
);
|
|
properties.add(
|
|
DiagnosticsProperty<TextScaler>(
|
|
'textScaler',
|
|
textScaler,
|
|
defaultValue: TextScaler.noScaling,
|
|
),
|
|
);
|
|
properties.add(IntProperty('maxLines', maxLines, ifNull: 'unlimited'));
|
|
properties.add(
|
|
EnumProperty<TextWidthBasis>(
|
|
'textWidthBasis',
|
|
textWidthBasis,
|
|
defaultValue: TextWidthBasis.parent,
|
|
),
|
|
);
|
|
properties.add(StringProperty('text', text.toPlainText()));
|
|
properties.add(
|
|
DiagnosticsProperty<Locale>('locale', locale, defaultValue: null),
|
|
);
|
|
properties.add(
|
|
DiagnosticsProperty<StrutStyle>(
|
|
'strutStyle',
|
|
strutStyle,
|
|
defaultValue: null,
|
|
),
|
|
);
|
|
properties.add(
|
|
DiagnosticsProperty<TextHeightBehavior>(
|
|
'textHeightBehavior',
|
|
textHeightBehavior,
|
|
defaultValue: null,
|
|
),
|
|
);
|
|
}
|
|
}
|