ArtifactTestGame/Sources/TestGame/TestGame.swift
2026-08-03 15:11:53 -04:00

286 lines
11 KiB
Swift
Raw Permalink Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

import ArtifactPlatform
import ArtifactGraphics
import ArtifactUI
import ArtifactState
import ArtifactMath
import ArtifactFilesystem
import ArtifactColor
import Foundation
import ArgumentParser
@main
struct TestGame: AsyncParsableCommand {
@Option(name: [.short, .long], defaultAsFlag: "")
var config: String?
struct AppSettings: Codable {
var name: String
}
@MainActor
public mutating func run() async {
let atlas = STBTextProvider()
@State var thing = 0
@Settings
var settings: AppSettings = AppSettings(name: "test")
let scrollView2 = ScrollView {
VStack {
for i in 1...20 {
Text("Sub-item \(i)")
}
}
}
.height(120)
.backgroundColor(.gold)
.style(BorderWidthStyle.self, 1)
.style(BorderColorStyle.self, .blue)
let scrollView: ScrollView = ScrollView {
VStack {
for i in 1...20 {
Text("Item \(i)")
}
scrollView2
for i in 21...50 {
Text("Item \(i)")
}
Text("""
If the ScrollView has an absolute height, preferredSize correctly returns that height.
If the ScrollView has height: .auto (the default), preferredSize returns the sum of its childrens preferred sizes (or zero if it has no children yet / they havent been laid out).
The fatal interaction appears when the ScrollView is given an absolute size after it has already been inserted, or when the Stacks first measurement pass sees .auto / .grow and therefore writes a zero (or very small) height into child.frame.size.y. Subsequent layout of the ScrollView itself then sees a zero-height content area and never recovers.
""")
}
}
.height(.grow(1))
let styles = StyleSheet {
StyleRule(.type(Stack.self) / (.type(Text.self) | .type(Stack.self))) {
BackgroundColorStyle.property(Color(21, 22, 24))
BorderColorStyle.property(Color(64, 64, 64))
TextColorStyle.property(Color(100, 100, 100))
}
}
let ctx = UIContext(atlas, styles) {
HStack {
VStack {
Text("Test 1")
Text("Test 3")
HStack {
Spacer(width: 50%, height: .absolute(50))
.style(BorderRadiusStyle.self, 4)
.style(BackgroundColorStyle.self, .red)
.onClick { ev in
print("Click")
scrollView.style[BackgroundColorStyle.self] = .rgb(.random(in: 0...255), .random(in: 0...255), .random(in: 0...255))
}
.onPointerDown { ev in
print("Pointer down")
}
.onPointerUp { ev in
print("Pointer up")
}
// .onPointerEnter { ev in
// scrollView.style[BackgroundColorStyle.self] = .blue
// }
// .onPointerLeave { ev in
// scrollView.style[BackgroundColorStyle.self] = .red
// }
}
.alignment(.center)
DerivedElement {
Text("Thing: \(thing)")
}
}
.groups("foo")
.width(.grow(1))
.padding(50, 25)
.style(BorderRadiusStyle.self, 100)
.style(BorderWidthStyle.self, 6)
VStack {
scrollView
Text("Test 2")
}
.alignment(.trailing)
.width(.grow(1))
.padding(15)
}
.width(100%)
.height(100%)
}
print(scrollView2.preferredSize())
let window = SDL3Window(width: 1024, height: 720)
let graphics = WebGPUGraphics(window)
let uniforms = graphics.createBuffer(label: "Uniform buffer", usage: [.uniform, .copyDest], size: 2048)
let texture = graphics.createTexture(label: "Texture", usage: [.texture, .copyDest], size: [1024, 1024])
let sampler = graphics.createSampler(label: "Sampler")
var rgba = [UInt8](repeating: 0, count: 1024*1024*4)
for i in 0..<1024*1024 {
let gray = atlas.pixels[i]
let idx = i * 4
rgba[idx+0] = 255 // R
rgba[idx+1] = 255 // G
rgba[idx+2] = 255 // B
rgba[idx+3] = gray // A
}
texture.writeData(rgba)
var projection = Mat4.ortho(left: 0, right: Float(window.width), bottom: Float(window.height), top: 0, near: -1, far: 1)
let verts = graphics.createBuffer(label: "UI vertex buffer", usage: [.vertex, .copyDest], size: 1024 * 1024)
let inds = graphics.createBuffer(label: "UI index buffer", usage: [.index, .copyDest], size: 1024 * 1024)
var drawable = Drawable(passes: ["ui"], vertices: verts, vertexCount: 0, indices: inds, indexCount: 0, uniforms: projection.bytes)
ctx.setViewportSize(to: Vec2(1024, 720))
let list = ctx.getDrawList()
verts.writeData(list.vertices)
drawable.vertexCount = list.vertexCount
inds.writeData(list.indices.withUnsafeBytes { Array($0) })
drawable.indexCount = list.indices.count
drawable.operations = list.operations.map {
switch $0 {
case .draw(let count):
.draw(count: count)
case .pushScissor(let rect):
.pushScissor(rect: rect)
case .popScissor:
.popScissor
case .pushTransform(let transformation):
.pushTransform(transformation)
case .popTransform:
.popTransform
}
}
ctx.dump()
window.onDraw {
ctx.update(deltaTime: $0)
if ctx.needsDisplay {
let list = ctx.getDrawList()
verts.writeData(list.vertices)
drawable.vertexCount = list.vertexCount
inds.writeData(list.indices.withUnsafeBytes { Array($0) })
drawable.indexCount = list.indices.count
drawable.operations = list.operations.map {
switch $0 {
case .draw(let count):
.draw(count: count)
case .pushScissor(let rect):
.pushScissor(rect: rect)
case .popScissor:
.popScissor
case .pushTransform(let transformation):
.pushTransform(transformation)
case .popTransform:
.popTransform
}
}
}
}
window.onResize { x, y in
ctx.setViewportSize(to: Vec2d(Double(x), Double(y)))
projection = Mat4.ortho(left: 0, right: Float(window.width), bottom: Float(window.height), top: 0, near: -1, far: 1)
drawable.uniforms = projection.bytes
}
window.actions.onKeyDown(.escape) {
window.close()
}
window.actions.onKeyDown(.enter) {
scrollView.animate(duration: 1000, easing: .easeInOut) {
HeightStyle.property(50%)
// BackgroundColorStyle.property(.red)
} to: {
HeightStyle.property(100%)
// BackgroundColorStyle.property(.blue)
}
}
window.actions.onPointerDown { pointer in
ctx.handlePointerDown(pointer)
}
window.actions.onPointerMove { pointer in
ctx.handlePointerMove(pointer)
}
window.actions.onPointerUp { pointer in
ctx.handlePointerUp(pointer)
}
window.actions.onWheel { pointer, delta in
ctx.handleWheel(pointer, delta: delta)
// thing += 1
// print(thing)
// //ctx.input.scroll(amount: Vec2d(delta) * 2)
// scrollView.scrollPos += Vec2d(delta) * 4
}
graphics.registerPass(
RenderPass(
name: "ui",
label: "UI Pass",
bindings: [
.buffer(stage: .vertex, type: .uniform, buffer: uniforms),
.texture(stage: .fragment, sampleType: .float, texture: texture),
.sampler(stage: .fragment, type: .filtering, sampler: sampler)
],
vertexLayout: [.init(attributes: [.vec2f, .vec2f, .vec4n])],
vertexShader: Shader(entryPoint: "vs_main", source: """
struct Uniforms {
ortho: mat4x4<f32>,
}
@group(0) @binding(0) var<uniform> uniforms: Uniforms;
struct VertexOutput {
@builtin(position) pos: vec4<f32>,
@location(0) uv: vec2<f32>,
@location(1) color: vec4<f32>,
}
@vertex
fn vs_main(@location(0) pos: vec2<f32>, @location(1) uv: vec2<f32>, @location(2) color: vec4<f32>) -> VertexOutput {
var out: VertexOutput;
out.pos = uniforms.ortho * vec4<f32>(pos, 0.0, 1.0);
out.uv = uv;
out.color = color;
return out;
}
"""),
colorTargets: [ColorTarget(channels: .all, colorBlendOperation: .add, colorBlendSrcFactor: .srcAlpha, colorBlendDestFactor: .oneMinusSrcAlpha, alphaBlendOperation: .add, alphaBlendSrcFactor: .one, alphaBlendDestFactor: .oneMinusSrcAlpha)],
fragmentShader: Shader(entryPoint: "fs_main", source: """
@group(0) @binding(1) var texture: texture_2d<f32>;
@group(0) @binding(2) var sampler_: sampler;
@fragment
fn fs_main(@location(0) uv: vec2<f32>, @location(1) color: vec4<f32>) -> @location(0) vec4<f32> {
return textureSample(texture, sampler_, uv) * color;
}
"""),
clearColor: .init(0, 0, 128, .max)
)
)
graphics.scene.append(drawable)
window.run()
}
}