Geometry
The four value types layout and drawing share: a position, a size, a rectangle
and an inset. All coordinates are i32 logical screen pixels in the current
orientation, the same space the host draws in, with the origin at the top-left
corner and y growing downwards.
Never assume a panel size or shape. The panels this runs on are portrait and
landscape, from 296x128 to 800x480; ask
Renderer::screen_size, or better, lay out
from Theme::content_area.
Writing a widget uses all four.
Topics
Section titled “Topics”Point | A position on screen. |
Size | A width and height, clamped at zero by every constructor. |
Rect | A positioned, sized region. |
Insets | Space inset equally or individually on each edge. |
Edges and clamping
Section titled “Edges and clamping”A rect's right and bottom edges are outside it. A rect at x with width
w covers the columns x to x + w - 1; Rect::right is
x + w, the first column outside. contains and
intersects agree on this, so two rects that merely touch
do not overlap, and rects laid edge to edge tile without a shared pixel.
A size never goes below zero. Size::new,
Size::shrink and everything built on them clamp each dimension
at zero, so a layout that over-subtracts gives an empty rectangle rather than
an inverted one. A position is not clamped, and neither is an inset.
All four types are Copy, Default (all zeros), Debug and Eq.
A position on screen.
pub struct Point| Field or constant | Abstract |
|---|---|
Point::x | Pixels from the left edge. |
Point::y | Pixels from the top edge. |
Point::ORIGIN | The top-left corner of the screen. |
A point may be negative or beyond the panel: a view scrolled up past the top
of its viewport renders at a negative y.
Example — moving a point
use xpui::Point;
let origin = Point::new(16, 60);assert_eq!(origin.offset(4, -8), Point::new(20, 52));assert_eq!(Point::default(), Point::ORIGIN);Creating a point
Section titled “Creating a point”Point::new
Section titled “Point::new”A point at x, y.
pub const fn new(x: i32, y: i32) -> SelfMoving a point
Section titled “Moving a point”Point::offset
Section titled “Point::offset”This point moved by dx/dy.
pub const fn offset(self, dx: i32, dy: i32) -> SelfEither may be negative. A widget uses it to place a child inside its own
padding: origin.offset(pad, pad).
See also: Rect, Rect::contains
A width and height, clamped at zero by every constructor.
pub struct Size| Field or constant | Abstract |
|---|---|
Size::width | Pixels across. |
Size::height | Pixels down. |
Size::ZERO | Nothing at all. |
A size is what a view's measure receives and what its size answers. It has
no position: a Rect pairs one with an origin.
Example — clamping at zero
use xpui::Size;
assert_eq!(Size::new(-10, 20), Size::new(0, 20));assert_eq!(Size::new(0, 0), Size::ZERO);assert!(Size::new(-10, 20).is_empty());Creating a size
Section titled “Creating a size”Size::new
Section titled “Size::new”A size of width by height, each clamped at zero.
pub fn new(width: i32, height: i32) -> SelfShrinking a size
Section titled “Shrinking a size”Size::shrink
Section titled “Size::shrink”This size shrunk by dw/dh, clamped at zero.
pub fn shrink(self, dw: i32, dh: i32) -> SelfIt saturates at zero rather than going negative. A negative dw or dh
grows the size instead.
Example — shrinking past zero
use xpui::Size;
let available = Size::new(100, 40);assert_eq!(available.shrink(32, 8), Size::new(68, 32));
// Over-subtracting saturates, one dimension at a time.assert_eq!(available.shrink(120, 0), Size::new(0, 40));
// A negative amount grows it.assert_eq!(available.shrink(-10, -10), Size::new(110, 50));Testing a size
Section titled “Testing a size”Size::is_empty
Section titled “Size::is_empty”Whether either dimension is zero, so nothing could be drawn in it.
pub fn is_empty(self) -> boolExample — a row with no height is empty
use xpui::Size;
assert!(Size::new(200, 0).is_empty());assert!(Size::ZERO.is_empty());assert!(!Size::new(1, 1).is_empty());A positioned, sized region.
pub struct Rect| Field | Abstract |
|---|---|
Rect::origin | The top-left corner. |
Rect::size | The width and height. |
The origin may be anywhere, negative included; the size is clamped at zero by
Rect::new and Rect::inset. Its right and bottom
edges are outside it: see Edges and clamping. A view
builds one from what render receives, Rect { origin, size: self.size }.
Example — edges
use xpui::{Point, Rect, Size};
let card = Rect::new(10, 20, 200, 80);
assert_eq!((card.x(), card.y()), (10, 20));assert_eq!((card.width(), card.height()), (200, 80));assert_eq!((card.right(), card.bottom()), (210, 100));assert_eq!(card.origin, Point::new(10, 20));assert_eq!(card.size, Size::new(200, 80));
// A negative size is clamped; a negative origin is not.assert_eq!(Rect::new(-5, 0, -1, 10).size, Size::new(0, 10));assert_eq!(Rect::new(-5, 0, -1, 10).x(), -5);Example — hit-testing a tap
use xpui::{Point, Rect};
let button = Rect::new(0, 0, 200, 80);
assert!(button.contains(Point::new(0, 0))); // the top-left corner is insideassert!(button.contains(Point::new(199, 79))); // the last pixel is insideassert!(!button.contains(Point::new(200, 10))); // the right edge is outsideassert!(!button.contains(Point::new(10, 80))); // the bottom edge is outsideCreating a rect
Section titled “Creating a rect”Rect::new
Section titled “Rect::new”A rect with its top-left corner at x, y.
pub fn new(x: i32, y: i32, width: i32, height: i32) -> Selfwidth and height are clamped at zero, as by Size::new.
Reading its edges
Section titled “Reading its edges”Rect::x
Section titled “Rect::x”The left edge.
pub const fn x(&self) -> i32Rect::y
Section titled “Rect::y”The top edge.
pub const fn y(&self) -> i32Rect::width
Section titled “Rect::width”Pixels across.
pub const fn width(&self) -> i32Rect::height
Section titled “Rect::height”Pixels down.
pub const fn height(&self) -> i32Rect::right
Section titled “Rect::right”The first column outside the rect.
pub const fn right(&self) -> i32x() + width(). The last column inside is one less.
Rect::bottom
Section titled “Rect::bottom”The first row outside the rect.
pub const fn bottom(&self) -> i32y() + height(). The next row of a stack starts here, with no gap and no
overlap.
Example — stacking rows edge to edge
use xpui::Rect;
let first = Rect::new(16, 60, 448, 40);let second = Rect::new(16, first.bottom(), 448, 40);
assert_eq!(second.y(), 100);assert!(!first.intersects(second));Insetting
Section titled “Insetting”Rect::inset
Section titled “Rect::inset”This rect pulled inward on every edge by insets.
pub fn inset(&self, insets: Insets) -> SelfThe origin moves by left and top, and the size shrinks by
horizontal and vertical, clamped at
zero. Negative insets push the rect outward.
Example — padding, too much padding, and an outset
use xpui::{Insets, Rect, Size};
let card = Rect::new(0, 0, 200, 80);
assert_eq!(card.inset(Insets::all(8)), Rect::new(8, 8, 184, 64));
// More inset than there is room: the size stops at zero.let crushed = card.inset(Insets::symmetric(120, 0));assert_eq!(crushed.size, Size::new(0, 80));assert!(crushed.size.is_empty());
// Negative insets grow it, as a touch target larger than its glyph does.assert_eq!(card.inset(Insets::all(-4)), Rect::new(-4, -4, 208, 88));Hit-testing
Section titled “Hit-testing”Rect::contains
Section titled “Rect::contains”Whether point is inside; the right and bottom edges are outside.
pub fn contains(&self, point: Point) -> boolAn empty rect contains nothing.
Rect::intersects
Section titled “Rect::intersects”Whether the two overlap at all.
pub fn intersects(&self, other: Rect) -> boolTouching edges do not count, matching contains, which treats
the right and bottom edges as outside.
Example — overlap
use xpui::{Point, Rect};
let a = Rect::new(0, 0, 100, 100);
assert!(a.intersects(Rect::new(99, 99, 10, 10))); // one pixel sharedassert!(!a.intersects(Rect::new(100, 0, 10, 10))); // touching the right edgeassert!(!a.intersects(Rect::new(0, 100, 10, 10))); // touching the bottom edgeassert!(a.intersects(Rect::new(-10, -10, 200, 200))); // around itassert!(a.intersects(Rect::new(50, 50, 0, 0))); // empty, but insideassert!(!Rect::new(50, 50, 0, 0).contains(Point::new(50, 50)));See also: Point, Size, Insets, Renderer::clip
Insets
Section titled “Insets”Space inset equally or individually on each edge.
pub struct Insets| Field or constant | Abstract |
|---|---|
Insets::top | Pixels taken from the top edge. |
Insets::right | Pixels taken from the right edge. |
Insets::bottom | Pixels taken from the bottom edge. |
Insets::left | Pixels taken from the left edge. |
Insets::ZERO | No inset on any edge. |
Insets are not clamped: a negative one grows a rect under
Rect::inset. Padding takes one.
Example — building insets
use xpui::Insets;
let even = Insets::all(8);assert_eq!((even.top, even.right, even.bottom, even.left), (8, 8, 8, 8));
let wide = Insets::symmetric(16, 4);assert_eq!((wide.top, wide.right, wide.bottom, wide.left), (4, 16, 4, 16));
let header = Insets { top: 40, ..Insets::ZERO };assert_eq!(header.vertical(), 40);assert_eq!(Insets::default(), Insets::ZERO);Creating insets
Section titled “Creating insets”Insets::all
Section titled “Insets::all”The same inset on all four edges.
pub const fn all(value: i32) -> SelfInsets::symmetric
Section titled “Insets::symmetric”Independent horizontal and vertical insets.
pub const fn symmetric(horizontal: i32, vertical: i32) -> Self| Parameter | Meaning |
|---|---|
horizontal | Taken from the left and from the right, each. |
vertical | Taken from the top and from the bottom, each. |
Reading totals
Section titled “Reading totals”Insets::horizontal
Section titled “Insets::horizontal”Left and right together: the width an inset rect loses.
pub const fn horizontal(&self) -> i32Insets::vertical
Section titled “Insets::vertical”Top and bottom together: the height an inset rect loses.
pub const fn vertical(&self) -> i32Example — the size inside the insets
use xpui::{Insets, Size};
let insets = Insets::symmetric(16, 4);assert_eq!((insets.horizontal(), insets.vertical()), (32, 8));
let inner = Size::new(480, 40).shrink(insets.horizontal(), insets.vertical());assert_eq!(inner, Size::new(448, 32));See also: Rect::inset, Padding