Added convenience overloads to Rect and also, Collection conformance in the Playdate bindings target.

This commit is contained in:
2026-07-26 00:31:58 +02:00
parent 41558399e3
commit 0d0b397ce1
5 changed files with 92 additions and 0 deletions
@@ -56,3 +56,17 @@ extension Graphics {
public var count: Int { info.count }
}
}
extension Graphics.BitmapTable: RandomAccessCollection {
public var startIndex: Int { 0 }
public var endIndex: Int { count }
/// The bitmap at `position`. The bitmap references storage owned by the
/// table; keep the table alive while using it.
public subscript(position: Int) -> Graphics.Bitmap {
guard let bitmap = bitmap(at: position) else {
preconditionFailure("bitmap table index out of range")
}
return bitmap
}
}
@@ -46,11 +46,21 @@ extension Graphics {
gfx.pointee.setClipRect.unsafelyUnwrapped(Int32(x), Int32(y), Int32(width), Int32(height))
}
/// Sets the clip rect in world coordinates (affected by the draw offset).
public static func setClipRect(_ rect: Rect) {
setClipRect(x: rect.left, y: rect.top, width: rect.width, height: rect.height)
}
/// Sets the clip rect in screen coordinates (unaffected by the draw offset).
public static func setScreenClipRect(x: Int, y: Int, width: Int, height: Int) {
gfx.pointee.setScreenClipRect.unsafelyUnwrapped(Int32(x), Int32(y), Int32(width), Int32(height))
}
/// Sets the clip rect in screen coordinates (unaffected by the draw offset).
public static func setScreenClipRect(_ rect: Rect) {
setScreenClipRect(x: rect.left, y: rect.top, width: rect.width, height: rect.height)
}
/// Clears the current clip rect.
public static func clearClipRect() {
gfx.pointee.clearClipRect.unsafelyUnwrapped()
@@ -103,6 +113,11 @@ extension Graphics {
}
}
/// Draws the outline of a rectangle, stroked inside its frame.
public static func drawRect(_ rect: Rect, color: Color) {
drawRect(x: rect.left, y: rect.top, width: rect.width, height: rect.height, color: color)
}
/// Fills the rectangle with `color`.
public static func fillRect(x: Int, y: Int, width: Int, height: Int, color: Color) {
color.withLCDColor {
@@ -110,6 +125,11 @@ extension Graphics {
}
}
/// Fills the rectangle with `color`.
public static func fillRect(_ rect: Rect, color: Color) {
fillRect(x: rect.left, y: rect.top, width: rect.width, height: rect.height, color: color)
}
/// Draws the outline of a rectangle with rounded corners, stroked with
/// `lineWidth`.
public static func drawRoundRect(x: Int, y: Int, width: Int, height: Int, radius: Int,
@@ -120,6 +140,13 @@ extension Graphics {
}
}
/// Draws the outline of a rectangle with rounded corners, stroked with
/// `lineWidth`.
public static func drawRoundRect(_ rect: Rect, radius: Int, lineWidth: Int, color: Color) {
drawRoundRect(x: rect.left, y: rect.top, width: rect.width, height: rect.height,
radius: radius, lineWidth: lineWidth, color: color)
}
/// Fills a rectangle with rounded corners.
public static func fillRoundRect(x: Int, y: Int, width: Int, height: Int, radius: Int, color: Color) {
color.withLCDColor {
@@ -128,6 +155,12 @@ extension Graphics {
}
}
/// Fills a rectangle with rounded corners.
public static func fillRoundRect(_ rect: Rect, radius: Int, color: Color) {
fillRoundRect(x: rect.left, y: rect.top, width: rect.width, height: rect.height,
radius: radius, color: color)
}
/// Draws an ellipse stroked inside the rect. If the angles differ, draws
/// an arc from `startAngle` to `endAngle` (clockwise degrees, 0 at top).
public static func drawEllipse(x: Int, y: Int, width: Int, height: Int, lineWidth: Int,
@@ -148,6 +181,22 @@ extension Graphics {
}
}
/// Draws an ellipse stroked inside the rect. If the angles differ, draws
/// an arc from `startAngle` to `endAngle` (clockwise degrees, 0 at top).
public static func drawEllipse(in rect: Rect, lineWidth: Int,
startAngle: Float = 0, endAngle: Float = 0, color: Color) {
drawEllipse(x: rect.left, y: rect.top, width: rect.width, height: rect.height,
lineWidth: lineWidth, startAngle: startAngle, endAngle: endAngle, color: color)
}
/// Fills an ellipse inside the rect. If the angles differ, fills the
/// wedge from `startAngle` to `endAngle` (clockwise degrees, 0 at top).
public static func fillEllipse(in rect: Rect,
startAngle: Float = 0, endAngle: Float = 0, color: Color) {
fillEllipse(x: rect.left, y: rect.top, width: rect.width, height: rect.height,
startAngle: startAngle, endAngle: endAngle, color: color)
}
/// Fills the polygon described by the points, connecting the last point
/// back to the first.
public static func fillPolygon(points: [(x: Int, y: Int)], color: Color,
@@ -205,6 +254,13 @@ extension Graphics {
}
}
/// Draws `text` wrapped and aligned inside the given rectangle.
public static func drawText(_ text: String, in rect: Rect,
wrap: TextWrappingMode = .word, align: TextAlignment = .left) {
drawText(text, x: rect.left, y: rect.top, width: rect.width, height: rect.height,
wrap: wrap, align: align)
}
/// Sets the font used by subsequent text drawing.
public static func setFont(_ font: Font) {
gfx.pointee.setFont.unsafelyUnwrapped(font.pointer)
@@ -33,6 +33,12 @@ extension Graphics {
top: Int32(top), bottom: Int32(bottom))
}
/// The rect's width.
public var width: Int { right - left }
/// The rect's height.
public var height: Int { bottom - top }
/// Returns the rect offset by (dx, dy).
public func translated(dx: Int, dy: Int) -> Rect {
Rect(left: left + dx, right: right + dx, top: top + dy, bottom: bottom + dy)
+6
View File
@@ -186,6 +186,12 @@ enum Mock {
Mock.record("getTableBitmap(\(index))")
return index == 0 ? Mock.fakePointer() : nil
}
gfxAPI.pointee.getBitmapTableInfo = { _, count, cellsWide in
Mock.record("getBitmapTableInfo")
// One bitmap, matching getTableBitmap vending index 0 only.
count?.pointee = 1
cellsWide?.pointee = 1
}
}
// MARK: - Sprite
+10
View File
@@ -89,6 +89,16 @@ struct WrapperTests {
#expect(Mock.eventCount("freeBitmapTable") == 1)
}
@Test func bitmapTableIsACollection() {
let table = Graphics.BitmapTable(count: 4, width: 8, height: 8)
#expect(table.count == 1) // the mock reports a single bitmap
#expect(table.first != nil)
var visited = 0
for _ in table { visited += 1 }
#expect(visited == 1)
}
// MARK: Sprite
@Test func spriteUserdataRecoversWrapperInCallbacks() {