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

Block cursor improvements #82

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

Conversation

timdown
Copy link
Contributor

@timdown timdown commented Feb 22, 2023

Why

The block cursor currently has a couple of issues.

Firstly, the cursor is taller on an empty line than on non-empty lines. To illustrate this, all of the following screenshots use line-height: 2.5.

Non-empty line:
small_cursor

Empty line:
large_cursor

Secondly, an unfocused cursor overlays a copy of the character such that both are visible, making the character look stronger:
strong_m

What changed

For the first issue, I've added a fullHeightCoordsAtPos function, which acts like coordsAtPos except that the top and bottom coordinates represent the top and bottom of the whole visual line, not just the text. It also works with wrapped lines. However, in order to work with wrapped lines, it relies on the assumption that every visual line within the wrapped line has the same height, which is not necessarily the case if the line contains widgets or decorations that affect the height of the line.
new_cursor

For the second issue, I've made the colour of the content of an unfocused block cursor transparent:
normal_m

Test plan

  1. Add a line height of 2.5 to the editor in dev/index.ts: EditorView.baseTheme({ '.cm-line': { lineHeight: '2.5' } })
  2. View the dev page in a browser. Place the cursor on an empty line and a non-empty line to observe the difference.
  3. Place the cursor over a letter. Click outside the editor. Look closely at the letter inside the cursor.

@timdown
Copy link
Contributor Author

timdown commented Feb 22, 2023

I've simplified the implementation of fullHeightCoordsAtPos. However, I'm unsure about whether the whole thing is appropriate for everyone. We want it for our editor, where we've applied similar workarounds to get the selection, cursor and highlighter decorations to cover the full height of the line. However, it might look a bit odd in general. Example:

image

The inconsistency between the heights of the block cursor on empty and non-empty lines is still something I think should be fixed; I could try to find another way if you prefer.

@nightwing
Copy link
Collaborator

On which version of codemirror do you see cursor height changing on empty line, i don't see it with @codemirror/view@6.9.0.

I am not completely sure, but I think changing the way character height is calculated only for block cursor is not a good approach. It adds more problems like not having good solution for widgets, your screenshot, the text cursor being one pixel higher than what it should be in some cases

image

If one wants both cursor and selection to be of defaultLineHeight, wouldn't it be simpler to override coordsAtPos method, like this?

if (!_view.__proto__.coordsAtPos0) _view.__proto__.coordsAtPos0 = _view.__proto__.coordsAtPos
_view.__proto__.coordsAtPos = function(pos, side) {

  const coords = this.coordsAtPos0(pos, side);
  if (!coords) {
    return null;
  }

  const halfLeading =
      (this.defaultLineHeight - (coords.bottom - coords.top)) / 2;

  return {
    left: coords.left,
    right: coords.right,
    top: coords.top - halfLeading,
    bottom: coords.bottom + halfLeading,
  };
}

The change for transparent color looks good.

@timdown
Copy link
Contributor Author

timdown commented Mar 3, 2023

On which version of codemirror do you see cursor height changing on empty line, i don't see it with @codemirror/view@6.9.0.

I'm seeing it on @codemirror/view@6.9.1. It happens to the regular CM6 cursor too (try moving the cursor between empty and non-empty lines), so you could argue this is an issue for CM6 itself.

I am not completely sure, but I think changing the way character height is calculated only for block cursor is not a good approach. It adds more problems like not having good solution for widgets, your screenshot, the text cursor being one pixel higher than what it should be in some cases.

Yeah, I think I agree. I'm not sure what the answer is though. I'll give it some more thought.

If one wants both cursor and selection to be of defaultLineHeight, wouldn't it be simpler to override coordsAtPos method, like this?

if (!_view.__proto__.coordsAtPos0) _view.__proto__.coordsAtPos0 = _view.__proto__.coordsAtPos
_view.__proto__.coordsAtPos = function(pos, side) {

  const coords = this.coordsAtPos0(pos, side);
  if (!coords) {
    return null;
  }

  const halfLeading =
      (this.defaultLineHeight - (coords.bottom - coords.top)) / 2;

  return {
    left: coords.left,
    right: coords.right,
    top: coords.top - halfLeading,
    bottom: coords.bottom + halfLeading,
  };
}

coordsAtPos is used in lots of places, so I suspect it could have undesirable consequences. Also, that seems somewhat... brutal.

The change for transparent color looks good.

Great. I've made a new PR with just that part: #89

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.

None yet

2 participants