KRect and KRectF proposal

Vlad Zahorodnii vlad.zahorodnii at kde.org
Wed Apr 8 15:29:11 BST 2026


Hi,

On 4/8/26 2:30 AM, Thiago Macieira wrote:
> On Tuesday, 7 April 2026 12:56:19 Pacific Daylight Time Vlad Zahorodnii wrote:
>>> Speaking of negative, what happens if you have negative height and width?
>>> Which edge is exclusive?
>> Such rectangles will be considered invalid. So `::contains()` will
>> return false. You'll need to use either `::normalized()` or `::span()`
>> to get a proper rectangle. That's another (intentional) change that I
>> forgot to mention in the original message.
> Ok, so it means it's not a good replacement for QRect because it removes a
> functionality, in addition to fixing the known issue.

Negative width and height are a corner case. I'm sure you can encounter 
them, e.g. in some drawing applications when the user presses and drags 
the pointer to draw a rectangle, but I don't think they are so common 
that the core abstraction needs to handle that. For example, in KWin, we 
have no valid case where rectangles with negative sizes are okay, and it 
is a project that uses rectangles heavily.

The problem with such rects is that they make things more ambiguous and 
error prone, for example isEmpty() will return true but contains() can 
still report that a given point is inside the rectangle; or what should 
top(), left(), and so on return? They contribute complexity too, which 
one can argue can be avoided, for example contains(QPoint) can be 
implemented with a single line that has four comparisons but with 
negative sizes, it'll take more. Excluding rects with negative sizes was 
a conscious decision to keep everything clean and simple but I don't 
think it warrants that the proposed rects are bad (I know it's not the 
word you used but the opposite of good is bad).

The fact that right() and bottom() return "x() + width()" and "y() + 
height()", respectively, already means that you cannot port to the new 
rects using sed. The focus is rather that you can combine the new rects 
with QRect + QRectF and switch existing types (after close code 
inspection) to KRect + KRectF with minimal changes so you can transition 
gradually.

>>>> - `RectF::toRect()` rounds the coordinates of top, left, right, and
>>>> bottom edges;
>>> Away from each other, towards each other, or towards a particular
>>> direction? In other words, how do the height() and width() get rounded?
>> The final coordinates will be as follows:
>>
>> - left = std::round(rect.x())
>> - right = std::round(rect.x() + rect.width())
>> - top = std::round(rect.y())
>> - bottom = std::round(rect.y() + rect.height())
>>
>> or
>>
>> - x = std::round(rect.x())
>> - y = std::round(rect.y())
>> - width = std::round(rect.x() + rect.width()) - std::round(rect.x())
>> - height = std::round(rect.y() + rect.height()) - std::round(rect.y())
> So you may end up with a zero-sized integer rectangle when the original FP one
> isn't. Consider rounding fixing the rounding directions and in opposite of each
> other.

Yes, it's possible that a rectangle can become empty. What rounding 
strategy you use depends on the use case. For example, if it's a regular 
geometry, use plain round()s. If it's a repaint region, use something 
equivalent to toAlignedRect().

In some cases, you may need to account for the device pixels when 
computing logical coordinates to ensure that everything rounds nicely. 
For example, that's what we do with server side decoration outlines to 
ensure that they don't magically disappear.

>> The main reasoning behind this is HiDPI or fractional scaling to be
>> specific. We need a predictable rounding algorithm, which doesn't tend
>> to introduce gaps, and that's universal for both apps and compositors.
> Agreed, which is why it shouldn't be rounding to nearest.
>
>> Although note that the rectangles are stored as (left, top, right,
>> bottom) tuples rather than (x, y, width, height) tuples, so it's just
>> four std::round() calls. The reasoning behind it is that the LTRB format
>> is nicer for contains(), intersects(), Region, etc.
> Consider:
> top() = -0.4999
> left() = -0.4999
> bottom() = 0.4999
> right() = 0.4999
>
> width() and height() are 0.9998, which clearly rounds to 1.0. But all of the
> four coordinates round to 0.

Correct, whether it's fine depends on the context though.

Vlad

>>>> I would like to propose adding a new library, e.g. kprimitivetypes, that
>>>> would host `KRect` and `KRectF`. There is a slight challenge with KF
>>>> though. It will be nice if KConfig and maybe other tier 1 libraries are
>>>> able use `KRect(F)`. In order to achieve that, the new library will need
>>>> to live either outside of KF or perhaps there could be tier 0 in KF?
>>> Bring this discussion to the Qt dev mailing list so we can discuss having
>>> it in QtCore.
>> Okay, will do.
> You'll need to offer an convincing reason why negative sizes aren't allowed.


More information about the kde-devel mailing list