286 lines
11 KiB
Swift
286 lines
11 KiB
Swift
|
||
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 children’s preferred sizes (or zero if it has no children yet / they haven’t been laid out).
|
||
The fatal interaction appears when the ScrollView is given an absolute size after it has already been inserted, or when the Stack’s 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()
|
||
}
|
||
}
|