Previously, clicking in the middle of a multi-code point grapheme would
place the cursor at a code unit index somewhere in the middle of the
grapheme. This was not only visually misleading, but the user could then
start typing and insert characters in the middle of the cluster. This
also made text select pretty wonky.
The main issue was that we were treating the glyph index in a glyph run
as a code unit index. We must instead map that glyph index back to a
code unit index with help from LibGfx (via harfbuzz).
The distance computation used here was also a bit off, especially for
the last glyph in a glyph run. We essentially want the cursor to end
up on whichever edge of the clicked glyph it is closest to. The result
of the distance computation limited us to the left edge of the last
glyph. Instead, we can use the same edge tracking we use for form-
associated elements to handle this for us.
Fixes rendering of elements with large border-radius values by scaling
radii proportionally when they exceed element dimensions per CSS spec.
Co-authored-by: Samyat Gautam <thesamyatgautam@gmail.com>
This reverts commit 7dc8062283.
This did not propagate correctly to paintables whose style was inherited
from an ancestor, causing rendering artifacts on https://linear.app/
PaintableBox::handle_mouseleave is turning off scrollbar updating, but
the user might still have the primary button down to scroll. Don't turn
it off if grabbing the thumb to scroll.
Resolves crashing on MacOSX AppKit and Qt where gutter_size is 0 when
mouse is moved outside window.
When rounding a CSSPixelRect to a DevicePixelRect, we simply pulled its
width and height through round() and called it a day. Unfortunately this
could negatively affect the rect's perceived positioning.
A rect at { 0.5, 0.0 } with size { 19.5 x 20.0 } should have its right
edge at position 20, but after rounding it would end up at { 1, 0 } with
size { 20 x 20 }, causing its right edge to be at position 21 instead.
Fix this by first rounding the right and bottom edges of the input rect,
and then determining the dimensions by subtracting its rounded position.
Fixes#245.
We can use BorderRadiiData::as_corners() to avoid converting the corners
one by one. Instead of passing all four corners one by one, use a
reference to CornerRadii.
No functional changes.
Previously we would paint the cursor the entire height of the text
fragment - this didn't look great with large line-heights. Now we only
paint it the height of the actual text, with the top of the cursor
aligning with the font "ascent" and the bottom the "descent".
Call paint_border() recursively, once for the outer line, and once for
the inner one. This is done in a lambda so that we can reuse it for a
couple of other line styles.
Border-radius behaviour doesn't match other browsers, and goes a bit
haywire in some cases. I've left some FIXMEs for someone who
understands the maths here better than I do. 😅
The LineStyle handling is moved to the start of the function, to avoid
unnecessary work.
These will be used for the mask-repeat property as well in an upcoming
commit, hence the more generic names. Also, this more closely matches
the names used in the spec.
Using a generic context argument will allow us to resolve colors in
places where we have all the required information but not in the form of
a layout node as was expected previously.
`aa_translation` is something we inherited from times when
AntiAliasingPainter was a thing. This change replaces it by applying
offset directly to path.
There's no need to have separate display list item for drawing triangle
wave when we could simply use StrokePathUsingColor. By switching to
StrokePathUsingColor we could also reduce painting because it supports
filtering out by bounding box.
Output display list dumps with an indentation level to show balanced
commands. It makes it much easier to see what is happening between e.g.
PushStackingContext and PopStackingContext, or SaveLayer and Restore.
PaintContext dates back to a time when display lists didn't exist and it
truly represented "paint context". Renaming it to better align with its
current role.
Before committing a new layout (and thus building a new paint tree)
we now go through both the old paint tree and the layout tree and detach
them from each other.
This is a little extra work, but it ensures that there are no lingering
references across the trees, which we were apparently accumulating in
some cases on Discord, causing GC leaks.
The faux position we created here is adjusted by the device pixel ratio
later on, which would invoke integer overflow on screens with a DPR
greater than 1.
Instead of creating special data for a mouse move event, let's just add
an explicit leave event handler.
Having a setter for `device_pixels_per_css_pixel` was confusing because
display lists are immutable, so it doesn't make sense to override this
value after the display list has been created.
6507d23 introduced a bug when snapshot for iframe is saved in
`PaintNestedDisplayList` and, since display lists are immutable, it's
not possible to update before the next repaint.
This change fixes the issue by moving `ScrollStateSnapshot` for
nested display lists from `PaintNestedDisplayList` to
`HashMap<NonnullRefPtr<DisplayList>, ScrollStateSnapshot>` that is
placed into pending rendering task, making it possible to update
snapshots for all display lists before the next repaint.
This change doesn't have a test because it's really hard to make a ref
test that will specifically check scenario when scroll offset of an
iframe is advanced after display list is cached. We already have
`Tests/LibWeb/Ref/input/scroll-iframe.html` but unfortunately it did
not catch this bug.
Fixes https://github.com/LadybirdBrowser/ladybird/issues/5486
This migrates TextNode::text_for_rendering() to Utf16String and deals
with the fallout. As a consequence, this effectively reverts commit
3df83dade8.
The layout test expecation file updates are because we now express text
lengths and offsets in UTF-16 code units.
The selection-over-multiple-code-units expectation file update actually
represents a correctness fix. Our result now matches Firefox.
Fixes race condition introduced in eed47acb when rendering thread
accesses ScrollFrame that could be mutated in the middle of
rasterization by the main thread, resulting in broken rendering.
Fixes https://github.com/LadybirdBrowser/ladybird/issues/5553
Skia allows you to pass a bounding rect to its saveLayer() function as
an optimization when you know that you won't paint outside those bounds.
Unfortunately, we were passing a too-small rectangle that didn't take
into account transformed descendants, etc.
For now, simply pass null instead of a bounding rect. This way, Skia
figures it out internally. It may allocate larger temporary bitmaps than
needed this way, but at least we get more correct results. I've left
re-enabling the optimization as a FIXME in the code.
This fixes unwanted clipping in various parts of the Discord UI.