Skip to content

Commit aa51267

Browse files
authored
Merge pull request #297 from GPrimola/new-component-events
New component events
2 parents 0b6b4b2 + 1b98178 commit aa51267

File tree

6 files changed

+93
-12
lines changed

6 files changed

+93
-12
lines changed

lib/scenic/component/button.ex

Lines changed: 13 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -22,7 +22,16 @@ defmodule Scenic.Component.Button do
2222
If a button press is successful, it sends an event message to the host scene
2323
in the form of:
2424
25-
{:click, id}
25+
`{:click, id}`
26+
27+
This event is only sent after the button is released. There're also, though,
28+
two other events that you can receive:
29+
30+
`{:btn_pressed, id}`
31+
32+
and
33+
34+
`{:btn_released, id}`
2635
2736
These messages can be received and handled in your scene via
2837
`c:Scenic.Scene.handle_event/3`. For example:
@@ -296,9 +305,10 @@ defmodule Scenic.Component.Button do
296305
def handle_input(
297306
{:cursor_button, {:btn_left, 1, _, _}},
298307
:btn,
299-
%Scene{assigns: %{graph: graph, theme: theme}} = scene
308+
%Scene{assigns: %{id: id, graph: graph, theme: theme}} = scene
300309
) do
301310
:ok = capture_input(scene, :cursor_button)
311+
:ok = send_parent_event(scene, {:btn_pressed, id})
302312

303313
graph = update_color(graph, theme, true)
304314

@@ -340,6 +350,7 @@ defmodule Scenic.Component.Button do
340350
) do
341351
:ok = release_input(scene)
342352
:ok = send_parent_event(scene, {:click, id})
353+
:ok = send_parent_event(scene, {:btn_released, id})
343354

344355
graph = update_color(graph, theme, false)
345356

lib/scenic/component/input/dropdown.ex

Lines changed: 15 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -31,6 +31,12 @@ defmodule Scenic.Component.Input.Dropdown do
3131
3232
`{:value_changed, id, selected_item_id}`
3333
34+
It also send the following events:
35+
36+
`{:dropdown_opened, id}` - sent when the dropdown opens
37+
`{:dropdown_closed, id}` - sent when the dropdown closes
38+
`{:dropdown_item_hover, id, item_id}` - sent when and item is hovered
39+
3440
## Options
3541
3642
Dropdowns honor the following list of options.
@@ -431,6 +437,7 @@ defmodule Scenic.Component.Input.Dropdown do
431437
%Scene{
432438
assigns: %{
433439
down: true,
440+
id: component_id,
434441
items: items,
435442
graph: graph,
436443
selected_id: selected_id,
@@ -441,6 +448,8 @@ defmodule Scenic.Component.Input.Dropdown do
441448
# set the appropriate hilighting for each of the items
442449
graph = update_highlighting(graph, items, selected_id, id, theme)
443450

451+
:ok = send_parent_event(scene, {:dropdown_item_hover, component_id, id})
452+
444453
scene =
445454
scene
446455
|> assign(hover_id: nil, graph: graph)
@@ -453,11 +462,13 @@ defmodule Scenic.Component.Input.Dropdown do
453462
def handle_input(
454463
{:cursor_button, {:btn_left, 1, _, _}},
455464
@button_id,
456-
%Scene{assigns: %{down: false, graph: graph, rotate_caret: rotate_caret}} = scene
465+
%Scene{assigns: %{down: false, graph: graph, id: id, rotate_caret: rotate_caret}} = scene
457466
) do
458467
# capture input
459468
:ok = capture_input(scene, [:cursor_button, :cursor_pos])
460469

470+
:ok = send_parent_event(scene, {:dropdown_opened, id})
471+
461472
# drop the menu
462473
graph =
463474
graph
@@ -483,6 +494,7 @@ defmodule Scenic.Component.Input.Dropdown do
483494
theme: theme,
484495
items: items,
485496
graph: graph,
497+
id: id,
486498
selected_id: selected_id
487499
}
488500
} = scene
@@ -496,6 +508,8 @@ defmodule Scenic.Component.Input.Dropdown do
496508

497509
:ok = release_input(scene)
498510

511+
:ok = send_parent_event(scene, {:dropdown_closed, id})
512+
499513
scene =
500514
scene
501515
|> assign(down: false, hover_id: nil, graph: graph)

lib/scenic/component/input/text_field.ex

Lines changed: 9 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -20,6 +20,11 @@ defmodule Scenic.Component.Input.TextField do
2020
2121
`{:value_changed, id, value}`
2222
23+
It also sends other two events when focus is gained or lost, respectively:
24+
25+
`{:focus, id}`
26+
`{:blur, id}`
27+
2328
## Styles
2429
2530
Text fields honor the following styles
@@ -254,9 +259,10 @@ defmodule Scenic.Component.Input.TextField do
254259
end
255260

256261
# --------------------------------------------------------
257-
defp capture_focus(%{assigns: %{focused: false, graph: graph, theme: theme}} = scene) do
262+
defp capture_focus(%{assigns: %{focused: false, graph: graph, id: id, theme: theme}} = scene) do
258263
# capture the input
259264
capture_input(scene, @input_capture)
265+
:ok = send_parent_event(scene, {:focus, id})
260266

261267
# start animating the caret
262268
cast_children(scene, :start_caret)
@@ -274,9 +280,10 @@ defmodule Scenic.Component.Input.TextField do
274280
end
275281

276282
# --------------------------------------------------------
277-
defp release_focus(%{assigns: %{focused: true, graph: graph, theme: theme}} = scene) do
283+
defp release_focus(%{assigns: %{focused: true, graph: graph, id: id, theme: theme}} = scene) do
278284
# release the input
279285
release_input(scene)
286+
:ok = send_parent_event(scene, {:blur, id})
280287

281288
# stop animating the caret
282289
cast_children(scene, :stop_caret)

test/scenic/component/button_test.exs

Lines changed: 8 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -83,15 +83,22 @@ defmodule Scenic.Component.ButtonTest do
8383
refute_receive(_, 10)
8484
end
8585

86-
test "Press in and release in sends the event", %{vp: vp} do
86+
test "Press in and release in sends 'click' and 'btn_released' events", %{vp: vp} do
8787
Input.send(vp, @press_in)
8888
Input.send(vp, @release_in)
8989
assert_receive({:fwd_event, {:click, :test_btn}}, 200)
90+
assert_receive({:fwd_event, {:btn_released, :test_btn}}, 200)
91+
end
92+
93+
test "Press in sends a btn_pressed event", %{vp: vp} do
94+
Input.send(vp, @press_in)
95+
assert_receive({:fwd_event, {:btn_pressed, :test_btn}}, 200)
9096
end
9197

9298
test "Press in and release out does not send the event", %{vp: vp} do
9399
Input.send(vp, @press_in)
94100
Input.send(vp, @release_out)
101+
assert_receive({:fwd_event, {:btn_pressed, :test_btn}}, 10)
95102
refute_receive(_, 10)
96103
end
97104

test/scenic/component/input/dropdown_test.exs

Lines changed: 35 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -29,6 +29,9 @@ defmodule Scenic.Component.Input.DropdownTest do
2929
@press_out {:cursor_button, {:btn_left, 1, [], {1000, 1000}}}
3030
@release_out {:cursor_button, {:btn_left, 1, [], {1000, 1000}}}
3131

32+
@hover_a {:cursor_pos, {20, 50}}
33+
@hover_b {:cursor_pos, {20, 80}}
34+
3235
defmodule TestScene do
3336
use Scenic.Scene
3437
import Scenic.Components
@@ -90,13 +93,18 @@ defmodule Scenic.Component.Input.DropdownTest do
9093
assert_receive({:fwd_event, {:value_changed, :dropdown, 1}}, 100)
9194
end
9295

93-
test "press_in/release_in/press_in does nothing", %{vp: vp, comp_pid: comp_pid} do
96+
test "press_in/release_in/press_in will fire open and close events", %{
97+
vp: vp,
98+
comp_pid: comp_pid
99+
} do
94100
Input.send(vp, @press_in)
95101
force_sync(vp.pid, comp_pid)
96102
Input.send(vp, @release_in)
97103
force_sync(vp.pid, comp_pid)
98-
Input.send(vp, @press_in)
104+
assert_receive({:fwd_event, {:dropdown_opened, :dropdown}}, 100)
99105

106+
Input.send(vp, @press_in)
107+
assert_receive({:fwd_event, {:dropdown_closed, :dropdown}}, 100)
100108
refute_receive(_, 10)
101109
end
102110

@@ -119,6 +127,8 @@ defmodule Scenic.Component.Input.DropdownTest do
119127
force_sync(vp.pid, comp_pid)
120128
Input.send(vp, @release_in)
121129
force_sync(vp.pid, comp_pid)
130+
assert_receive({:fwd_event, {:dropdown_opened, :dropdown}}, 100)
131+
122132
Input.send(vp, @press_a)
123133
assert_receive({:fwd_event, {:value_changed, :dropdown, 1}}, 100)
124134
end
@@ -128,15 +138,18 @@ defmodule Scenic.Component.Input.DropdownTest do
128138
force_sync(vp.pid, comp_pid)
129139
Input.send(vp, @release_in)
130140
force_sync(vp.pid, comp_pid)
141+
assert_receive({:fwd_event, {:dropdown_opened, :dropdown}}, 100)
142+
131143
Input.send(vp, @press_b)
132144
assert_receive({:fwd_event, {:value_changed, :dropdown, 2}}, 100)
133145
end
134146

135-
test "Press in and release out does not send the event", %{vp: vp, comp_pid: comp_pid} do
147+
test "Press in and release out send only opened event", %{vp: vp, comp_pid: comp_pid} do
136148
Input.send(vp, @press_in)
137149
force_sync(vp.pid, comp_pid)
138-
Input.send(vp, @release_out)
150+
assert_receive({:fwd_event, {:dropdown_opened, :dropdown}}, 100)
139151

152+
Input.send(vp, @release_out)
140153
refute_receive(_, 10)
141154
end
142155

@@ -148,6 +161,24 @@ defmodule Scenic.Component.Input.DropdownTest do
148161
refute_receive(_, 10)
149162
end
150163

164+
test "", %{vp: vp, comp_pid: comp_pid} do
165+
Input.send(vp, @press_in)
166+
force_sync(vp.pid, comp_pid)
167+
Input.send(vp, @release_in)
168+
force_sync(vp.pid, comp_pid)
169+
assert_receive({:fwd_event, {:dropdown_opened, :dropdown}}, 100)
170+
171+
Input.send(vp, @hover_a)
172+
force_sync(vp.pid, comp_pid)
173+
assert_receive({:fwd_event, {:dropdown_item_hover, :dropdown, 1}}, 100)
174+
175+
Input.send(vp, @hover_b)
176+
force_sync(vp.pid, comp_pid)
177+
assert_receive({:fwd_event, {:dropdown_item_hover, :dropdown, 2}}, 100)
178+
179+
refute_receive(_, 10)
180+
end
181+
151182
test "implements get/put", %{scene: scene} do
152183
assert Scene.get_child(scene, :dropdown) == [2]
153184
assert Scene.put_child(scene, :dropdown, 1) == :ok

test/scenic/component/input/text_field_test.exs

Lines changed: 13 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -94,25 +94,29 @@ defmodule Scenic.Component.Input.TextFieldTest do
9494
:_pong_ = GenServer.call(vp_pid, :_ping_)
9595
end
9696

97-
test "press_in captures and starts editing", %{vp: vp, pid: pid} do
97+
test "press_in captures, starts editing and fire focus event", %{vp: vp, pid: pid} do
9898
assert Input.fetch_captures!(vp) == {:ok, []}
9999
Input.send(vp, @press_in)
100100
force_sync(vp.pid, pid)
101+
101102
assert Input.fetch_captures!(vp) ~> {:ok, sorted_list([:codepoint, :cursor_button, :key])}
103+
assert_receive({:fwd_event, {:focus, :text_field}}, 200)
102104

103105
Input.send(vp, @cp_k)
104106
assert_receive({:fwd_event, {:value_changed, :text_field, "kInitial value"}}, 200)
105107
end
106108

107-
test "press_out releases and ends editing", %{vp: vp, pid: pid} do
109+
test "press_out releases, ends editing and fire blur event", %{vp: vp, pid: pid} do
108110
Input.send(vp, @press_in)
109111
force_sync(vp.pid, pid)
110112

111113
assert Input.fetch_captures!(vp) ~> {:ok, sorted_list([:codepoint, :cursor_button, :key])}
114+
assert_receive({:fwd_event, {:focus, :text_field}}, 200)
112115

113116
Input.send(vp, @press_out)
114117
force_sync(vp.pid, pid)
115118
assert Input.fetch_captures!(vp) == {:ok, []}
119+
assert_receive({:fwd_event, {:blur, :text_field}}, 200)
116120

117121
Input.send(vp, @cp_k)
118122
refute_receive(_, 10)
@@ -121,6 +125,7 @@ defmodule Scenic.Component.Input.TextFieldTest do
121125
test "pressing in the field moves the cursor to the nearst character gap", %{vp: vp, pid: pid} do
122126
Input.send(vp, @press_in)
123127
force_sync(vp.pid, pid)
128+
assert_receive({:fwd_event, {:focus, :text_field}}, 200)
124129

125130
Input.send(vp, @cp_k)
126131
assert_receive({:fwd_event, {:value_changed, :text_field, "kInitial value"}}, 200)
@@ -201,6 +206,7 @@ defmodule Scenic.Component.Input.TextFieldTest do
201206
test "backspace does nothing at the start of the string", %{vp: vp, pid: pid} do
202207
Input.send(vp, @press_in)
203208
force_sync(vp.pid, pid)
209+
assert_receive({:fwd_event, {:focus, :text_field}}, 200)
204210

205211
Input.send(vp, @key_backspace)
206212
refute_receive(_, 10)
@@ -217,6 +223,7 @@ defmodule Scenic.Component.Input.TextFieldTest do
217223
test "delete does nothing at the end of the field", %{vp: vp, pid: pid} do
218224
Input.send(vp, @press_in)
219225
force_sync(vp.pid, pid)
226+
assert_receive({:fwd_event, {:focus, :text_field}}, 200)
220227

221228
Input.send(vp, @key_end)
222229
Input.send(vp, @key_delete)
@@ -229,6 +236,7 @@ defmodule Scenic.Component.Input.TextFieldTest do
229236

230237
Input.send(vp, {:cursor_button, {:btn_left, 1, [], {20, 60}}})
231238
force_sync(vp.pid, pid)
239+
assert_receive({:fwd_event, {:focus, :number_field}}, 200)
232240

233241
Input.send(vp, {:codepoint, {"a", []}})
234242
refute_receive(_, 10)
@@ -248,6 +256,7 @@ defmodule Scenic.Component.Input.TextFieldTest do
248256

249257
Input.send(vp, {:cursor_button, {:btn_left, 1, [], {14, 86}}})
250258
force_sync(vp.pid, pid)
259+
assert_receive({:fwd_event, {:focus, :integer_field}}, 200)
251260

252261
Input.send(vp, {:codepoint, {"a", []}})
253262
refute_receive(_, 10)
@@ -267,6 +276,7 @@ defmodule Scenic.Component.Input.TextFieldTest do
267276

268277
Input.send(vp, {:cursor_button, {:btn_left, 1, [], {14, 121}}})
269278
force_sync(vp.pid, pid)
279+
assert_receive({:fwd_event, {:focus, :abcdefg_field}}, 200)
270280

271281
Input.send(vp, {:codepoint, {"a", []}})
272282
assert_receive({:fwd_event, {:value_changed, :abcdefg_field, "a"}}, 200)
@@ -284,6 +294,7 @@ defmodule Scenic.Component.Input.TextFieldTest do
284294

285295
Input.send(vp, {:cursor_button, {:btn_left, 1, [], {14, 171}}})
286296
force_sync(vp.pid, pid)
297+
assert_receive({:fwd_event, {:focus, :fn_field}}, 200)
287298

288299
Input.send(vp, {:codepoint, {"a", []}})
289300
refute_receive(_, 10)

0 commit comments

Comments
 (0)