Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

[SuperEditor] Make selected text color strategy consider each colored span (Resolves #2093) #2122

Open
wants to merge 3 commits into
base: main
Choose a base branch
from

Conversation

angelosilvestre
Copy link
Collaborator

[SuperEditor] Make selected text color strategy consider each colored span. Resolves #2093

SingleColumnLayoutComponentViewModel invokes the SelectedTextColorStrategy using only the default text color for the node as the originalColor argument, ignoring any ColorAttributions. If we return the same original color, we loose the color attribution:

SCR-20240622-oxai

SCR-20240622-oxck

This PR changes SingleColumnLayoutComponentViewModel to consider each colored span and call SelectedTextColorStrategy for each of then:

SCR-20240622-oxlx

@@ -88,6 88,79 @@ void main() {
expect(richText.getSpanForPosition(const TextPosition(offset: 5))!.style!.color, Colors.green);
expect(richText.getSpanForPosition(const TextPosition(offset: 16))!.style!.color, Colors.green);
});

testWidgetsOnArbitraryDesktop("overrides each colored span", (tester) async {
Copy link
Contributor

Choose a reason for hiding this comment

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

When I read the name of this test, I don't know what "overrides each colored span" means. I don't know the UX impetus for this test, nor what the UI result is expected to be.

Moreover, when I look down at the bottom of the test to see what the expectations are looking for, the comment above them simply says that all the colors have changed. Whatever this test is verifying, it's currently too difficult to figure out what that is.

Copy link
Collaborator Author

Choose a reason for hiding this comment

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

Would "allows overriding each color within the selected text" be more clear?

Copy link
Contributor

Choose a reason for hiding this comment

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

how about "can choose new selected text color based on the original text color"?


final componentTextColor = viewModel.textStyleBuilder({}).color;
if (componentTextColor != null) {
// Compute the selected text color for the default color of the node. If there is any
Copy link
Contributor

Choose a reason for hiding this comment

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

To help avoid ambiguity with the concept of "selection color", any place where you're currently saying "selected text color", you should probably say "color of the selected text".

Also, I think this comment is overall difficult to follow. I think the following is what you're doing:

// Copy the existing text, switching the color of the text from the regular text color to the
// color of selected text. For example, text that's normally black, might become white when
// selected with a dark selection box color.
//
// Any text with an explicit color attribution will remain unaffected, e.g., a red word will
// remain red.

Copy link
Collaborator Author

Choose a reason for hiding this comment

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

// Any text with an explicit color attribution will remain unaffected, e.g., a red word will
// remain red.

This isn't the case. For each span with a different color, we call _selectedTextColorStrategy with that color as the originalTextColor. If the method returns a different color, the returned color will override the existing color.

// text color attributions in the selected range, they will override this color.
textWithSelectionAttributions = viewModel.text.copyText(0)
..addAttribution(
ColorAttribution(_selectedTextColorStrategy!(
Copy link
Contributor

Choose a reason for hiding this comment

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

I'm not sure the overall assumptions of this change are correct. We're assuming that any text with a color applied to it should retain that same color when selected.

That might be what some people want, but it's probably not true in general, for the same reason that we're letting people change the color of selected text in the first place. Colors that are high contrast when unselected might be low contrast when selected. This applies to spans of text with color attributions, too.

Shouldn't the selected text color strategy get an opportunity to switch any given color, as desired?

Copy link
Contributor

Choose a reason for hiding this comment

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

Indeed, as a user, I would like to know the original color (after the attributions were applied), but I want the color returned by selectedTextColorStrategy to be displayed without further modifications. I just need to know about the original color so that I can calculate the correct color to be displayed.

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'm not sure the overall assumptions of this change are correct. We're assuming that any text with a color applied to it should retain that same color when selected.

As explained in this comment, the selected color strategy has to opportunity to switch any given color. The difference now is that, if the selected range has multiple different colored spans, the selected color strategy can return a new color for each existing color.

Copy link
Contributor

Choose a reason for hiding this comment

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

@angelosilvestre can you add a test for that so I can see it in action?

Copy link
Collaborator Author

Choose a reason for hiding this comment

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

@@ -88,6 88,79 @@ void main() {
expect(richText.getSpanForPosition(const TextPosition(offset: 5))!.style!.color, Colors.green);
expect(richText.getSpanForPosition(const TextPosition(offset: 16))!.style!.color, Colors.green);
});

testWidgetsOnArbitraryDesktop("overrides each colored span", (tester) async {
Copy link
Contributor

Choose a reason for hiding this comment

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

how about "can choose new selected text color based on the original text color"?


testWidgetsOnArbitraryDesktop("overrides each colored span", (tester) async {
final stylesheet = defaultStylesheet.copyWith(
selectedTextColorStrategy: ({required Color originalTextColor, required Color selectionHighlightColor}) {
Copy link
Contributor

Choose a reason for hiding this comment

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

If we consider that editors will be used in a light mode and a dark mode, it seems likely that users would benefit from a strategy that works well in dark mode, to go with our existing strategy for light mode. A dark mode strategy should probably use a heuristic that inverts the luminance of each existing text color. Can you see if you can put together a reasonable dark mode strategy that automatically selects new colors and then we could test that actual strategy here?

Copy link
Collaborator Author

@angelosilvestre angelosilvestre Jul 10, 2024

Choose a reason for hiding this comment

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

I tried a naive solution inverting the lightness of a HLS color with the following code, but it doesn't look great:

Color darkModeSelectedColorStrategy({
  required Color originalTextColor,
  required Color selectionHighlightColor,
}) {
  final hslColor = HSLColor.fromColor(originalTextColor);
  return hslColor.withLightness(1.0 - hslColor.lightness).toColor();    
}

Not selected:

SCR-20240709-tcza

With a strategy that returns the original color:

SCR-20240709-tdil

Color with an inverse lightness.

SCR-20240709-tdpn

What do you think? Do you have other ideas ? Maybe we should take the selectionHighlightColor also in consideration.

Copy link
Contributor

Choose a reason for hiding this comment

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

Maybe we should take the selectionHighlightColor also in consideration.

Yeah I think we probably should. The primary consideration is to ensure that the selected text contrasts with the selection. Do you know of any other app references to look at for this, where selection colors are different from typical browser selections?

Copy link
Contributor

Choose a reason for hiding this comment

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

Word (at least on Mac) seems to change the text color slightly if the selection color is the exact same as the text color.

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 tried some apps:

Notion doesn't seem to change the font color when the text is selected:

image

image

Apple notes changes all text to white:

image

image

Google docs don't change the font color:

image

image

Word online also doesn't seem to change the font color:

image

image

@matthew-carroll
Copy link
Contributor

@angelosilvestre do you need anything from me to continue this PR?

@angelosilvestre
Copy link
Collaborator Author

@matthew-carroll Updated the example app to use the same behavior as Apple Notes to color the text in dark mode.

Also, update a test name.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

Successfully merging this pull request may close these issues.

[BUG] - selectedTextColorStrategy's originalTextColor ignores ColorAttributions
3 participants