Skip to content

Commit 65a97d1

Browse files
committed
update push_graph docs and some cache docs
1 parent ee7e12a commit 65a97d1

File tree

6 files changed

+65
-58
lines changed

6 files changed

+65
-58
lines changed

CHANGELOG.md

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -49,6 +49,14 @@
4949
**Breaking Changes:**
5050
* Breaking changes to Scenic.Cache. It has been replaced by asset specific caches.
5151

52+
| Asset Type | Module |
53+
| --- | --- |
54+
| Static Textures | `Scenic.Cache.Static.Texture` |
55+
| Fonts | `Scenic.Cache.Static.Font` |
56+
| Font Metrics | `Scenic.Cache.Static.FontMetrics` |
57+
| Dynamic Textures | `Scenic.Cache.Dynamic.Texture` |
58+
59+
5260
## 0.9.0
5361
* Much improved testing
5462
* Much improved documentation

guides/custom_fonts.md

Lines changed: 24 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -2,12 +2,12 @@
22

33
Scenic ships standard with support for the [Roboto](https://fonts.google.com/specimen/Roboto) and [Roboto_Mono](https://fonts.google.com/specimen/Roboto+Mono) fonts. The [Roboto_Slab](https://fonts.google.com/specimen/Roboto+Slab) font is no longer standard in Scenic.
44

5-
You can add the RobotoSlab font, and most any other TrueType font you want to use into Scenic.
5+
You can, however, add the RobotoSlab font, and most any other TrueType font you want to use into Scenic.
66

77
To do this you will need do the following steps.
88

99
1. Download the font you want to use and add it to the static files of your `priv/` folder in your application. Please be aware of font licenses and make good choices.
10-
2. Use the truetype_metrics hex package to generate a `*.metrics` file, which you also add to your `priv/` folder. This tool will also change the name of your font so that a hash of the data is included in the file name.
10+
2. Use the [truetype_metrics](https://hex.pm/packages/truetype_metrics) hex package to generate a `*.metrics` file, which you also add to your `priv/` folder. This tool will also change the name of your font so that a hash of the data is included in the file name.
1111
3. Load the `*.metrics` file into the `Scenic.Cache.Static.FontMetrics` cache.
1212
4. refer to the font by the key to the metrics file in the graph where you want to use it.
1313

@@ -28,9 +28,9 @@ priv/
2828

2929
## Generate the `\*.metrics` file
3030

31-
Use the truetype_metrics tool to generate a font metrics file. This tool is not included in Scenic because this code is complicated (don't get me started on the TrueType format) and only needs to be run once when you set it up.
31+
Use the [truetype_metrics](https://hex.pm/packages/truetype_metrics) tool to generate a font metrics file. This tool is not included in Scenic because this code is complicated (don't get me started on the TrueType format) and only needs to be used once when you set up your project.
3232

33-
This tool will do two things. It will create a .metrics file and decorate the name of the font itself with a hash of its contents.
33+
This tool will do two things. It will create a `\*.metrics` file and decorate the name of the font itself with a hash of its contents.
3434

3535
You may need to install the tool as an archive first
3636

@@ -56,24 +56,41 @@ priv/
5656
RobotoSlab-Regular.ttf.metrics
5757
```
5858

59-
Read the truetype_metrics documentation to learn how to recurse font folders, control the font name decoration and force the file to be re-generated.
59+
Read the [truetype_metrics](https://hex.pm/packages/truetype_metrics) documentation to learn how to recurse font folders, control the font name decoration and force the file to be re-generated.
6060

61-
## Load the `\*.metrics` file into the cache
61+
## Load the `\*.metrics` file and the font into the cache
6262

63-
In your scene, you need to make load the `\*.metrics` file into the `Scenic.Cache.Static.FontMetrics` cache.
63+
In your scene, you need to make load both the `\*.metrics` file into the `Scenic.Cache.Static.FontMetrics` cache and the font itself into the `Scenic.Cache.Static.Font` cache.
6464

6565

6666
```elixir
67+
@font_folder :code.priv_dir(:my_app) |> Path.join("/static/fonts")
68+
@custom_font_hash "0IXAWqFTtjn6MKSgQOzxUgxNKGrmyhqz1e2d90PVHck"
69+
@custom_metrics_path :code.priv_dir(:scenic_example)
70+
|> Path.join("/static/fonts/Roboto_Slab/RobotoSlab-Regular.ttf.metrics")
71+
@custom_metrics_hash Scenic.Cache.Support.Hash.file!(@custom_metrics_path, :sha)
72+
73+
def init(_, _opts) do
74+
# load the custom font
75+
Cache.Static.Font.load(@font_folder, @custom_font_hash)
76+
Cache.Static.FontMetrics.load(@custom_metrics_path, @custom_metrics_hash)
77+
78+
# no need to put the graph into state as we won't be using it again
79+
{:ok, nil, push: @graph}
80+
end
6781
```
6882

6983
Note that if you use this font everywhere, you may want to load it once during app startup with the `:global` scope. Then you can just refer to it without having to load it in every scene.
7084

7185
```elixir
86+
Cache.Static.Font.load(@font_folder, @custom_font_hash, scope: :global)
87+
Cache.Static.FontMetrics.load(@custom_metrics_path, @custom_metrics_hash, scope: :global)
7288
```
7389

7490
## Refer to the font by the metrics key
7591

7692
Finally, you use the key to refer to the font metrics in the graph. The font itself will be loaded and used automatically as needed. The metrics file already contains the hash of the font itself.
7793

7894
```elixir
95+
text_spec("Font Test", translate: {0, 40}, font_size: 60, font: @custom_metrics_hash)
7996
```

guides/overview_graph.md

Lines changed: 1 addition & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -84,9 +84,8 @@ This time, we've assigned ids to both of the text primitives. This makes it easy
8484
@graph
8585
|> Graph.modify( :small_text, &text(&1, "Smaller Hello", font_size: 16))
8686
|> Graph.modify( :big_text, &text(&1, "Bigger Hello", font_size: 60))
87-
|> push_graph()
8887

89-
Notice that the graph is modified multiple times in the pipeline. The `push_graph/1` function is relatively heavy when the graph references other scenes. The recommended pattern is to make multiple changes to the graph and then push once at the end.
88+
Notice that the graph is modified multiple times in the pipeline.
9089

9190

9291
## What to read next?

guides/overview_primitives.md

Lines changed: 1 addition & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -93,9 +93,8 @@ In the above graph, we've assigned `:id` values to both primitives. This makes i
9393
@graph
9494
|> Graph.modify( :small_text, &text(&1, "Smaller Hello", font_size: 16))
9595
|> Graph.modify( :big_text, &text(&1, "Bigger Hello", font_size: 60))
96-
|> push_graph()
9796

98-
Notice that the graph is modified multiple times in the pipeline. The `push_graph/1` function is relatively heavy when the graph references other scenes. The recommended pattern is to make multiple changes to the graph and then push once at the end.
97+
Notice that the graph is modified multiple times in the pipeline.
9998

10099
The last parameter to `Graph.modify/3` is a pointer to a function that receives a primitive and returns the the new primitive that should be inserted in its place.
101100

guides/overview_scene.md

Lines changed: 30 additions & 45 deletions
Original file line numberDiff line numberDiff line change
@@ -55,8 +55,7 @@ whose helper function is imported from the `Scenic.Components` module.
5555
|> button({"Do Something", :btn_something}, translate: {20, 180})
5656

5757
def init( _scene_args, _options ) do
58-
push_graph( @graph )
59-
{:ok, @graph}
58+
{:ok, @graph, push: @graph}
6059
end
6160

6261
...
@@ -85,14 +84,12 @@ build and push your first graph, the user will see a blank space or screen until
8584
you are ready.
8685

8786
def init( _scene_args, opts ) do
88-
push_graph( @graph )
89-
9087
state = %{
9188
graph: @graph,
9289
viewport: opts[:viewport]
9390
}
9491

95-
{:ok, state}
92+
{:ok, state, push: @graph}
9693
end
9794

9895
The first argument, `scene_args`, is any term that you pass to your scene when
@@ -134,51 +131,40 @@ option | description
134131

135132
## Pushing a Graph
136133

137-
`push_graph/1` is a "magic" function. Magic functions embody knowledge of the
138-
system outside of the data passed into them and are not purely "functional" in
139-
the programming sense. I am generally against using magic functions. However,
140-
after much thought and experimentation, I landed on this one bit of magic to
141-
accomplish a difficult job.
142-
143-
> `push_graph/1` is a private function that is injected into your scene by the
144-
> `use Scenic.Scene` macro. Your scene will not work without it.
145-
146-
In a nutshell, `push_graph/1` does two jobs.
147-
148-
First, it triggers the [life-cycle](scene_lifecycle.html) of any components the
149-
graph references. This means that component processes may start or stop when you
150-
push a graph. This will only happen if you change components used in the graph.
134+
Previous to v0.10, the way to push a graph to the ViewPort was the magic `push_graph/1` function, This has been deprecated in favor of a more functional return option. `push_graph/1` was also interfering with the newer OTP 21+ continue callbacks and timeouts and such. Since this is only a deprecation `push_graph/1` will continue to work, but will log a warning when used. `push_graph/1` will be removed in a future release.
151135

152-
Second, it prepares the graph for use by the [Drivers](overview_driver.html) and
153-
the input path of the [ViewPort](overview_viewport.html). This mostly involves
154-
stripping internal data and caching the resulting term in an
155-
[ets](https://elixirschool.com/en/lessons/specifics/ets/) table so that it can
156-
be used very quickly, on demand, by those systems.
136+
The new, better way to push a graph is via a {:push, graph} option when you return from any scene callback.
157137

158-
`push_graph/1` returns the original graph passed in to it. This is so you can
159-
hang the call off the end of a pipe chain that transforms the graph. This isn't
160-
really necessary, but I like it as it visually indicates that the graph was
161-
transformed and pushed as a logical unit.
138+
```elixir
139+
def init(_,_) do
140+
{:ok, :whatever_state_you_want, push: @graph}
141+
end
162142

163-
graph
164-
|> Graph.modify(:background, &update_opts(&1, fill: clear_color) )
165-
|> push_graph()
143+
def handle_info(:some_msg, your_state) do
144+
graph = Graph.modify( @graph, :some_id, &text(&1, "modified text") )
145+
{:noreply, your_state, push: graph}
146+
end
147+
```
166148

167-
More on `Graph.modify` in the [input](#user-input) and [events](#events)
168-
sections below. Also see the [Graph Overview](overview_graph.html) page.
149+
Almost all (except terminate...) of the callbacks accept a `{:push, graph}` option. Replacing the call of `push_graph(graph)` within a callback function depends slightly on the context in which it is used.
169150

170-
As you can guess, `push_graph/1` is relatively heavy since it scans every node
171-
in your graph every time you call it. You should only call it once per input or
172-
event that you handle.
151+
* in `init/2`:
152+
* `{:ok, state, [push: graph]}`
153+
* in `filter_event/3`:
154+
* `{:halt, state, [push: graph]}`
155+
* `{:cont, event, state, [push: graph]}`
156+
* in `handle_cast/2`:
157+
* `{:noreply, state, [push: graph]}`
158+
* in `handle_info/2`:
159+
* `{:noreply, state, [push: graph]}`
160+
* in `handle_call/3`:
161+
* `{:reply, reply, state, [push: graph]}`
162+
* `{:noreply, state, [push: graph]}`
163+
* in `handle_continue/3`:
164+
* `{:noreply, state, [push: graph]}`
173165

174-
> A best practice is to make multiple modifications to a graph and then call
175-
> `push_graph/1` at the end.
166+
See the [documentation for scene callbacks](Scenic.Scene.html#callbacks) for more information.
176167

177-
graph = graph
178-
|> Graph.modify(:text, &text(&1, "I've been modified") )
179-
|> Graph.modify(:a_box, &rect(&1, {100, 200}) )
180-
|> Graph.modify(:a_circle, &update_opts(&1, fill: :blue) )
181-
|> push_graph()
182168

183169
## User Input
184170

@@ -228,14 +214,13 @@ This will be included in the error that gets raised.
228214
graph = @graph
229215
|> Graph.modify(:_root_, &update_opts(&1, styles: opts[:styles]) )
230216
|> Graph.modify(:text, &text(&1, text) )
231-
|> push_graph()
232217

233218
state = %{
234219
graph: graph,
235220
text: text
236221
}
237222

238-
{:ok, state}
223+
{:ok, state, push: graph}
239224
end
240225

241226
...

lib/scenic/scene.ex

Lines changed: 1 addition & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -245,8 +245,7 @@ defmodule Scenic.Scene do
245245
use Scenic.Component, has_children: false
246246
247247
Setting `has_children` to `false` this will do two things. First, it won't create
248-
a dynamic supervisor for this scene, which saves some resources. Second,
249-
`push_graph/1` goes through a fast pass that doesn't scan the graph for dynamic children.
248+
a dynamic supervisor for this scene, which saves some resources.
250249
251250
For example, the Button component sets `has_children` to `false`.
252251
"""

0 commit comments

Comments
 (0)