-
-
Notifications
You must be signed in to change notification settings - Fork 354
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
feat(Rect): add offset
method
#533
Conversation
Codecov ReportAttention:
Additional details and impacted files@@ Coverage Diff @@
## main #533 +/- ##
=====================================
Coverage 90.7% 90.7%
=====================================
Files 41 42 +1
Lines 12130 12170 +40
=====================================
+ Hits 11003 11042 +39
- Misses 1127 1128 +1 ☔ View full report in Codecov by Sentry. |
Can you explain why "An i32 would not be enough"? isn't (0u16 as i32 + i32::MAX) > (u16::MAX as i32) (and likewise the subtraction. Regarding wrap vs saturating - consider what On thinking a bit more about this and seeing the implementation, I wonder if my problem is just that |
Oops my bad, it is. For some reason I red the line for
That's my problem ^^, I don't feel like one is more relevant / intuitive / useful than the other. You may want to saturate to not go out of the terminal ; you may want to loop around the terminal for some sort of banner.
|
898769c contains a Proof-of-Concept for the The fields order in Also, it can we weird to some, positive numbers move a border to the right (negative to the left). So something like that Offset {
top: 1,
right: 0,
bottom: 1,
left: 0 would actually move the
self.resize((y.into(), x.into())) but I feel like this is confusing and somewhat less understandable. |
The rect struct doesn't know the size of the terminal. Right now this wraps using integer wrap to u16:MAX, not the terminal size. Can you write a test that shows what a Rect at 0,0 wraps to? Put another way, does I suspect the right approach for this is to always saturate, though I'm not sure if we should make this explicit in the name or not. |
Perhaps we could consider naming this A small heads up - I just slapped together a refactor in #534 that extracts Rect out into layout/rect.rs to make it easier to just look at Rect related stuff instead of all the complexity of the layout module. |
898769c
to
edc5408
Compare
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I think it's probably more useful to saturate any values rather than wrapping, and consider that we should end up with a Rect that is useful to the app (i.e. if the x value become u16::MAX then the width must become 0)
Would it be the worst thing to consider making these 3 methods:
- move(amount: Move { x, y } ) // just changes the x, y values
- resize(amount: Size { width, height } ) // just changes the width, height values
- offset(amount: Offset { left, top, right, bottom }) // can change all
The rect returned in each case should be valid (x,y <= u16::MAX and x+width,y+height <= u16::MAX)
893dd7a
to
5bd4b55
Compare
inset
and offset
methods5bd4b55
to
3da3d47
Compare
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I added resize
, offset
and relocate
(see this comment on name) methods.
I am unsure whether structs are really needed for relocate
and resize
. I don't see the clear benefits over two simple integers.
PS. I'll add unit tests once the content is validated
Ok, so reading the current iteration, what if instead we end up with: struct Offset { x: i32, y: i32 }
/// moves the rect by the given offset
fn offset(offset: Offset) {} I like using a struct here rather than two values as it avoids the situation where people inadvertently flip the two. Resize could have some added complexity. Do the numbers in the resize refer to the delta or the final value (resize_by / resize_to). How about we just do move in this PR and split the resize to another one? |
This seems like a good first step yes. This also enables us to wait for more feedback on |
515cafe
to
1dec078
Compare
offset
method
1dec078
to
21dd1ad
Compare
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Looking good.
I think we probably still need to define some behavior around what to do when we hit the max though. Say we have a 100x100 width rect, I think it's probably better to not move it beyond u16::MAX - 100
instead in order to keep the height and width the same and ensure the bounds are within u16.
This is unlikely to be ever hit - but in the odd event someone with a display the size of a house does hit it, I'd prefer to render badly than to have their app crash due to trying to address a part of a rect that doesn't exist.
Can you add the behavior and a test for this please?
21dd1ad
to
323bde7
Compare
I've added that quickly, but then I think it is inconsistent with I would have added it to I wanted to hear your opinion on it before implementing. We would also have to perform this check every time a value is modified. But ultimately that would be best imo, keep the Rect inside u16 in any case (area, right, bottom). |
TBH I don't understand the rationale for the ratio code (I haven't dug into the history of it), so I'm not really sure. |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
LGTM
Message for CHANGELOG: feat(Rect): add offset method The offset method creates a new let rect = area.offset(Offset { x: 10, y: -10 }); |
The offset method creates a new Rect that is moved by the amount specified in the x and y direction. These values can be positive or negative. This is useful for manual layout tasks. ```rust let rect = area.offset(Offset { x: 10, y -10 }); ```
323bde7
to
67acc04
Compare
Lets just merge this PR as is then. We can then find out the rationale for the ratio code and streamline the desired behavior on all Rect methods |
Yup - was just checking with the other devs that offset was fine :) |
Thanks again for this PR. |
You're welcome! Glad to be participating in such an amazing project! |
Description
As discussed here.
Add
inset
andoffset
methods to respectively adjust aRect
size and move it.This is common logic in renders and apps.
Implementation choices
I used generics to allow direct calculation in the call, like here. This just avoids having to wrap calculations in parenthesis and calling
into()
.I used two different generics to allow giving different types (e.g.
u16
calculation andi32
literal).A signed int allows to move the
Rect
forward or backward.I used an
i64
i32
as parameter to allow movingx
andy
from their minimal value to their maximal value (0
->u16::MAX
), even though this is something that very rarely occur (if at all).An
i32
i16
would not be enough (reference).Due to the preceding choice, we're casting an
i64
i32
to anu16
. If the calculation could not be converted back tou16
, values will wrap around. This is documented, I think this is legitimate since this case should not happen in the first place, we could provide variants later if needed (saturating_...
,checked_...
).Alternatives are:
Option
(easy to implement, kind of breaks builder pattern, unpleasant to use)Let me know what you think. IMO, this is basically choosing which behavior should be the default as we could provide variant later.