Commit Graph

603 Commits

Author SHA1 Message Date
Carson Katri 12a6256ec0
Add ProgressView (#425)
This adds `ProgressView` using the `<progress>` tag on the web.

* Add ProgressView implementation

* Fix native demo

* Enable Foundation.Progress in non-WASI environments

* Fix wasm build

* Update progress coc

* Improve snapshot copy error handling

* Use RenderingTests as directory name

* Fix snapshots CI script

* Make test failures fail the CI job

* Snapshot script debugging

* Copy failed snapshots in a different way

* Call `exit 1` when tests fail

* Use correct directory in the upload step

* Update test image

* Update .github/workflows/ci.yml

Co-authored-by: ezraberch <49635435+ezraberch@users.noreply.github.com>
Co-authored-by: Max Desiatov <max@desiatov.com>
2021-07-17 16:43:51 +01:00
Carson Katri ab5e564ada
Animation implementation using the Web Animations API (#427) 2021-07-13 08:48:45 -04:00
Carson Katri a064956095
Add `scaleEffect` modifier (#424) 2021-07-13 08:48:28 -04:00
Carson Katri ff3f81dbfd
Add `aspectRatio` modifier (#422) 2021-07-12 16:59:24 -04:00
Carson Katri b6790c5c6d
Add support for custom fonts (#421) 2021-07-12 12:10:26 -04:00
Carson Katri 30f55d9814
Check minWidth/Height == nil (#420) 2021-07-09 09:58:38 -04:00
Carson Katri 2efa80a57d
Add Primary/Secondary/Tertiary/QuaternaryContentStyle (#419) 2021-07-07 18:44:34 -04:00
Carson Katri 4a7748ad6b
Add `Material` to the HTML renderer (#418) 2021-07-07 18:09:13 -04:00
Carson Katri 54146b8a38
Improve ShapeStyles to match iOS 15+ (#417) 2021-07-07 16:04:26 -04:00
Carson Katri 79a9a66da2
Add ContainerRelativeShape (#416) 2021-07-07 13:21:07 -04:00
Max Desiatov 738455be68
Add HTML implementation for `opacity` modifier (#415)
* Support `spacing` property on `HStack`/`VStack`

* Remove unused properties in `StackDemo`

* Implement stack spacing with grid gaps

* Fix GTK build

* Bump browser requirements in README.md

* Remove outdated FIXME

* Generalize snapshot timeouts

* Prevent excessive CSS style leaks of properties

* Add HTML implementation for `opacity` modifier

* Update tests name to reflect file name

* Fix missing semicolon in opacity style attribute

Co-authored-by: ezraberch <49635435+ezraberch@users.noreply.github.com>

* Remove duplicate declaration

Co-authored-by: ezraberch <49635435+ezraberch@users.noreply.github.com>
2021-07-07 14:14:14 +01:00
Max Desiatov 719c109811
Support `spacing` property on `HStack`/`VStack` (#273)
It's much easier to implement stack spacing when stacks are rendered as single-row or single-column grids, and grid gaps already work in all browsers. For this we need to slightly bump browser version requirements, most notably from Safari 11 to Safari 12.

Resolves #272.

* Remove unused properties in `StackDemo`

* Implement stack spacing with grid gaps

* Fix GTK build

* Bump browser requirements in README.md

* Remove outdated FIXME

* Generalize snapshot timeouts

* Prevent excessive CSS style leaks of properties
2021-07-07 14:01:31 +01:00
yonihemi 2dcbc67cd3
Explicitly import CoreFoundation (#413)
This allows TokamakDemo to be built with [latest SwiftWasm 5.4 toolchain](https://github.com/swiftwasm/swift/releases/tag/swift-wasm-5.4-SNAPSHOT-2021-06-17-a) in **debug mode** (compiler still crashes when building for release).

BTW this import could be anywhere in the target, couldn't find a file that felt natural to include it in.

* Move import statement to CGStubs.swift
2021-06-30 14:14:59 +01:00
ezraberch 9aa88a1978
Fix handling of stroked shapes (#414)
The PR fixes multiple bugs which prevent stroked shapes from rendering correctly.

1. Allow environment injection into  `_StrokedShape`. This causes stroked shapes to no longer crash (#322).
2. Change `Path.strokedPath` and `Path.trimmedPath` to allow the `sizing` of the base shape to be inherited. Without this, stroked shapes can appear as 0x0 in size, making them invisible.
3. Change `_ShapeView` in the StaticHTML renderer to merge attributes in the `svg` rather than placing the `svg` in a `div`. This allows proper rendering when multiple shapes are in a stack.

Finally, the `Path` demo has been modified to add a stroked circle.
2021-06-28 12:45:23 +01:00
Max Desiatov d35e37c4f5
Add a snapshot test for `Path` SVG layout (#412)
This adds a dependency on the [SnapshotTesting](https://github.com/pointfreeco/swift-snapshot-testing) library, which allows testing our SVG layout algorithm end-to-end. We use the `--screenshot` flag of Chromium-based browsers (MS Edge in this case) to produce a PNG snapshot of a view rendered with `StaticHTMLRenderer`.

This test works only on macOS for now due to its dependency on `NSImage`, but that should be fine as we'd expect the same SVG output to be rendered in the same way on all platforms.

* Implement snapshot tests with headless MS Edge

* Increase snapshot tests timeout

* Force 1.0 resolution scale for headless Edge

* Avoid complex layouts in the snapshot test

* Exclude dir from target sources, upload failures

* Add a test to verify that fusion works

* Enable fusion of modifiers nested three times

* Filter out empty attributes

* Run snapshot tests only on macOS for now

* Fully exclude snapshot testing on WASI

* Fix `testOptional` snapshot

* Clean up code formatting

* Copy failed snapshots to a readable directory

* Make the copy script more resilient

* Use `--force-color-profile=srgb` Chromium flag

* Re-enable spooky hanger test

* Clean up testSpookyHanger

* Fix linter warnings

* Fix file_length linter warning

* Silence linter warning for `Text.attributes` func

* Split `PathLayout.swift` to appease the linter
2021-06-21 16:45:21 +01:00
Max Desiatov e6c37a4c80
Attempt `padding` modifier fusion to avoid nested `div`s (#253)
This allows fusing nested `.padding` modifiers into a single `div` that sums up padding values from all these modifiers.

Before:

```swift
Text("text").padding(10).padding(20)
```

rendered to this (text styling omitted for brevity):

```html
<div style="padding-top: 20.0px; padding-left: 20.0px; padding-bottom: 20.0px; padding-right: 20.0px;">
  <div style="padding-top: 10.0px; padding-left: 10.0px; padding-bottom: 10.0px; padding-right: 10.0px;">
    <span>text</span>
  </div>
</div>
```

Now it renders as

```html
<div style="padding-top: 30.0px; padding-left: 30.0px; padding-bottom: 30.0px; padding-right: 30.0px;">
  <span>text</span>
</div>
```

I hope this approach could be applied to other modifier combinations where it makes sense (in separate PRs).

* Attempt `padding` modifier fusion

* Fix linter warning

* Add a test to verify that fusion works

* Enable fusion of modifiers nested three times

* Filter out empty attributes

* Run snapshot tests only on macOS for now

* Fully exclude snapshot testing on WASI

* Fix `testOptional` snapshot

* Clean up code formatting
2021-06-21 16:00:28 +01:00
Max Desiatov ae219e947b
Use `CGFloat`, `CGPoint`, `CGRect` from Foundation (#411)
Resolves #404.

This also allows us to write more tests that are source-compatible with SwiftUI.

* Use `CGFloat`, `CGPoint`, `CGRect` from Foundation

* Fix GTK build

* Fix macOS build
2021-06-19 19:29:44 +01:00
Max Desiatov ac69bbc3e5
Add reconciler stress tests for elaborate testing (#381)
Most of the changes are related to the use of OpenCombineShim (available in upstream OpenCombine now) instead of CombineShim. But there is also a new test added during the investigation of #367, where an app is rendered end-to end, which is a good way to expand our test suite I think.

* Use immediate scheduler in TestRenderer

This allows running our test suite on WASI too, which doesn't have Dispatch and also can't wait on XCTest expectations. Previously none of our tests (especially runtime reflection tests) ran on WASI.

* Run `carton test` and `carton bundle` in separate jobs

* Bump year in the `LICENSE` file

* Add reconciler stress tests for elaborate testing

* Move default App implementation to TestRenderer

* Use OpenCombineShim instead of CombineShim
2021-06-15 23:01:45 +01:00
ezraberch 3302a5163c
Fix rendering of spacers after DOMRenderer.update is called (#410)
`DOMRenderer.mount` contains code which can be necessary to properly render spacers. However, `update` can overwrite what this code does, leading to the problem described in #395.

This PR modifies `update` to fix this issue.
2021-06-15 22:59:33 +01:00
Max Desiatov 5926e9f182
Replace `ViewDeferredToRenderer`, fix renderer tests (#408)
This allows writing tests for `TokamakStaticHTML`, `TokamakDOM`, and `TokamakGTK` targets.

The issue was caused by conflicting `ViewDeferredToRenderer` conformances declared in different modules, including the `TokamakTestRenderer` module. 

This works around a general limitation in Swift, which was [discussed at length on Swift Forums previously](https://forums.swift.org/t/an-implementation-model-for-rational-protocol-conformance-behavior/37171). When multiple conflicting conformances to the same protocol (`ViewDeferredToRenderer` in our case) exist in different modules, only one of them is available in a given binary (even a test binary). Also, only of them can be loaded and used. Which one exactly is loaded can't be known at compile-time, which is hard to debug and leads to breaking tests that cover code in different renderers. We had to disable `TokamakStaticHTMLTests` for this reason.

The workaround is to declare two new functions in the `Renderer` protocol:

```swift
public protocol Renderer: AnyObject {
  // ...
  // Functions unrelated to the issue at hand skipped for brevity.

  /** Returns a body of a given pritimive view, or `nil` if `view` is not a primitive view for
   this renderer.
   */
  func body(for view: Any) -> AnyView?

  /** Returns `true` if a given view type is a primitive view that should be deferred to this
   renderer.
   */
  func isPrimitiveView(_ type: Any.Type) -> Bool
}
```

Now each renderer can declare their own protocols for their primitive views, i.e. `HTMLPrimitive`, `DOMPrimitive`, `GTKPrimitive` etc, delegating to them from the implementations of `body(for view:)` and `isPrimitiveView(_:)`. Conformances to these protocols can't conflict across different modules. Also, these protocols can have `internal` visibility, as opposed to `ViewDeferredToRenderer`, which had to be declared as `public` in `TokamakCore` to be visible in renderer modules.
2021-06-07 17:24:02 +01:00
ezraberch da9843d07f
Allow DOMRenderer to render buttons with non-Text labels (#403) (#409)
Currently, `DOMRenderer` can only handle `Button`s where is the label is `Text`. If it is any other `View`, the `Button` is not rendered. This is the cause of #403.

This PR removes this restriction. Additionally, it expands the `ButtonStyle` demo to include `Button`s with complex labels.
2021-06-07 17:17:43 +01:00
Max Desiatov 44280847cf
Sort attributes in HTML nodes when rendering (#346)
This makes attributes order deterministic and allows testing against HTML renderer output, while currently attributes order is random.

Benchmarks results:

```
name                            time            std        iterations
---------------------------------------------------------------------
render Text                         9667.000 ns ±   4.35 %     145213
render App unsorted attributes     51917.000 ns ±   4.23 %      26835
render App sorted attributes       52375.000 ns ±   1.62 %      26612
render List unsorted attributes 34546833.500 ns ±   0.79 %         40
render List sorted attributes   34620000.500 ns ±   0.69 %         40
```

Looks like on average there's ~0.2% difference in performance. I was leaning towards enabling sorting by default, but we're benchmarking here only with short attribute dictionaries, I wonder if the difference could become prominent for elements with more attributes. I kept sorting disabled by default after all, but still configurable.

`var html: String` on `StaticHTMLRenderer` was changed to `func render(shouldSortAttributes: Bool = false) -> String` to allow configuring this directly.

* Sort attributes in HTML nodes when rendering

* Make sorting configurable, add benchmarks

* Disable sorting by default, clean up product name

* Fix build errors
2021-06-06 18:52:15 +01:00
ezraberch 77759777f1
Fix DOMRenderer crash after DOM has been directly modified. (#326, #369) (#407)
When _domRef is used to directly modify the DOM, this causes the state of the DOM to no longer match the View from which it was rendered. When the renderer later tries to unmount the modified element, this can cause a crash.

This PR fixes the crash by catching (and ignoring) this failure in DOMRenderer.unmount. This fixes #326 and #369, which are the same issue.

Note that directly modifying the DOM with `_domRef` can still cause problems, as the state mismatch remains. For example, an update to the `View` can cause the renderer to overwrite those DOM changes.
2021-05-31 14:42:39 +01:00
Carson Katri 096ec5c4a2
Add multilineTextAlignment and use <br> to split spans up (#401) 2021-05-13 14:03:29 -04:00
Max Desiatov 57f57174c5 Fix typo in `CHANGELOG.md` 2021-05-03 12:14:52 +01:00
Max Desiatov 4a372c3fa8
Update `CHANGELOG.md` for the 0.7.0 release (#398)
Time for a new release!
2021-05-03 12:08:04 +01:00
Carson Katri d914e1bdc9
Add dynamicMemberLookup attribute to Binding (#396) 2021-04-09 09:58:03 -04:00
Emil 5c458f92b8
Add `DatePicker` to the `TokamakDOM` module (#394)
This fixes #320 by adding a SwiftUI-compatible `DatePicker`. However, `DatePickerStyle` is not supported. 

This uses the HTML inputs `date`, `time`, or `datetime-local`, depending on the given `displayedComponents`. This means that not all browsers show the picker, as Mac Safari currently does not support them. Safari on Mac will just show an ISO-format text field. If the date is in an invalid format, the binding will not receive updates until it becomes parseable by JSDate.

On supported browsers, the binding gets updated in real time, as you would expect, with a Foundation.Date, just like SwiftUI.

* Add DatePicker to TokamakCore and TokamakDOM

* Fix crash on invalid date

* Update progress.md and add credit

* Fix time zone related issues with the DatePicker

* Add DatePickerDemo to the TokamakDemo

* Fix overview for DatePicker

* Fix NativeDemo build
2021-03-28 21:32:29 +01:00
Max Desiatov bde7de9be0
Use `String(reflecting:)` vs `String(describing:)` (#391)
Resolves #218.

`String(describing:)` initializer applied to metatypes does not include a module name, which can cause problems if two different types with same name come from different modules.

OTOH `String(reflecting:)` does include module name, which makes these reflection strings slightly longer, but should prevent obscure issues with name collisions from happening.
2021-03-20 15:42:54 +00:00
Max Desiatov 8076035120
Clarify the difference between HTML and DynamicHTML (#389)
Resolves #388.
2021-03-11 10:21:48 +00:00
filip-sakel 4d211af563
Add '_spi(TokamakCore)' to ideally internal public members. (#386)
Adds `_spi(TokamakCore)` to the modules:
1. TokamakCore
2. TokamakDOM
3. TokamakGTK
4. TokamakStaticHTML

The attribute is applied to:
1. All `View` bodies in TokamakCore — either primitive or regular like `Color`
2. `ViewDeferredToRenderer` bodies
4. `ParentView` `children` members
5. `View` modifiers (such as  `_onMount(perform:)`)
6. Other members of types (like `Color._withScheme`, and `_AnyApp._launch`) 

The attribute semantics (from my brief testing)
1. It can only be applied to `public` declarations
2. It ensures that every SPI declaration is exposed only by other SPI declarations (i.e. `@_spi(Module) public enum A {}; public var a: A` is illegal)
3. Regularly importing a library prohibits clients from accessing SPI declarations that are not protocol witnesses (with an error).
4. Regularly importing a library "discourages" clients from accessing SPI protocol witnesses by hiding them from the autocompletion suggestions (i.e. users can still access `body` of `Text`, but autocompletion hides it).
5. For a declaration marked with `@_spi(Module)`, a client has to write `@_spi(Module) import Library` in order to normally access such SPI declarations.

* Add '_spi(TokamakCore)' to ideally internal public members.

* Remove `_spi` attribute on '_ConditionalContent'.

* Remove spi from 'Path._PathBox', 'Font._Font', and '_TupleScene.body'.

* Remove spi from types.

* Remove trailing whitespace.

* Apply spi to 'View' modifiers.

* Add _spi imports.

* Introduce 'PrimitiveView'.

* Remove 'PrimitiveView' conformances outside of TokamakCore.

* Fix `PrimitiveView` default implementation.

* Remove "BubbleCore" references.
2021-03-08 18:49:05 +00:00
Max Desiatov 4a1101c21a
Delete unused test.sh file 2021-02-23 19:51:12 +00:00
Morten Bek Ditlevsen 19cf8b6782
Made properties of CGPoint, CGSize and CGRect vars instead of lets. Initialized array used by CGAffineTransform concatenation (#382) 2021-02-18 08:04:37 +01:00
Max Desiatov eeddfe9e4b
Use immediate scheduler in TestRenderer (#380)
* Use immediate scheduler in TestRenderer

This allows running our test suite on WASI too, which doesn't have Dispatch and also can't wait on XCTest expectations. Previously none of our tests (especially runtime reflection tests) ran on WASI.

* Run `carton test` and `carton bundle` in separate jobs

* Bump year in the `LICENSE` file
2021-02-15 11:36:23 +00:00
Mathew Polzin ea94ef9bcc
Simple Code Coverage analysis (#378)
Closes https://github.com/TokamakUI/Tokamak/issues/350.

Adds simple code coverage analysis. Until the GitHub token is set up in this repo, you can see the results including a comment on the PR here: https://github.com/mattpolzin/Tokamak/pull/2

* Adding codecov CI workflow.

* doh. forgot to skip build.

* drop unneeded llvm env var from build invocation. make comment always run.

* try a way to update a previous comment

* try to get comments to run on failure.

* use bug fix to codecov action.

* fix comment search string and drop min coverage

* use replace on comment updates

* attempt at diffing coverage from main branch

* comment out tests to affect test coverage

* try method for multiline outputs

* do i need to put it in its own script file

* right, switch coverage gen order to have branch checked out second.

* attempt to get code quotation around diff output.

* switch to git diff

* add note about the new script.

* try echoing a warning.

* post warning on success and error on failure.

* uncomment tests
2021-02-05 11:47:56 +00:00
Max Desiatov 53b474c33f
Delete unused `codecov.yml` config 2021-02-01 22:08:59 +00:00
Max Desiatov 5d951ad0af
Delete unused `codecov.sh` script 2021-02-01 22:08:39 +00:00
Max Desiatov d75b185553
Add checks for metadata state (#375)
* Add checks for metadata state

Type metadata actually *is* mutable, is updated by the runtime, and we need to be careful when reading from it.

* Add a precondition for type metadata state

* Replace `precondition` with an `assert`

* Add missing license headers
2021-02-01 10:02:49 +00:00
Max Desiatov 9a79548312
Use upstream OpenCombine instead of a fork (#377) 2021-01-30 10:26:30 +00:00
Max Desiatov 30b8d46aa8
Update JavaScriptKit, OpenCombineJS dependencies (#376) 2021-01-29 07:53:21 +00:00
Max Desiatov 0e89ea9529
Clean up metadata reflection code (#372)
Our OpenCombine fork no longer depends on Runtime, and we don't need much from it other than struct metadata. I removed the unused bits and bobs and kept only a minimal subset of it that we really need. This should make it easier for us to test and debug, as #367 has shown that some weird stuff may still lurk in that area.

* Add a test for environment injection

We had some issues in this code area previously and I'm thinking of refactoring it in attempt to fix #367. Would be great to increase the test coverage here before further refactoring.

* Update copyright years in `MountedElement.swift`

* Update copyright years in the rest of the files

* Vend the Runtime library directly

* Remove unused class, enum, tuple, func reflection

* Remove unused models and protocol metadata

* Remove unused MetadataType and NominalMetadataType

* Remove unused protocols, rename RelativePointer

* Remove more unused protocols

* Use immutable pointers for reflection

* Update copyright headers
2021-01-27 18:24:04 +00:00
Max Desiatov 07ccef88e1
Add @foscomputerservices to the list of maintainers (#373)
David has previously submitted super useful bug reports and fixes, and his experience in porting larger projects from SwiftUI to Tokamak is very valuable. Welcome to the team!
2021-01-25 20:08:53 +00:00
Max Desiatov 192c43b140
Refactor environment injection, add a test (#371)
* Add a test for environment injection

We had some issues in this code area previously and I'm thinking of refactoring it in attempt to fix #367. Would be great to increase the test coverage here before further refactoring.

* Update copyright years in `MountedElement.swift`

* Update copyright years in the rest of the files
2021-01-25 11:39:09 +00:00
Max Desiatov e04b7934fb
Replace uses of the Runtime library with stdlib (#370)
This should allow us to remove the Runtime dependency eventually, which seems to be unstable, especially across different platforms and Swift versions.

Seems to resolve in one instance https://github.com/TokamakUI/Tokamak/issues/367. There are a few other places where `typeInfo` is still used, I'll clean that up in a follow-up PR.

* Replace uses of the Runtime library with stdlib

* Remove irrelevant Runtime library imports

* Add TokamakCoreBenchmark target
2021-01-24 15:26:51 +00:00
Max Desiatov 9549282e53
Use `macos-latest` agent for the GTK build (#360)
`macos-latest` still points to macOS 10.15 right now, which has more agent's capacity, and macOS 11.0 agents are slow and unreliable. At the dame time, we have to keep using macOS 11.0 for the core build to have access to the new `App` lifecycle in the native demo project.
2021-01-22 12:54:49 +00:00
Max Desiatov 6f0528fe06
Add a benchmark target and a script to run it (#365)
* Add a benchmark target and a script to run it

Benchmarks need to be built in the release mode, that's why I created a separate `benchmark.sh` script to build it and run it.

I've also cleaned up a compiler warning in `TextEditor.swift` and bumped macOS agents to use Xcode 12.3 instead of 12.2.

* Benchmark `App` and `WindowGroup` rendering

* Add a `benchmark` task to `tasks.json`

* Exit `NativeDemo` directory before benchmarking
2021-01-20 21:09:54 +00:00
Max Desiatov 67aea3cc3b
Fix crashes in views with optional content (#364)
* Add TokamakStaticHTMLTests target

* Add AnyOptional, clarify conformances issues
2021-01-20 08:07:01 +03:00
Morten Bek Ditlevsen 163005dfe0
Add GTK support for `SecureField` (#363) 2021-01-19 18:20:39 +01:00
David Hunt ee4e8debc1
Two infinite loop fixes (#359)
Both of these issues are fixes to `CustomStringConvertible` implementations that either directly or indirectly called themselves via `String(describing:)`.
2021-01-19 15:13:48 +03:00
Morten Bek Ditlevsen 9199a90551
Added TextField support for GTK using GtkEntry (#361)
* Added TextField to TokamakGTK - WIP

* Made TextView update on gtk entry changes
2021-01-19 12:47:06 +01:00