Skip to content
Open
Show file tree
Hide file tree
Changes from 4 commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
13 changes: 12 additions & 1 deletion super_editor/lib/src/core/styles.dart
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,7 @@ import 'package:flutter/painting.dart';
import 'package:super_editor/src/default_editor/text/custom_underlines.dart';
import 'package:super_editor/src/infrastructure/attributed_text_styles.dart';

import 'document.dart';
import 'package:super_editor/src/core/document.dart';

/// Stylesheet for styling content within a document.
///
Expand All @@ -17,6 +17,7 @@ class Stylesheet {
required this.inlineTextStyler,
this.inlineWidgetBuilders = const [],
this.selectedTextColorStrategy,
this.inheritDefaultTextStyle = false,
});

/// Padding applied around the interior edge of the document.
Expand All @@ -36,6 +37,14 @@ class Stylesheet {
/// The strategy that chooses the color for selected text.
final SelectedTextColorStrategy? selectedTextColorStrategy;

/// Whether to inherit the default text style from the context.
///
/// If `true` the [TextStyle] obtained from the [rules] will be merged with
/// the closest enclosing [DefaultTextStyle].
///
/// Defaults to `false`.
final bool inheritDefaultTextStyle;

/// Priority-order list of style rules.
final List<StyleRule> rules;

Expand All @@ -44,6 +53,7 @@ class Stylesheet {
AttributionStyleAdjuster? inlineTextStyler,
InlineWidgetBuilderChain? inlineWidgetBuilders,
SelectedTextColorStrategy? selectedTextColorStrategy,
bool? inheritDefaultTextStyle,
List<StyleRule> addRulesBefore = const [],
List<StyleRule>? rules,
List<StyleRule> addRulesAfter = const [],
Expand All @@ -53,6 +63,7 @@ class Stylesheet {
inlineTextStyler: inlineTextStyler ?? this.inlineTextStyler,
inlineWidgetBuilders: inlineWidgetBuilders ?? this.inlineWidgetBuilders,
selectedTextColorStrategy: selectedTextColorStrategy ?? this.selectedTextColorStrategy,
inheritDefaultTextStyle: inheritDefaultTextStyle ?? this.inheritDefaultTextStyle,
rules: [
...addRulesBefore,
...(rules ?? this.rules),
Expand Down
Original file line number Diff line number Diff line change
@@ -1,14 +1,16 @@
import 'package:flutter/painting.dart';
import 'package:super_editor/src/core/styles.dart';

import '../../core/document.dart';
import '_presenter.dart';
import 'package:super_editor/src/core/document.dart';
import 'package:super_editor/src/default_editor/layout_single_column/_presenter.dart';

/// Style phase that applies a given [Stylesheet] to the document view model.
class SingleColumnStylesheetStyler extends SingleColumnLayoutStylePhase {
SingleColumnStylesheetStyler({
required Stylesheet stylesheet,
}) : _stylesheet = stylesheet;
TextStyle? defaultTextStyle,
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Please add a constructor Dart Doc that explains what should be passed for defaultTextStyle and what happens if nothing is passed for it.

Copy link
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Updated.

}) : _stylesheet = stylesheet,
_defaultTextStyle = defaultTextStyle;

Stylesheet _stylesheet;

Expand All @@ -30,6 +32,28 @@ class SingleColumnStylesheetStyler extends SingleColumnLayoutStylePhase {
markDirty();
}

TextStyle? _defaultTextStyle;

/// Sets the [TextStyle] that's used by this styler to merge with
/// the styles obtained by the stylesheet's rules to each component.
///
/// If [newDefaultTextStyle] is the same as the existing default text style,
/// this method does nothing.
///
/// If [newDefaultTextStyle] is different than the existing default text style,
/// this method marks this style phase a dirty, which will cause the associated presenter
/// to re-run this style phase, and all presentation phases after it.
///
/// Has no effect if [Stylesheet.inheritDefaultTextStyle] is `false`.
set defaultTextStyle(TextStyle? newDefaultTextStyle) {
if (newDefaultTextStyle == _defaultTextStyle) {
return;
}

_defaultTextStyle = newDefaultTextStyle;
markDirty();
}

@override
SingleColumnLayoutViewModel style(Document document, SingleColumnLayoutViewModel viewModel) {
return SingleColumnLayoutViewModel(
Expand All @@ -56,6 +80,17 @@ class SingleColumnStylesheetStyler extends SingleColumnLayoutStylePhase {
Styles.inlineTextStyler: _stylesheet.inlineTextStyler,
Styles.inlineWidgetBuilders: _stylesheet.inlineWidgetBuilders,
};

if (_stylesheet.inheritDefaultTextStyle && _defaultTextStyle != null) {
// We have a default text style, use it as the base for all text
// styles. If the stylesheet has rules that apply text styles,
// those rules will merge with the default text style, overriding
// any conflicting styles. For example, if both the default text style
// and a stylesheet rule specify the font family, the rule's font family
// will be used.
aggregateStyles[Styles.textStyle] = _defaultTextStyle!;
}

for (final rule in _stylesheet.rules) {
if (rule.selector.matches(document, node)) {
_mergeStyles(
Expand Down
19 changes: 17 additions & 2 deletions super_editor/lib/src/default_editor/super_editor.dart
Original file line number Diff line number Diff line change
Expand Up @@ -461,7 +461,19 @@ class SuperEditorState extends State<SuperEditor> {
);

_createEditContext();
_createLayoutPresenter();
}

@override
void didChangeDependencies() {
super.didChangeDependencies();

if (_docLayoutPresenter == null) {
_createLayoutPresenter();
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Instead of adding this conditional logic, can we create the layout presenter with a null default text style and then update it here without any conditional at all?

Copy link
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I added this logic because there are some situations where we re-create the presenter inside didUpdateWidget. For example, when the stylesheet changes.

It we always create the presenter with a null default textstyle, then we need to replicate the code that sets the default text style everywhere we create the presenter.

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I didn't follow that explanation. Can you provide an example of what you mean?

Copy link
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The first code snippet you included doesn't seem to reference the layout presenter at all. The second code snippet calls a method to create the layout presenter. Neither of these code snippets appear to show code that would need to be changed?

It would be great if you could show exactly what code snippet exists now, and exactly what code snippet it would need to become, so that we can avoid any confusion about what you're trying to say.

Copy link
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Both snippets call _createLayoutPresenter. Each one would need to change to the following:

_createLayoutPresenter();
_docStylesheetStyler.defaultTextStyle = DefaultTextStyle.of(context).style;

} else if (widget.stylesheet.inheritDefaultTextStyle) {
// The default text style might have changed. Update it in the stylesheet styler.
final defaultTextStyle = DefaultTextStyle.of(context).style;
_docStylesheetStyler.defaultTextStyle = defaultTextStyle;
}
}

@override
Expand Down Expand Up @@ -590,7 +602,10 @@ class SuperEditorState extends State<SuperEditor> {

final document = editContext.document;

_docStylesheetStyler = SingleColumnStylesheetStyler(stylesheet: widget.stylesheet);
_docStylesheetStyler = SingleColumnStylesheetStyler(
stylesheet: widget.stylesheet,
defaultTextStyle: DefaultTextStyle.of(context).style,
);

_docLayoutPerComponentBlockStyler = SingleColumnLayoutCustomComponentStyler();

Expand Down
19 changes: 15 additions & 4 deletions super_editor/lib/src/infrastructure/attributed_text_styles.dart
Original file line number Diff line number Diff line change
Expand Up @@ -21,22 +21,33 @@ extension ComputeTextSpan on AttributedText {
///
/// The given [inlineWidgetBuilders] interprets every placeholder `Object`
/// and builds a corresponding inline widget.
///
/// If [defaultTextStyle] is non-`null`, the resulting [TextStyle]s
/// will be merged with it.
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Let's try to never say "merged" because it always begs the question as to which style wins out.

Copy link
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Updated.

InlineSpan computeInlineSpan(
BuildContext context,
AttributionStyleBuilder styleBuilder,
InlineWidgetBuilderChain inlineWidgetBuilders,
) {
InlineWidgetBuilderChain inlineWidgetBuilders, {
TextStyle? defaultTextStyle,
}) {
if (isEmpty) {
// There is no text and therefore no attributions.
return TextSpan(text: '', style: styleBuilder({}));
return TextSpan(
text: '',
style: defaultTextStyle != null //
? defaultTextStyle.merge(styleBuilder({}))
: styleBuilder({}),
);
}

final inlineSpans = <InlineSpan>[];

final collapsedSpans = spans.collapseSpans(contentLength: length);

for (final span in collapsedSpans) {
final textStyle = styleBuilder(span.attributions);
final textStyle = defaultTextStyle != null
? defaultTextStyle.merge(styleBuilder(span.attributions))
: styleBuilder(span.attributions);

// A single span might be divided in multiple inline spans if there are placeholders.
// Keep track of the start of the current inline span.
Expand Down
14 changes: 13 additions & 1 deletion super_editor/lib/src/super_reader/super_reader.dart
Original file line number Diff line number Diff line change
Expand Up @@ -270,8 +270,19 @@ class SuperReaderState extends State<SuperReader> {
_docLayoutKey = widget.documentLayoutKey ?? GlobalKey();

_createReaderContext();
}

@override
void didChangeDependencies() {
super.didChangeDependencies();

_createLayoutPresenter();
if (_docLayoutPresenter == null) {
_createLayoutPresenter();
} else if (widget.stylesheet.inheritDefaultTextStyle) {
// The default text style might have changed. Update it in the stylesheet styler.
final defaultTextStyle = DefaultTextStyle.of(context).style;
_docStylesheetStyler.defaultTextStyle = defaultTextStyle;
}
}

@override
Expand Down Expand Up @@ -326,6 +337,7 @@ class SuperReaderState extends State<SuperReader> {

_docStylesheetStyler = SingleColumnStylesheetStyler(
stylesheet: widget.stylesheet,
defaultTextStyle: DefaultTextStyle.of(context).style,
);

_docLayoutPerComponentBlockStyler = SingleColumnLayoutCustomComponentStyler();
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -19,9 +19,9 @@ import 'package:super_editor/src/super_textfield/infrastructure/text_scrollview.
import 'package:super_editor/src/super_textfield/input_method_engine/_ime_text_editing_controller.dart';
import 'package:super_text_layout/super_text_layout.dart';

import '../../infrastructure/_logging.dart';
import '../metrics.dart';
import '../styles.dart';
import 'package:super_editor/src/infrastructure/_logging.dart';
import 'package:super_editor/src/super_textfield/metrics.dart';
import 'package:super_editor/src/super_textfield/styles.dart';

export '_caret.dart';

Expand All @@ -36,6 +36,7 @@ class SuperAndroidTextField extends StatefulWidget {
this.textAlign,
this.textStyleBuilder = defaultTextFieldStyleBuilder,
this.inlineWidgetBuilders = const [],
this.inheritDefaultTextStyle = false,
this.hintBehavior = HintBehavior.displayHintUntilFocus,
this.hintBuilder,
this.minLines,
Expand Down Expand Up @@ -77,6 +78,9 @@ class SuperAndroidTextField extends StatefulWidget {
/// {@macro super_text_field_inline_widget_builders}
final InlineWidgetBuilderChain inlineWidgetBuilders;

/// {@macro super_text_field_inherit_default_text_style}
final bool inheritDefaultTextStyle;

/// Policy for when the hint should be displayed.
final HintBehavior hintBehavior;

Expand Down Expand Up @@ -629,9 +633,20 @@ class SuperAndroidTextFieldState extends State<SuperAndroidTextField>
}

Widget _buildSelectableText() {
final defaultTextStyle = widget.inheritDefaultTextStyle ? DefaultTextStyle.of(context).style : null;
final textSpan = _textEditingController.text.isNotEmpty
? _textEditingController.text.computeInlineSpan(context, widget.textStyleBuilder, widget.inlineWidgetBuilders)
: TextSpan(text: "", style: widget.textStyleBuilder({}));
? _textEditingController.text.computeInlineSpan(
context,
widget.textStyleBuilder,
widget.inlineWidgetBuilders,
defaultTextStyle: defaultTextStyle,
)
: TextSpan(
text: "",
style: defaultTextStyle != null
? defaultTextStyle.merge(widget.textStyleBuilder({}))
: widget.textStyleBuilder({}),
);

return Directionality(
textDirection: _textDirection,
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -52,6 +52,7 @@ class SuperDesktopTextField extends StatefulWidget {
this.textController,
this.textStyleBuilder = defaultTextFieldStyleBuilder,
this.inlineWidgetBuilders = const [],
this.inheritDefaultTextStyle = false,
this.textAlign = TextAlign.left,
this.hintBehavior = HintBehavior.displayHintUntilFocus,
this.hintBuilder,
Expand Down Expand Up @@ -96,6 +97,9 @@ class SuperDesktopTextField extends StatefulWidget {
/// {@macro super_text_field_inline_widget_builders}
final InlineWidgetBuilderChain inlineWidgetBuilders;

/// {@macro super_text_field_inherit_default_text_style}
final bool inheritDefaultTextStyle;

/// Policy for when the hint should be displayed.
final HintBehavior hintBehavior;

Expand Down Expand Up @@ -529,7 +533,12 @@ class SuperDesktopTextFieldState extends State<SuperDesktopTextField> implements
textDirection: _textDirection,
child: SuperText(
key: _textKey,
richText: _controller.text.computeInlineSpan(context, widget.textStyleBuilder, widget.inlineWidgetBuilders),
richText: _controller.text.computeInlineSpan(
context,
widget.textStyleBuilder,
widget.inlineWidgetBuilders,
defaultTextStyle: widget.inheritDefaultTextStyle ? DefaultTextStyle.of(context).style : null,
),
textAlign: _textAlign,
textDirection: _textDirection,
textScaler: _textScaler,
Expand Down
21 changes: 15 additions & 6 deletions super_editor/lib/src/super_textfield/ios/ios_textfield.dart
Original file line number Diff line number Diff line change
Expand Up @@ -20,11 +20,11 @@ import 'package:super_editor/src/super_textfield/input_method_engine/_ime_text_e
import 'package:super_editor/src/super_textfield/ios/editing_controls.dart';
import 'package:super_text_layout/super_text_layout.dart';

import '../metrics.dart';
import '../styles.dart';
import 'floating_cursor.dart';
import '../../infrastructure/platforms/ios/ios_system_context_menu.dart';
import 'user_interaction.dart';
import 'package:super_editor/src/super_textfield/metrics.dart';
import 'package:super_editor/src/super_textfield/styles.dart';
import 'package:super_editor/src/super_textfield/ios/floating_cursor.dart';
import 'package:super_editor/src/infrastructure/platforms/ios/ios_system_context_menu.dart';
import 'package:super_editor/src/super_textfield/ios/user_interaction.dart';

export '../infrastructure/magnifier.dart';
export 'caret.dart';
Expand All @@ -42,6 +42,7 @@ class SuperIOSTextField extends StatefulWidget {
this.tapHandlers = const [],
this.textController,
this.textStyleBuilder = defaultTextFieldStyleBuilder,
this.inheritDefaultTextStyle = false,
this.inlineWidgetBuilders = const [],
this.textAlign = TextAlign.left,
this.padding,
Expand Down Expand Up @@ -87,6 +88,9 @@ class SuperIOSTextField extends StatefulWidget {
/// {@macro super_text_field_inline_widget_builders}
final InlineWidgetBuilderChain inlineWidgetBuilders;

/// {@macro super_text_field_inherit_default_text_style}
final bool inheritDefaultTextStyle;

/// Padding placed around the text content of this text field, but within the
/// scrollable viewport.
final EdgeInsets? padding;
Expand Down Expand Up @@ -629,7 +633,12 @@ class SuperIOSTextFieldState extends State<SuperIOSTextField>

Widget _buildSelectableText() {
final textSpan = _textEditingController.text //
.computeInlineSpan(context, widget.textStyleBuilder, widget.inlineWidgetBuilders);
.computeInlineSpan(
context,
widget.textStyleBuilder,
widget.inlineWidgetBuilders,
defaultTextStyle: widget.inheritDefaultTextStyle ? DefaultTextStyle.of(context).style : null,
);

CaretStyle caretStyle = widget.caretStyle;

Expand Down
14 changes: 14 additions & 0 deletions super_editor/lib/src/super_textfield/super_textfield.dart
Original file line number Diff line number Diff line change
Expand Up @@ -62,6 +62,7 @@ class SuperTextField extends StatefulWidget {
this.textController,
this.textAlign,
this.textStyleBuilder = defaultTextFieldStyleBuilder,
this.inheritDefaultTextStyle = false,
this.inlineWidgetBuilders = const [],
this.hintBehavior = HintBehavior.displayHintUntilFocus,
this.hintBuilder,
Expand Down Expand Up @@ -116,6 +117,16 @@ class SuperTextField extends StatefulWidget {
/// {@endtemplate}
final InlineWidgetBuilderChain inlineWidgetBuilders;

/// {@template super_text_field_inherit_default_text_style}
/// Whether this text field should inherit the enclosing [DefaultTextStyle].
///
/// If `true`, the text styles in [textStyleBuilder] will be merged with the
/// enclosing [DefaultTextStyle] in the widget tree.
///
/// Defaults to `false`.
/// {@endtemplate}
final bool inheritDefaultTextStyle;

/// Policy for when the hint should be displayed.
final HintBehavior hintBehavior;

Expand Down Expand Up @@ -374,6 +385,7 @@ class SuperTextFieldState extends State<SuperTextField> implements ImeInputOwner
textAlign: widget.textAlign,
textStyleBuilder: widget.textStyleBuilder,
inlineWidgetBuilders: widget.inlineWidgetBuilders,
inheritDefaultTextStyle: widget.inheritDefaultTextStyle,
hintBehavior: widget.hintBehavior,
hintBuilder: widget.hintBuilder,
selectionHighlightStyle: SelectionHighlightStyle(
Expand Down Expand Up @@ -409,6 +421,7 @@ class SuperTextFieldState extends State<SuperTextField> implements ImeInputOwner
textAlign: widget.textAlign,
textStyleBuilder: widget.textStyleBuilder,
inlineWidgetBuilders: widget.inlineWidgetBuilders,
inheritDefaultTextStyle: widget.inheritDefaultTextStyle,
hintBehavior: widget.hintBehavior,
hintBuilder: widget.hintBuilder,
caretStyle: widget.caretStyle ??
Expand Down Expand Up @@ -439,6 +452,7 @@ class SuperTextFieldState extends State<SuperTextField> implements ImeInputOwner
textAlign: widget.textAlign,
textStyleBuilder: widget.textStyleBuilder,
inlineWidgetBuilders: widget.inlineWidgetBuilders,
inheritDefaultTextStyle: widget.inheritDefaultTextStyle,
padding: widget.padding,
hintBehavior: widget.hintBehavior,
hintBuilder: widget.hintBuilder,
Expand Down
Loading
Loading