w3c / csswg-drafts

CSS Working Group Editor Drafts
https://drafts.csswg.org/
Other
4.5k stars 661 forks source link

[CSS-UI] caret-shape: block/underscore and overflow #10289

Open schenney-chromium opened 6 months ago

schenney-chromium commented 6 months ago

The spec for caret-shape sensibly says that a caret at the end of a text run should paint to the right (in ltr, left in rtl) where the next character in a run would appear. But what should happen if there is no space to put the caret in the container at that location?

Options include: 1) Clip the caret. 2) Start a new line and put the caret on that line. 3) Paint the caret as close as possible to the edge, even if it overlaps the last character in the run. 4) Treat the caret as overflow and apply the overflow rules for the container.

Terminal programs on Mac and Linux, the things I can easily check, both exhibit behavior (2). There is no existing browser implementation other than maybe Opera (according to canIuse).

Igalia is considering a chromium implementation and this is a question that immediately comes to mind as a blocker.

schenney-chromium commented 3 months ago

As a reference point, chrome currently clips the caret when it hits the end of a run in a fixed width content editable div with horizontal text, then puts the next entered character on the next line. Or maybe it is painting it under the focus ring.

Firefox paints the caret on top of the focus ring.

But neither start a new line if the caret doesn't fit.

<!DOCTYPE html>
<html>
<head>
<style>
    div {
        width: 200px;
    }
</style>
</head>
<body>
    <div contenteditable="true">
        This is a long horizontal string
    </div>
</body>

But if you remove the focus ring and set the padding to zero, the behavior changes in both browsers. As far as I can tell by messing with widths etc, both Chrome and Firefox consider the cursor part of the word and start a new line by breaking the line and starting a new one with the last word and the caret.

<!DOCTYPE html>
<html>
<head>
<style>
    div {
        width: 200px;
        padding: 0px;
        border: 1px solid black;
        outline: none;
    }
</style>
</head>
<body>
    <div contenteditable="true">
        This is a long horizontal string
    </div>
</body>
</html>

In the example above, it is possible to set the width so that the caret is rendered at the end of the line very close to the border. Then, reducing the width by one (the width of the caret) creates a new line with the last word and the caret. I can't find any code in chromium that would cause this, so it may be that effectively the cursor is currently shown at "width - 1" of the shaped text string. Such behavior would result in clipping of a block or underline cursor.

css-meeting-bot commented 3 months ago

The CSS Working Group just discussed [CSS-UI] caret-shape: block/underscore and overflow, and agreed to the following:

The full IRC log of that discussion <fantasai> The reason we introduced Discontinued Draft was to have an IPR-safe place to park the draft.
<fantasai> FWIW
<TabAtkins> schenney: This is the only signif issue blocking Igalia implementing the feature
<TabAtkins> schenney: If you have a fixed-size container, and you're editing, and you have a block or underscore caret-shape
<TabAtkins> schenney: What happens when it gets to the end of the line and there's no space to draw the caret?
<TabAtkins> schenney: Spec isn't clear, I did some investigation but it's not clear what's happening.
<kizu> q+
<TabAtkins> schenney: My argument is for not drawing, or drawing what you can
<astearns> ack kizu
<TabAtkins> (+1 for drawing clipped; making a new line is wrong)
<TabAtkins> kizu: Firefox draws regardless and possibly clips
<TabAtkins> kizu: You can see that with overflow:clip on a parent
<TabAtkins> kizu: I think I like this better, if it's on the edge of a container and it's clipped by overflow, the user can't see the caret.
<TabAtkins> kizu: Easier if it still shows. Still *possible* to be invisible, but not common.
<TabAtkins> kizu: I think in general if we can draw the caret, perhaps on the border of the contnet, we should. Also agree that wrapping isn't a good idea.
<TabAtkins> astearns: So not really option 4 in the issue, but a new option? Treat teh caret as overflow regardless of overflow on the container?
<TabAtkins> kizu: No, more like focus-ring in Firefox. It's drawn over everything (but inside of browser chrome).
<TabAtkins> astearns: With chair hat off, I'm also in favor of not clipping the cursor. If you clip it it might not look like a cursor even if partially drawn.
<TabAtkins> schenney: I'm coming around to thinking that is the best option. Anything creating a new line is def a bad idea.
<TabAtkins> emilio: Clarifying firefox - it doesn't skip clipping arbitrarily, it's just drawn by the containing block of the element. So it'll skip *some* clipping but not all.
<TabAtkins> emilio: I implemented this to be compatible with some Chromium quirks.
<TabAtkins> emilio: I can dig up some history, but yeah, it's not just "dont' clip the caret"
<TabAtkins> schenney: So one option is we can try implementing with not clipping, and see what happens. I suspect it's problematic, because clip is applied to the container.
<TabAtkins> schenney: But that does seem like the best way to resolve this in the short term.
<TabAtkins> emilio: I suspect it interacts badly with scrolling and such if you scroll off the editable element entirely.
<flackr> +1
<TabAtkins> emilio: That's why I landed on that compromise in FF
<PaulG> (APA interest here) I agree not clipping (or limited clipping) sounds better.
<TabAtkins> [emilio's kid interjects with an important point]
<TabAtkins> emilio: I'd be suspicious with "not clip at all" even being compatible. Don't think it's most desirable either.
<TabAtkins> kizu: Just tested in codepen, FF is interesting.
<TabAtkins> kizu: Still seeing it painting outisde of a contain:paint
<TabAtkins> kizu: [another case]
<TabAtkins> astearns: Perhaps have a reoslution to attempt to show the caret in full, in the case this issue talks about, but leave open exactly what the clipping behavior is until Steven has time to work things out?
<kizu> I tested in CodePen: https://codepen.io/kizu/pen/oNrbYPP — Firefox' behavior is interested, where when there is overflow: auto, the cursor is visible on the edge, as if it was “sticky”
<TabAtkins> schenney: Yes, sounds reasonable to resolve on that and leave the issue open for details.
<TabAtkins> astearns: Proposed: when the caret is past the end of a line, attempt to show the caret even if it overflow, with any necessary clipping behavior we end up needing to be specified later.
<TabAtkins> astearns: objections?
<TabAtkins> RESOLVED: when the caret is past the end of a line, attempt to show the caret even if it overflow, with any necessary clipping behavior we end up needing to be specified later.