|
2 | 2 |
|
3 | 3 | Similar to **Calico**, **Swing.IO** takes its concepts from Cats Effect and FS2. This page lists some common idioms used in Swing.IO development.
|
4 | 4 |
|
| 5 | +Note: Some of these docs were copied from [Calico](https://www.armanbilge.com/calico/concepts.html), as these are very similar projects with a similar basis. |
| 6 | + |
5 | 7 | ## Components and resource management
|
6 | 8 |
|
7 | 9 | The most important idea of **Swing.IO** is that all components are represented as a `Resource[IO, Component[IO]]`.
|
@@ -34,4 +36,67 @@ val component: Resource[IO, Component[IO]] = box("Hello, checkbox:", checkbox())
|
34 | 36 | ```
|
35 | 37 | In this example, a checkbox that does nothing composes together to create a `Resource`
|
36 | 38 |
|
37 |
| -TODO: Complete docs LATER™ |
| 39 | +## Signals |
| 40 | + |
| 41 | +`Signal`s are time varying values. You can always obtain the current value and subscribe to stream of update events that notify when it is updated. |
| 42 | +This is ideal for use in UI Components: they can always render immediately with the current value, and re-render only when there are updates. |
| 43 | + |
| 44 | +`Signal` is a monad, enabling them to be transformed with pure functions and composed with each other. |
| 45 | +Using transformation and composition, you can derive a `Signal` that contains precisely the data you are interested in. |
| 46 | + |
| 47 | +```scala |
| 48 | +import cats.syntax.all.* |
| 49 | + |
| 50 | +case class S3Weapon(name: String) |
| 51 | +enum Games { |
| 52 | + case Splatoon1 |
| 53 | + case Splatoon2 |
| 54 | + case Splatoon3 |
| 55 | +} |
| 56 | +val signals = ( |
| 57 | + SignallingRef[IO].of(S3Weapon("52 Gal")), |
| 58 | + SignallingRef[IO].of(Games.Splatoon3), |
| 59 | +) |
| 60 | + |
| 61 | +val weapons: Seq[S3Weapon] = Seq(???) |
| 62 | + |
| 63 | +val app = signals.flatMap { (weaponSig, gameSig) => window( |
| 64 | + flow( |
| 65 | + comboBox[S3Weapon].withSelf { self => ( |
| 66 | + items := weapons, |
| 67 | + renderer := { (it: S3Weapon) => it.name }, |
| 68 | + onSelectionChange --> { |
| 69 | + _.foreach(_ => self.value.get.flatMap(weaponSig.set)) |
| 70 | + } |
| 71 | + )}, |
| 72 | + comboBox[Games].withSelf { self => ( |
| 73 | + items := Seq(Games.Splatoon1, Games.Splatoon2, Games.Splatoon3), |
| 74 | + onSelectionChange --> { |
| 75 | + _.foreach(_ => self.value.get.flatMap(gameSig.set)) |
| 76 | + } |
| 77 | + )} |
| 78 | + ) |
| 79 | + ) |
| 80 | + } |
| 81 | +``` |
| 82 | + |
| 83 | +There are various ways to obtain a `Signal`. |
| 84 | + |
| 85 | +- Create a `SignallingRef` with an initial value. |
| 86 | +```scala |
| 87 | +SignallingRef[IO].of("initial value") |
| 88 | +``` |
| 89 | + |
| 90 | +- Derive a `Signal` from a `Stream`, by “holding” its latest value. |
| 91 | +```scala |
| 92 | +def stringStream: Stream[IO, String] = ??? |
| 93 | +stringStream.holdResource("initial value") |
| 94 | +stringStream.holdOptionResource // use None for the intitial value |
| 95 | +``` |
| 96 | + |
| 97 | +## Glitch-free rendering |
| 98 | + |
| 99 | +Swing forces all access to swing components to be on the Event Dispatch Thread. This ensures that rendering is glitch-free. |
| 100 | +**Swing.IO** automatically handles this by evaluating all access to swing components on the EDT, and it lets Cats Effect |
| 101 | +handle anything that doesn't touch swing components. This means you get all the benefits of multithreading your Swing app |
| 102 | +without the hassle of having to deal with the EDT. |
0 commit comments