Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

New component events #297

Merged
merged 5 commits into from
May 5, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
15 changes: 13 additions & 2 deletions lib/scenic/component/button.ex
Original file line number Diff line number Diff line change
Expand Up @@ -22,7 +22,16 @@ defmodule Scenic.Component.Button do
If a button press is successful, it sends an event message to the host scene
in the form of:

{:click, id}
`{:click, id}`

This event is only sent after the button is released. There're also, though,
two other events that you can receive:

`{:btn_pressed, id}`

and

`{:btn_released, id}`

These messages can be received and handled in your scene via
`c:Scenic.Scene.handle_event/3`. For example:
Expand Down Expand Up @@ -296,9 +305,10 @@ defmodule Scenic.Component.Button do
def handle_input(
{:cursor_button, {:btn_left, 1, _, _}},
:btn,
%Scene{assigns: %{graph: graph, theme: theme}} = scene
%Scene{assigns: %{id: id, graph: graph, theme: theme}} = scene
) do
:ok = capture_input(scene, :cursor_button)
:ok = send_parent_event(scene, {:btn_pressed, id})

graph = update_color(graph, theme, true)

Expand Down Expand Up @@ -340,6 +350,7 @@ defmodule Scenic.Component.Button do
) do
:ok = release_input(scene)
:ok = send_parent_event(scene, {:click, id})
:ok = send_parent_event(scene, {:btn_released, id})

graph = update_color(graph, theme, false)

Expand Down
16 changes: 15 additions & 1 deletion lib/scenic/component/input/dropdown.ex
Original file line number Diff line number Diff line change
Expand Up @@ -31,6 +31,12 @@ defmodule Scenic.Component.Input.Dropdown do

`{:value_changed, id, selected_item_id}`

It also send the following events:

`{:dropdown_opened, id}` - sent when the dropdown opens
`{:dropdown_closed, id}` - sent when the dropdown closes
`{:dropdown_item_hover, id, item_id}` - sent when and item is hovered

## Options

Dropdowns honor the following list of options.
Expand Down Expand Up @@ -431,6 +437,7 @@ defmodule Scenic.Component.Input.Dropdown do
%Scene{
assigns: %{
down: true,
id: component_id,
items: items,
graph: graph,
selected_id: selected_id,
Expand All @@ -441,6 +448,8 @@ defmodule Scenic.Component.Input.Dropdown do
# set the appropriate hilighting for each of the items
graph = update_highlighting(graph, items, selected_id, id, theme)

:ok = send_parent_event(scene, {:dropdown_item_hover, component_id, id})

scene =
scene
|> assign(hover_id: nil, graph: graph)
Expand All @@ -453,11 +462,13 @@ defmodule Scenic.Component.Input.Dropdown do
def handle_input(
{:cursor_button, {:btn_left, 1, _, _}},
@button_id,
%Scene{assigns: %{down: false, graph: graph, rotate_caret: rotate_caret}} = scene
%Scene{assigns: %{down: false, graph: graph, id: id, rotate_caret: rotate_caret}} = scene
) do
# capture input
:ok = capture_input(scene, [:cursor_button, :cursor_pos])

:ok = send_parent_event(scene, {:dropdown_opened, id})

# drop the menu
graph =
graph
Expand All @@ -483,6 +494,7 @@ defmodule Scenic.Component.Input.Dropdown do
theme: theme,
items: items,
graph: graph,
id: id,
selected_id: selected_id
}
} = scene
Expand All @@ -496,6 +508,8 @@ defmodule Scenic.Component.Input.Dropdown do

:ok = release_input(scene)

:ok = send_parent_event(scene, {:dropdown_closed, id})

scene =
scene
|> assign(down: false, hover_id: nil, graph: graph)
Expand Down
11 changes: 9 additions & 2 deletions lib/scenic/component/input/text_field.ex
Original file line number Diff line number Diff line change
Expand Up @@ -20,6 +20,11 @@ defmodule Scenic.Component.Input.TextField do

`{:value_changed, id, value}`

It also sends other two events when focus is gained or lost, respectively:

`{:focus, id}`
`{:blur, id}`

## Styles

Text fields honor the following styles
Expand Down Expand Up @@ -254,9 +259,10 @@ defmodule Scenic.Component.Input.TextField do
end

# --------------------------------------------------------
defp capture_focus(%{assigns: %{focused: false, graph: graph, theme: theme}} = scene) do
defp capture_focus(%{assigns: %{focused: false, graph: graph, id: id, theme: theme}} = scene) do
# capture the input
capture_input(scene, @input_capture)
:ok = send_parent_event(scene, {:focus, id})

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

# --------------------------------------------------------
defp release_focus(%{assigns: %{focused: true, graph: graph, theme: theme}} = scene) do
defp release_focus(%{assigns: %{focused: true, graph: graph, id: id, theme: theme}} = scene) do
# release the input
release_input(scene)
:ok = send_parent_event(scene, {:blur, id})

# stop animating the caret
cast_children(scene, :stop_caret)
Expand Down
9 changes: 8 additions & 1 deletion test/scenic/component/button_test.exs
Original file line number Diff line number Diff line change
Expand Up @@ -83,15 +83,22 @@ defmodule Scenic.Component.ButtonTest do
refute_receive(_, 10)
end

test "Press in and release in sends the event", %{vp: vp} do
test "Press in and release in sends 'click' and 'btn_released' events", %{vp: vp} do
Input.send(vp, @press_in)
Input.send(vp, @release_in)
assert_receive({:fwd_event, {:click, :test_btn}}, 200)
assert_receive({:fwd_event, {:btn_released, :test_btn}}, 200)
end

test "Press in sends a btn_pressed event", %{vp: vp} do
Input.send(vp, @press_in)
assert_receive({:fwd_event, {:btn_pressed, :test_btn}}, 200)
end

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

Expand Down
39 changes: 35 additions & 4 deletions test/scenic/component/input/dropdown_test.exs
Original file line number Diff line number Diff line change
Expand Up @@ -29,6 +29,9 @@ defmodule Scenic.Component.Input.DropdownTest do
@press_out {:cursor_button, {:btn_left, 1, [], {1000, 1000}}}
@release_out {:cursor_button, {:btn_left, 1, [], {1000, 1000}}}

@hover_a {:cursor_pos, {20, 50}}
@hover_b {:cursor_pos, {20, 80}}

defmodule TestScene do
use Scenic.Scene
import Scenic.Components
Expand Down Expand Up @@ -90,13 +93,18 @@ defmodule Scenic.Component.Input.DropdownTest do
assert_receive({:fwd_event, {:value_changed, :dropdown, 1}}, 100)
end

test "press_in/release_in/press_in does nothing", %{vp: vp, comp_pid: comp_pid} do
test "press_in/release_in/press_in will fire open and close events", %{
vp: vp,
comp_pid: comp_pid
} do
Input.send(vp, @press_in)
force_sync(vp.pid, comp_pid)
Input.send(vp, @release_in)
force_sync(vp.pid, comp_pid)
Input.send(vp, @press_in)
assert_receive({:fwd_event, {:dropdown_opened, :dropdown}}, 100)

Input.send(vp, @press_in)
assert_receive({:fwd_event, {:dropdown_closed, :dropdown}}, 100)
refute_receive(_, 10)
end

Expand All @@ -119,6 +127,8 @@ defmodule Scenic.Component.Input.DropdownTest do
force_sync(vp.pid, comp_pid)
Input.send(vp, @release_in)
force_sync(vp.pid, comp_pid)
assert_receive({:fwd_event, {:dropdown_opened, :dropdown}}, 100)

Input.send(vp, @press_a)
assert_receive({:fwd_event, {:value_changed, :dropdown, 1}}, 100)
end
Expand All @@ -128,15 +138,18 @@ defmodule Scenic.Component.Input.DropdownTest do
force_sync(vp.pid, comp_pid)
Input.send(vp, @release_in)
force_sync(vp.pid, comp_pid)
assert_receive({:fwd_event, {:dropdown_opened, :dropdown}}, 100)

Input.send(vp, @press_b)
assert_receive({:fwd_event, {:value_changed, :dropdown, 2}}, 100)
end

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

Input.send(vp, @release_out)
refute_receive(_, 10)
end

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

test "", %{vp: vp, comp_pid: comp_pid} do
Input.send(vp, @press_in)
force_sync(vp.pid, comp_pid)
Input.send(vp, @release_in)
force_sync(vp.pid, comp_pid)
assert_receive({:fwd_event, {:dropdown_opened, :dropdown}}, 100)

Input.send(vp, @hover_a)
force_sync(vp.pid, comp_pid)
assert_receive({:fwd_event, {:dropdown_item_hover, :dropdown, 1}}, 100)

Input.send(vp, @hover_b)
force_sync(vp.pid, comp_pid)
assert_receive({:fwd_event, {:dropdown_item_hover, :dropdown, 2}}, 100)

refute_receive(_, 10)
end

test "implements get/put", %{scene: scene} do
assert Scene.get_child(scene, :dropdown) == [2]
assert Scene.put_child(scene, :dropdown, 1) == :ok
Expand Down
15 changes: 13 additions & 2 deletions test/scenic/component/input/text_field_test.exs
Original file line number Diff line number Diff line change
Expand Up @@ -94,25 +94,29 @@ defmodule Scenic.Component.Input.TextFieldTest do
:_pong_ = GenServer.call(vp_pid, :_ping_)
end

test "press_in captures and starts editing", %{vp: vp, pid: pid} do
test "press_in captures, starts editing and fire focus event", %{vp: vp, pid: pid} do
assert Input.fetch_captures!(vp) == {:ok, []}
Input.send(vp, @press_in)
force_sync(vp.pid, pid)

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

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

test "press_out releases and ends editing", %{vp: vp, pid: pid} do
test "press_out releases, ends editing and fire blur event", %{vp: vp, pid: pid} do
Input.send(vp, @press_in)
force_sync(vp.pid, pid)

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

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

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

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

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

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

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

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

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

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

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

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

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

Input.send(vp, {:codepoint, {"a", []}})
refute_receive(_, 10)
Expand Down
Loading