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

Add options to Kino.Control.keyboard #312

Merged
merged 4 commits into from
Aug 8, 2023
Merged
Show file tree
Hide file tree
Changes from 1 commit
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
53 changes: 45 additions & 8 deletions lib/kino/control.ex
Original file line number Diff line number Diff line change
Expand Up @@ -16,14 +16,18 @@ defmodule Kino.Control do

button = Kino.Control.button("Hello")

Next, to receive events from the control, a process needs to
subscribe to it and specify pick a name to distinguish the
events.
Next, events need to be received from the control. This can
be done either by subscribing a process to the control with
`subscribe/2` or by creating an event stream using `stream/1`
or `tagged_stream/1` and then registering a callback using
`Kino.listen/1`.

Here, we'll subscribe the current process to events:

Kino.Control.subscribe(button, :hello)

As the user interacts with the button, the subscribed process
receives corresponding events.
As the user clicks the button, the subscribed process
receives events:

IEx.Helpers.flush()
#=> {:hello, %{origin: "client1"}}
Expand All @@ -38,6 +42,9 @@ defmodule Kino.Control do

@type event_source :: t() | Kino.Input.t() | interval() | Kino.JS.Live.t()

@type keyboard_opt :: {:default_handlers, :off | :on | :disable_only}
@type keyboard_opts :: [keyboard_opt()]

defp new(attrs) do
ref = Kino.Output.random_ref()
subscription_manager = Kino.SubscriptionManager.cross_node_name()
Expand Down Expand Up @@ -81,6 +88,27 @@ defmodule Kino.Control do
This widget is represented as button that toggles interception
mode, in which the given keyboard events are captured.

> #### Keyboard shortcut {:.info}
>
> As of Livebook v0.10.1, keyboard controls can be toggled by
> focusing the cell and pressing `Ctrl + k` (or `Cmd + k` on
> MacOS).

## Options

Note that these options require Livebook v0.10.1 or later.
zachallaun marked this conversation as resolved.
Show resolved Hide resolved

* `:default_handlers` - controls Livebook's default keyboard
shortcut handlers while the keyboard control is enabled.
Must be one of:

* `:off` (default) - all Livebook keyboard shortcuts are disabled

* `:on` - all Livebook keyboard shortcuts are enabled

* `:disable_only` - Livebook keyboard shortcuts are disabled
zachallaun marked this conversation as resolved.
Show resolved Hide resolved
except for the shortcut to toggle the control.
zachallaun marked this conversation as resolved.
Show resolved Hide resolved

## Event info

In addition to standard properties, all events include additional
Expand Down Expand Up @@ -123,8 +151,10 @@ defmodule Kino.Control do
#=> {:keyboard, %{key: "o", origin: "client1", type: :keyup}}
#=> {:keyboard, %{key: "k", origin: "client1", type: :keyup}}
"""
@spec keyboard(list(:keyup | :keydown | :status)) :: t()
def keyboard(events) when is_list(events) do
@spec keyboard(list(:keyup | :keydown | :status), keyboard_opts()) :: t()
zachallaun marked this conversation as resolved.
Show resolved Hide resolved
def keyboard(events, opts \\ []) when is_list(events) do
opts = Keyword.validate!(opts, default_handlers: :off)

if events == [] do
raise ArgumentError, "expected at least one event, got: []"
end
Expand All @@ -136,7 +166,14 @@ defmodule Kino.Control do
end
end

new(%{type: :keyboard, events: events})
unless opts[:default_handlers] in [:off, :on, :disable_only] do
raise ArgumentError,
"when passed, :default_handlers must be one of :off, :on or :disable_only, got: #{inspect(opts[:default_handlers])}"
end

opts
|> Enum.into(%{type: :keyboard, events: events})
|> new()
zachallaun marked this conversation as resolved.
Show resolved Hide resolved
end

@doc """
Expand Down
8 changes: 8 additions & 0 deletions test/kino/control_test.exs
Original file line number Diff line number Diff line change
Expand Up @@ -15,6 +15,14 @@ defmodule Kino.ControlTest do
Kino.Control.keyboard([:keyword])
end
end

test "raises an error when invalid options are passed" do
assert_raise ArgumentError,
"when passed, :default_handlers must be one of :off, :on or :disable_only, got: :foo",
fn ->
Kino.Control.keyboard([:keydown], default_handlers: :foo)
end
end
end

describe "form/1" do
Expand Down
Loading