Skip to content

Commit cb03b75

Browse files
committed
Add support for transformed_value inside fields
Closes #43
1 parent 8921486 commit cb03b75

3 files changed

Lines changed: 86 additions & 5 deletions

File tree

CHANGELOG.md

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -10,6 +10,7 @@ This file contains the changelog for the PlutoExtras package. It follows the [Ke
1010

1111
### Added
1212
- Added possibility of providing any valid object with a `MIME"text/html"` representation as description of the `@NTBond` macro.
13+
- Added the possibility of simplifying application of `PlutoUI.Experimental.transformed_value` to the fields of an `@NTBond` using the `@tv` decorator (see the example notebook for details).
1314

1415
### Changed
1516
- Changed the hiding behavior of the `Popout` container so that it stays displayed if the mouse is hovering over its contents even if not popped out

src/structbond/macro.jl

Lines changed: 49 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,49 @@
11
# Helper Functions #
2+
##### Functions copied from MacroTools.jl ####
3+
walk(x, inner, outer) = outer(x)
4+
walk(x::Expr, inner, outer) = outer(Expr(x.head, map(inner, x.args)...))
5+
prewalk(f, x) = walk(f(x), x -> prewalk(f, x), identity)
6+
##### End of MacroTools.jl functions ####
7+
8+
# This traverse the expression and eventually replaces all single underscores with the provided alternative value
9+
function replace_single_underscore(ex::Expr, newval::Symbol)
10+
changed = Ref(false)
11+
newex = prewalk(ex) do x
12+
if x === :_
13+
changed[] = true
14+
return newval
15+
else
16+
return x
17+
end
18+
end
19+
return newex, changed[]
20+
end
21+
22+
# This will take an expression and check if it contains single underscores identifiers. If it does, it assumes it has to translate this into an anonymous function with a single argument that will go in place of the underscore
23+
make_function(x) = x
24+
function make_function(ex::Expr)
25+
Meta.isexpr(ex, :(->)) && return ex
26+
newsym = gensym()
27+
newex, changed = replace_single_underscore(ex, newsym)
28+
if changed
29+
return Expr(:(->), newsym, Expr(:block, newex))
30+
else
31+
return ex
32+
end
33+
end
34+
35+
# This function will try parsing the fieldbond expression to eventually deal with `@tv`
36+
process_fieldbond_expression(x) = x
37+
function process_fieldbond_expression(ex::Expr)
38+
Meta.isexpr(ex, :macrocall) || return ex
39+
args = filter(x -> !(x isa LineNumberNode), ex.args)
40+
macro_name = args[1]
41+
macro_name == Symbol("@tv") || return ex
42+
length(args) == 3 || throw(ArgumentError("The @tv decorator has to be called with two arguments after it.\nThe first must be the transforming function and the second the widget"))
43+
_, func, widget = args
44+
return :($(transformed_value)($(make_function(func)), $widget))
45+
end
46+
247
# Generic function for the convenience macros to add methods for the field functions
348
function _add_generic_field(s, block, fnames)
449
if !Meta.isexpr(block, [:block, :let])
@@ -18,6 +63,9 @@ function _add_generic_field(s, block, fnames)
1863
# If the value is not already a tuple or vect or expressions, we wrap it in a tuple
1964
values = Meta.isexpr(value, [:vect, :tuple]) ? value.args : (value,)
2065
for (fname,val) in zip(reverse(fnames), reverse(values))
66+
if fname == :fieldbond
67+
val = process_fieldbond_expression(val)
68+
end
2169
# Push the expression to overload the method
2270
push!(out.args, esc(:($Mod.$fname(::Type{$s}, ::Val{$symbol}) = $val)))
2371
end
@@ -332,7 +380,7 @@ end
332380

333381
function _NTBond(desc, block, tv)
334382
expr = _NTBond(desc, block)
335-
return :($(transformed_value)($(esc(tv)), $expr))
383+
return :($(transformed_value)($(esc(make_function(tv))), $expr))
336384
end
337385

338386

test/notebooks/structbondmodule.jl

Lines changed: 36 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -151,11 +151,35 @@ md"""
151151
@bind transformed_value_example @NTBond md"Transformed!" begin
152152
a = Slider(1:10)
153153
b = Slider(1:10)
154-
end nt -> nt.a + nt.b
154+
end _.a + _.b # This function is replaced within the macro to be `nt -> nt.a + nt.b`, which could have also been provided as anonymous function directly
155155

156156
# ╔═╡ 1d5573ee-872e-4dfb-a785-1ac9e836ad98
157157
transformed_value_example
158158

159+
# ╔═╡ 5bb22f07-fbea-44ab-9783-9ca16e0da11e
160+
md"""
161+
### Transform only a few fields
162+
"""
163+
164+
# ╔═╡ 134d6033-2f08-4f49-9829-6a023b368a70
165+
md"""
166+
A similar approach can also be achieved on a field by field basis by using the `@tv` decorator (which is not a macro per-se but is directly processed during the macro expansion of `@NTBond`).
167+
168+
`@tv` is a shorthand for transformed_value and it expects two arguments after it, the first being the function to process the value returned by the widget, and the second being the widget itself.
169+
170+
This can be used to be a bit less verbose when one is interested in only transforming one of many fields as below:
171+
"""
172+
173+
# ╔═╡ 02e58012-5af6-4b91-a9a4-09c9dc038f11
174+
@bind transform_single_field @NTBond "Field Level Transformed Value" begin
175+
a = ("Angle [°]", @tv deg2rad Slider(0:90; default = 60, show_value=true)) # This transforms the angle into radians
176+
b = Slider(1:10)
177+
c = Slider(1:10)
178+
end
179+
180+
# ╔═╡ e102613f-244c-4b49-9837-535bf047a14a
181+
transform_single_field
182+
159183
# ╔═╡ 8eb18c7f-bb4d-4cdc-9e30-d561f9099800
160184
md"""
161185
## Markdown math in description
@@ -166,11 +190,14 @@ md"""
166190
The description of an `@NTBond` can now be provided as anything that has a valid `MIME"text/html"` representation. This includes markdown with math inside!
167191
"""
168192

193+
# ╔═╡ bb04ac12-20a0-467a-af1a-c298301e4838
194+
markdown_function = nt -> atan(nt.x, nt.y) + 3
195+
169196
# ╔═╡ 36faf18c-11c3-4012-a996-55c7cdae71a8
170197
math_ntbond = @bind atanval @NTBond md"This is math!: ``\;atan(x,y) + 3``" begin
171-
x = (md"``x``", Slider(1:10))
172-
y = (md"``y``", Slider(1:10))
173-
end nt -> atan(nt.x, nt.y) + 3
198+
x = (md"``x``", Slider(1:10; show_value=true))
199+
y = (md"``y``", Slider(1:10; show_value=true))
200+
end markdown_function # We can also directly use functions in the caller scope to transform the value
174201

175202
# ╔═╡ 27977526-75dc-44c3-9976-c22e8cbd94da
176203
atanval
@@ -570,8 +597,13 @@ version = "17.5.0+2"
570597
# ╟─4cb128aa-7ad8-4d17-bced-36845703e6a8
571598
# ╠═7855826e-ccaa-4c27-a060-f5ceb927bbe8
572599
# ╠═1d5573ee-872e-4dfb-a785-1ac9e836ad98
600+
# ╟─5bb22f07-fbea-44ab-9783-9ca16e0da11e
601+
# ╟─134d6033-2f08-4f49-9829-6a023b368a70
602+
# ╠═02e58012-5af6-4b91-a9a4-09c9dc038f11
603+
# ╠═e102613f-244c-4b49-9837-535bf047a14a
573604
# ╟─8eb18c7f-bb4d-4cdc-9e30-d561f9099800
574605
# ╟─052b8bbd-acca-4c00-a5de-0c717ed068e3
606+
# ╠═bb04ac12-20a0-467a-af1a-c298301e4838
575607
# ╠═36faf18c-11c3-4012-a996-55c7cdae71a8
576608
# ╠═27977526-75dc-44c3-9976-c22e8cbd94da
577609
# ╟─da3ae348-083d-4a7d-aabd-d0bc25b3ca17

0 commit comments

Comments
 (0)