fix(mobile): render filled circles in shared native icon layer

Co-authored-by: George <9fe4f0b04e88b915789b35532b6288ed75d3b7f012a3cc74b5a265d27083c833@sprout-oss.stage.blox.sqprod.co>
Signed-off-by: Tom Brow <tomb@block.xyz>
This commit is contained in:
Tom Brow
2026-10-21 15:48:24 -07:00
parent 20ba95ed89
commit d0ab42c491
3 changed files with 40 additions and 1 deletions

View File

@@ -8,6 +8,7 @@
/* Begin PBXBuildFile section */
F84380010000000000000001 /* ProfileSetupRecoveryTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = F84380010000000000000002 /* ProfileSetupRecoveryTests.swift */; };
DAAB00000000000000000031 /* NativeTablerTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = DAAB00000000000000000032 /* NativeTablerTests.swift */; };
BBAA00000000000000000011 /* IdentityFilePlugin.swift in Sources */ = {isa = PBXBuildFile; fileRef = BBAA00000000000000000012 /* IdentityFilePlugin.swift */; };
BBAA00000000000000000001 /* HostedHpkeBackup.swift in Sources */ = {isa = PBXBuildFile; fileRef = BBAA00000000000000000002 /* HostedHpkeBackup.swift */; };
DAAB00000000000000000001 /* NativeTabler.swift in Sources */ = {isa = PBXBuildFile; fileRef = DAAB00000000000000000002; };
@@ -102,6 +103,7 @@
/* Begin PBXFileReference section */
F84380010000000000000002 /* ProfileSetupRecoveryTests.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ProfileSetupRecoveryTests.swift; sourceTree = "<group>"; };
DAAB00000000000000000032 /* NativeTablerTests.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = NativeTablerTests.swift; sourceTree = "<group>"; };
BBAA00000000000000000012 /* IdentityFilePlugin.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = IdentityFilePlugin.swift; sourceTree = "<group>"; };
BBAA00000000000000000002 /* HostedHpkeBackup.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = HostedHpkeBackup.swift; sourceTree = "<group>"; };
DAAB00000000000000000002 /* NativeTabler.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = NativeTabler.swift; sourceTree = "<group>"; };
@@ -209,6 +211,7 @@
children = (
331C807B294A618700263BE5 /* RunnerTests.swift */,
F84380010000000000000002 /* ProfileSetupRecoveryTests.swift */,
DAAB00000000000000000032 /* NativeTablerTests.swift */,
AB1200132F40400100A17E01 /* NativeMessageImageLoaderTests.swift */,
AB1200132F40400100A17E09 /* IdentityFilePluginTests.swift */,
BZZ0000000000000000002C /* BuzzCommunicationNotificationTests.swift */,
@@ -619,6 +622,7 @@
files = (
331C808B294A63AB00263BE5 /* RunnerTests.swift in Sources */,
F84380010000000000000001 /* ProfileSetupRecoveryTests.swift in Sources */,
DAAB00000000000000000031 /* NativeTablerTests.swift in Sources */,
AB1200032F40400100A17E01 /* NativeMessageImageLoaderTests.swift in Sources */,
AB1200032F40400100A17E09 /* IdentityFilePluginTests.swift in Sources */,
BZZ0000000000000000002B /* BuzzCommunicationNotificationTests.swift in Sources */,

View File

@@ -16,6 +16,15 @@ enum NativeTabler {
}()
static func image(_ symbol: String, size: CGFloat = 20) -> UIImage? {
// The bundled Tabler font is outline-only. Match filled/circle.svg's
// 10-unit radius on its 24-unit canvas for every filled-circle consumer.
if symbol == "circle.fill" {
return UIGraphicsImageRenderer(size: CGSize(width: size, height: size)).image { _ in
UIColor.black.setFill()
UIBezierPath(ovalIn: CGRect(x: 0, y: 0, width: size, height: size)
.insetBy(dx: size / 12, dy: size / 12)).fill()
}.withRenderingMode(.alwaysTemplate)
}
guard let code = glyphs[symbol], let scalar = UnicodeScalar(code),
let name = fontName, let font = UIFont(name: name, size: size) else {
assertionFailure("Missing Tabler icon: \(symbol)")
@@ -50,7 +59,6 @@ enum NativeTabler {
"crop": 0xea85, // crop
"arrow.triangle.2.circlepath.camera": 0xee22, // camera-rotate
"circle": 0xea6b, // circle
"circle.fill": 0xea6b, // circle
"sun.max": 0xeb30, // sun
"moon": 0xeaf8, // moon
"ellipsis": 0xea95, // dots

View File

@@ -0,0 +1,27 @@
import UIKit
import XCTest
@testable import Buzz
final class NativeTablerTests: XCTestCase {
@MainActor
func testFilledCircleHasOpaqueCenterAndTransparentCornersAtEachSize() throws {
for size: CGFloat in [16, 20, 32] {
let image = try XCTUnwrap(NativeTabler.image("circle.fill", size: size))
XCTAssertEqual(image.size, CGSize(width: size, height: size))
XCTAssertEqual(image.renderingMode, .alwaysTemplate)
let bitmap = try XCTUnwrap(image.cgImage)
var pixels = [UInt8](repeating: 0, count: bitmap.width * bitmap.height * 4)
try pixels.withUnsafeMutableBytes { bytes in
let context = try XCTUnwrap(CGContext(data: bytes.baseAddress,
width: bitmap.width, height: bitmap.height, bitsPerComponent: 8,
bytesPerRow: bitmap.width * 4, space: CGColorSpaceCreateDeviceRGB(),
bitmapInfo: CGImageAlphaInfo.premultipliedLast.rawValue))
context.draw(bitmap, in: CGRect(x: 0, y: 0, width: bitmap.width, height: bitmap.height))
}
let center = ((bitmap.height / 2) * bitmap.width + bitmap.width / 2) * 4 + 3
XCTAssertEqual(pixels[center], 255, "Filled circle must have an opaque center")
XCTAssertEqual(pixels[3], 0, "The icon canvas must retain transparent corners")
}
}
}