Merge branch 'main' of github.com:TokamakUI/Tokamak into fiber/view-support

This commit is contained in:
Carson Katri 2023-02-04 15:57:55 -05:00
commit 8aca895422
13 changed files with 141 additions and 107 deletions

View File

@ -3,7 +3,7 @@ name: Bug report
about: Create a report to help us improve
title: ''
labels: bug
assignees: MaxDesiatov
assignees: carson-katri
---
@ -28,17 +28,17 @@ A clear and concise description of what you expected to happen.
If this is a layout/rendering issue, please provide screenshots for both Tokamak and SwiftUI that highlight the difference.
**Desktop (please complete the following information):**
- OS: [e.g. macOS]
- OS: [e.g. macOS 12.4]
- Browser [e.g. chrome, safari]
- Version of the browser [e.g. 22]
- Version of Tokamak [e.g. 0.6.1]
- Version of Tokamak [e.g. 0.10.1]
**Smartphone (please complete the following information):**
- Device: [e.g. iPhone6]
- OS: [e.g. iOS8.1]
- Device: [e.g. iPhone 6]
- OS: [e.g. iOS15.1]
- Browser [e.g. stock browser, safari]
- Version of the browser [e.g. 22]
- Version of Tokamak [e.g. 0.6.1]
- Version of Tokamak [e.g. 0.10.1]
**Additional context**
Add any other context about the problem here.

View File

@ -27,8 +27,8 @@ jobs:
matrix:
include:
- { toolchain: wasm-5.6.0-RELEASE }
- { toolchain: wasm-5.7-SNAPSHOT-2022-06-01-a }
- { toolchain: wasm-DEVELOPMENT-SNAPSHOT-2022-06-23-a }
- { toolchain: wasm-5.7-SNAPSHOT-2022-07-27-a }
- { toolchain: wasm-DEVELOPMENT-SNAPSHOT-2022-07-23-a }
steps:
- uses: actions/checkout@v2
@ -76,25 +76,26 @@ jobs:
name: Failed snapshots
path: '*Tests'
gtk_macos_build:
runs-on: macos-12
steps:
- uses: actions/checkout@v2
- name: Build the GTK renderer on macOS
shell: bash
run: |
set -ex
sudo xcode-select --switch /Applications/Xcode_13.4.app/Contents/Developer/
brew install gtk+3
make build
# FIXME: disabled due to build errors, to be investigated
# gtk_macos_build:
# runs-on: macos-12
#
# steps:
# - uses: actions/checkout@v2
# - name: Build the GTK renderer on macOS
# shell: bash
# run: |
# set -ex
# sudo xcode-select --switch /Applications/Xcode_13.4.1.app/Contents/Developer/
#
# brew install gtk+3
#
# make build
gtk_ubuntu_18_04_build:
runs-on: ubuntu-latest
container:
image: swiftlang/swift:nightly-bionic
image: swiftlang/swift:nightly-5.7-bionic
steps:
- uses: actions/checkout@v2
@ -109,7 +110,7 @@ jobs:
gtk_ubuntu_20_04_build:
runs-on: ubuntu-latest
container:
image: swiftlang/swift:nightly-focal
image: swiftlang/swift:nightly-5.7-focal
steps:
- uses: actions/checkout@v2

View File

@ -8,26 +8,26 @@ on:
jobs:
codecov:
container:
image: swiftlang/swift:nightly-focal
image: swiftlang/swift:nightly-5.7-focal
runs-on: ubuntu-latest
steps:
- run: apt-get update && DEBIAN_FRONTEND=noninteractive apt-get install -y gtk+-3.0 libgtk+-3.0
- name: Checkout Branch
uses: actions/checkout@v2
- name: Build Test Target
run: swift build -Xswiftc -profile-coverage-mapping -Xswiftc -profile-generate --product TokamakPackageTests
- name: Run Tests
run: swift test --enable-code-coverage --skip-build
- name: Generate Branch Coverage Report
uses: mattpolzin/swift-codecov-action@0.7.1
id: cov
with:
MINIMUM_COVERAGE: 15
- name: Post Positive Results
if: ${{ success() }}
run: |
echo "::warning file=Package.swift,line=1,col=1::The current code coverage percentage is passing with ${{ steps.cov.outputs.codecov }} (minimum allowed: ${{ steps.cov.outputs.minimum_coverage }}%)."
- name: Post Negative Results
if: ${{ failure() }}
run: |
echo "::error file=Package.swift,line=1,col=1::The current code coverage percentage is failing with ${{ steps.cov.outputs.codecov }} (minimum allowed: ${{ steps.cov.outputs.minimum_coverage }}%)."
- run: apt-get update && DEBIAN_FRONTEND=noninteractive apt-get install -y gtk+-3.0 libgtk+-3.0
- name: Checkout Branch
uses: actions/checkout@v2
- name: Build Test Target
run: swift build -Xswiftc -profile-coverage-mapping -Xswiftc -profile-generate --product TokamakPackageTests
- name: Run Tests
run: swift test --enable-code-coverage --skip-build
- name: Generate Branch Coverage Report
uses: mattpolzin/swift-codecov-action@0.7.1
id: cov
with:
MINIMUM_COVERAGE: 15
- name: Post Positive Results
if: ${{ success() }}
run: |
echo "::warning file=Package.swift,line=1,col=1::The current code coverage percentage is passing with ${{ steps.cov.outputs.codecov }} (minimum allowed: ${{ steps.cov.outputs.minimum_coverage }}%)."
- name: Post Negative Results
if: ${{ failure() }}
run: |
echo "::error file=Package.swift,line=1,col=1::The current code coverage percentage is failing with ${{ steps.cov.outputs.codecov }} (minimum allowed: ${{ steps.cov.outputs.minimum_coverage }}%)."

View File

@ -241,6 +241,9 @@ carton dev
You can also clone this repository and run `carton dev --product TokamakDemo` in its root
directory. This will build the demo app that shows almost all of the currently implemented APIs.
If you have any questions, pleaes check out the [FAQ](docs/FAQ.md) document, and/or join the
#tokamak channel on [the SwiftWasm Discord server](https://discord.gg/ashJW8T8yp).
## Security
By default, the DOM renderer will escape HTML control characters in `Text` views. If you wish
@ -322,9 +325,10 @@ appreciated and helps in maintaining the project.
## Maintainers
In alphabetical order: [Carson Katri](https://github.com/carson-katri),
[David Hunt](https://github.com/foscomputerservices), [Ezra Berch](https://github.com/ezraberch),
[Jed Fox](https://jedfox.com), [Max Desiatov](https://desiatov.com),
[Morten Bek Ditlevsen](https://github.com/mortenbekditlevsen/), [Yuta Saito](https://github.com/kateinoigakukun/).
[Ezra Berch](https://github.com/ezraberch),
[Jed Fox](https://jedfox.com),
[Morten Bek Ditlevsen](https://github.com/mortenbekditlevsen/),
[Yuta Saito](https://github.com/kateinoigakukun/).
## Acknowledgments

View File

@ -13,7 +13,7 @@
// limitations under the License.
public struct Transaction {
/// The overriden transaction for a state change in a `withTransaction` block.
/// The overridden transaction for a state change in a `withTransaction` block.
/// Is always set back to `nil` when the block exits.
static var _active: Self?

View File

@ -21,7 +21,7 @@ import Foundation
public protocol _AnimationSolver {
/// Solve value at a specific point in time.
func solve(at t: Double) -> Double
/// Calculates the duration of the animation to a specific presision.
/// Calculates the duration of the animation to a specific precision.
func restingPoint(precision y: Double) -> Double
}

View File

@ -261,30 +261,8 @@ extension VStack: StackLayout {
public var _alignment: Alignment { .init(horizontal: alignment, vertical: .center) }
}
public extension VStack where Content == EmptyView {
init(
alignment: HorizontalAlignment = .center,
spacing: CGFloat? = nil
) {
self.alignment = alignment
self.spacing = spacing
content = EmptyView()
}
}
@_spi(TokamakCore)
extension HStack: StackLayout {
public static var orientation: Axis { .horizontal }
public var _alignment: Alignment { .init(horizontal: .center, vertical: alignment) }
}
public extension HStack where Content == EmptyView {
init(
alignment: VerticalAlignment = .center,
spacing: CGFloat? = nil
) {
self.alignment = alignment
self.spacing = spacing
content = EmptyView()
}
}

View File

@ -17,6 +17,10 @@
import Foundation
#if canImport(CoreGraphics)
import CoreGraphics
#endif
public struct StrokeStyle: Equatable {
public var lineWidth: CGFloat
public var lineCap: CGLineCap

View File

@ -103,7 +103,7 @@ public struct CGAffineTransform: Equatable {
/// Returns an affine transformation matrix constructed from a rotation value you provide.
/// - Parameters:
/// - angle: The angle, in radians, by which this matrix rotates the coordinate system axes.
/// A positive value specifies clockwise rotation and anegative value specifies
/// A positive value specifies clockwise rotation and a negative value specifies
/// counterclockwise rotation.
public init(rotationAngle angle: CGFloat) {
self.init(a: cos(angle), b: sin(angle), c: -sin(angle), d: cos(angle), tx: 0, ty: 0)

View File

@ -39,46 +39,48 @@ public struct List<SelectionValue, Content>: View
self.content = content()
}
var listStack: some View {
VStack(alignment: .leading, spacing: 0) { () -> AnyView in
if let contentContainer = content as? ParentView {
var sections = [AnyView]()
var currentSection = [AnyView]()
for child in contentContainer.children {
if child.view is SectionView {
if currentSection.count > 0 {
sections.append(AnyView(Section {
ForEach(Array(currentSection.enumerated()), id: \.offset) { _, view in view }
}))
currentSection = []
}
sections.append(child)
func stackContent() -> AnyView {
if let contentContainer = content as? ParentView {
var sections = [AnyView]()
var currentSection = [AnyView]()
for child in contentContainer.children {
if child.view is SectionView {
if currentSection.count > 0 {
sections.append(AnyView(Section {
ForEach(Array(currentSection.enumerated()), id: \.offset) { _, view in view }
}))
currentSection = []
}
sections.append(child)
} else {
if child.children.count > 0 {
currentSection.append(contentsOf: child.children)
} else {
if child.children.count > 0 {
currentSection.append(contentsOf: child.children)
} else {
currentSection.append(child)
}
currentSection.append(child)
}
}
if currentSection.count > 0 {
sections.append(AnyView(Section {
ForEach(Array(currentSection.enumerated()), id: \.offset) { _, view in view }
}))
}
return AnyView(_ListRow.buildItems(sections) { view, isLast in
if let section = view.view as? SectionView {
section.listRow(style)
} else {
_ListRow.listRow(view, style, isLast: isLast)
}
})
} else {
return AnyView(content)
}
if currentSection.count > 0 {
sections.append(AnyView(Section {
ForEach(Array(currentSection.enumerated()), id: \.offset) { _, view in view }
}))
}
return AnyView(_ListRow.buildItems(sections) { view, isLast in
if let section = view.view as? SectionView {
section.listRow(style)
} else {
_ListRow.listRow(view, style, isLast: isLast)
}
})
} else {
return AnyView(content)
}
}
var listStack: some View {
VStack(alignment: .leading, spacing: 0, content: stackContent)
}
@_spi(TokamakCore)
public var body: some View {
if let style = style as? ListStyleDeferredToRenderer {

View File

@ -16,6 +16,7 @@
//
import Foundation
import JavaScriptEventLoop
import JavaScriptKit
import OpenCombineJS
import OpenCombineShim
@ -97,6 +98,10 @@ public struct DOMFiberRenderer: FiberRenderer {
}
public init(_ rootSelector: String, useDynamicLayout: Bool = true) {
if #available(macOS 10.15, *) {
JavaScriptEventLoop.installGlobalExecutor()
}
guard let reference = document.querySelector!(rootSelector).object else {
fatalError("""
The root element with selector '\(rootSelector)' could not be found. \

View File

@ -162,7 +162,7 @@ extension InsetGroupedListStyle: ListStyleDeferredToRenderer {
}
}
// TODO: Make sections collabsible (see Section.swift for more impl. details)
// TODO: Make sections collapsible (see Section.swift for more impl. details)
extension SidebarListStyle: ListStyleDeferredToRenderer {
public func sectionHeader<Header>(_ header: Header) -> AnyView where Header: View {
AnyView(

40
docs/FAQ.md Normal file
View File

@ -0,0 +1,40 @@
# Frequently Asked Questions
## Why does Tokamak use HTML/CSS for rendering in the browser?
HTML/CSS has a benefit of built-in accessibility support. Other rendering systems in the browser (such as Canvas or WebGL/WebGPU)
that bypass HTML/CSS would have to reimplement accessibility from scratch, with all the downsides of increased binary
size and performance overhead. With HTML/CSS we can rely on what's already included in the browser and has been tested and polished
by hundreds of engineers over the decades of browser development.
Additionally, we can rely on optimized CSS layout algorithms where possible. This also unlocks more use-cases for Tokamak, such as
[static HTML generation](https://github.com/TokamakUI/TokamakPublish) and [server-side rendering](https://github.com/TokamakUI/TokamakVapor).
At the same time, Tokamak has [a new layout system in development](https://github.com/TokamakUI/Tokamak/pull/472) that accesses
DOM directly for layout calculations, bypassing CSS for a lot (or potentially all) of its algorithms.
## Does the word Tokamak mean anything? Why is it called this?
The project was originally inspired by [React](https://reactjs.org), which utilizes a model of an atom in its logo,
apparently as a reference to nuclear reactors. [Токамак](https://en.wikipedia.org/wiki/Tokamak) is a nuclear fusion reactor, and
the word itself is roughly an abbreviation of "**to**roidal **cha**mber with **ma**gnetic **c**oils".
## What's the history behind it?
The first commit to this project was made in September 2018, 9 months before SwiftUI was publicly announced. The original maintainer of
it had a feeling it would be beneficial to replace UIKit and AppKit with a declarative UI framework. It originally started
as a port of the [React API](https://reactjs.org/) to Swift. The opinion of the original maintainer was that React was a pretty good
solution at that time and was adopted widely enough for people to be acquainted with the general idea. The architecture of React
was quite modular, and it had a well-documented reconciler algorithm that worked independently from platform-specific renderers.
The plan was to build something similar to the React API in Swift with renderers for macOS and iOS, and then potentially for
WebAssembly, Android, and Windows. Shortly after a short series of [0.1 releases with the React
API](https://github.com/swiftwasm/Tokamak/blob/0.1.2/README.md), Tokamak for iOS/macOS was [sherlocked](https://en.wikipedia.org/wiki/Sherlock_(software)#Sherlocked_as_a_term) by
SwiftUI at WWDC 2019. It no longer made sense to continue developing it in that form for Apple's platforms, even though it could
still be useful for other platforms. The original maintainer thought it would be hard to convince Swift developers to use something
that doesn't look like SwiftUI, at least as long as the majority of Swift developers target Apple's platforms.
In addition to SwiftUI and React, we'd like to credit [SwiftWebUI](https://github.com/SwiftWebUI/SwiftWebUI) for reverse-engineering
some of the bits of SwiftUI and kickstarting the front-end Swift ecosystem for the web. [Render](https://github.com/alexdrone/Render),
[ReSwift](https://github.com/ReSwift/ReSwift), [Katana UI](https://github.com/BendingSpoons/katana-ui-swift), and
[Komponents](https://github.com/freshOS/Komponents) declarative UI frameworks served as additional inspiration for the project.