Skip to content

Commit 5b19c51

Browse files
Add custom_event/2 and webview reload Platform APIs
Provide Desktop.Platform.System.custom_event/2 for mobile bridge custom events (replacing Hex Bridge GenServer calls) and Desktop.Window.reload/1 plus Content.reload/1 as the backend-safe replacement for :wxWebView.reload/1. Co-authored-by: Cursor <cursoragent@cursor.com>
1 parent 9176365 commit 5b19c51

15 files changed

Lines changed: 169 additions & 1 deletion

File tree

CHANGELOG.md

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -9,6 +9,8 @@
99
- Menu adapters: `Desktop.Menu.Adapter.Json` and `Desktop.Menu.Adapter.Browser`
1010
- Public `Desktop.*` APIs unchanged (`Desktop.Window`, `Desktop.Env`, `Desktop.Menu`, etc.)
1111
- `Desktop.Platform.System.os_description/0` — backend-safe replacement for `:wx_misc.getOsDescription/0` (Wx, Json bridge, Browser)
12+
- `Desktop.Platform.System.custom_event/2` — mobile bridge custom events (replaces Hex `Bridge` `[:custom_event, …]` calls)
13+
- `Desktop.Platform.Content.reload/1` and `Desktop.Window.reload/1` — backend-safe webview reload (replaces `:wxWebView.reload/1`)
1214
- Test suite: `mix test.fast`, `xvfb-run mix test.wx`, `mix test.guard` — see `docs/TEST_PLAN.md`
1315
- Compile without OTP `:wx`: conditional `erl_src_paths` and `Desktop.Wx` fallbacks (no `wx.hrl` required)
1416

guides/faq.md

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -50,6 +50,8 @@ Desktop.Platform.backend() # e.g. Desktop.Backend.Wx
5050
Desktop.Platform.capabilities() # %{window: true, content: :webview, ...}
5151
Desktop.Platform.System.locale()
5252
Desktop.Platform.System.os_description() # replaces :wx_misc.getOsDescription/0
53+
Desktop.Platform.System.custom_event(:share, [path]) # mobile bridge custom events
54+
Desktop.Window.reload(pid) # replaces :wxWebView.reload/1
5355
```
5456

5557
| Backend | `window` | `content` | `menu` |
@@ -64,6 +66,14 @@ On Linux with DBus SNI available, Wx may use `:dbus` for the taskbar menu instea
6466

6567
On mobile targets, `desktop` uses `Desktop.Backend.Json` instead of OTP `:wx`. The Elixir side speaks the legacy JSON protocol over TCP to your native host app. Set `BRIDGE_PORT` to the port your host app listens on. Transport is built in as `Desktop.Bridge.Transport` — the separate `bridge` hex package is no longer required.
6668

69+
App-level native events (share, save, restart, etc.) use:
70+
71+
```elixir
72+
Desktop.Platform.System.custom_event(:share, [path, label])
73+
```
74+
75+
This sends `[:custom_event, event, args]` over the same wire format the Hex `Bridge` GenServer used previously.
76+
6777
Override explicitly if needed:
6878

6979
```elixir

lib/desktop/backend/browser.ex

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -48,6 +48,9 @@ defmodule Desktop.Backend.Browser do
4848
@impl true
4949
def os_description, do: nil
5050

51+
@impl true
52+
def custom_event(_event, _args), do: :ok
53+
5154
@impl true
5255
def activate_event_active?(_event), do: true
5356

@@ -121,6 +124,9 @@ defmodule Desktop.Backend.Browser do
121124
@impl true
122125
def rebuild(_frame, _url), do: nil
123126

127+
@impl true
128+
def reload(_content), do: :ok
129+
124130
@impl true
125131
def put_webview_backend(name) do
126132
Desktop.Env.put(:webview_backend, name)

lib/desktop/backend/json.ex

Lines changed: 17 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -82,6 +82,15 @@ defmodule Desktop.Backend.Json do
8282
Protocol.call(:wx_misc, :getOsDescription, [])
8383
end
8484

85+
@impl true
86+
def custom_event(event, args) do
87+
if Process.whereis(Transport) do
88+
Protocol.call(:custom_event, event, args)
89+
end
90+
91+
:ok
92+
end
93+
8594
@impl true
8695
def activate_event_active?(_event), do: true
8796

@@ -218,6 +227,14 @@ defmodule Desktop.Backend.Json do
218227
:ok
219228
end
220229

230+
@impl true
231+
def reload(nil), do: :ok
232+
233+
def reload(webview) do
234+
Protocol.call(:wxWebView, :reload, [webview])
235+
:ok
236+
end
237+
221238
@impl true
222239
def current_url(nil, last_url), do: last_url
223240

lib/desktop/backend/wx.ex

Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -80,6 +80,9 @@ defmodule Desktop.Backend.Wx do
8080
Null.wx_call(:wx_misc, :getOsDescription, [])
8181
end
8282

83+
@impl true
84+
def custom_event(_event, _args), do: :ok
85+
8386
@impl true
8487
def activate_event_active?(event) do
8588
if function_exported?(:wxActivateEvent, :getActive, 1) do
@@ -267,6 +270,14 @@ defmodule Desktop.Backend.Wx do
267270
:ok
268271
end
269272

273+
@impl true
274+
def reload(nil), do: :ok
275+
276+
def reload(webview) do
277+
Null.wx_call(:wxWebView, :reload, [webview])
278+
:ok
279+
end
280+
270281
@impl true
271282
def current_url(nil, last_url), do: last_url
272283

lib/desktop/bridge/mock.ex

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -15,6 +15,8 @@ defmodule Desktop.Bridge.Mock do
1515
def handle_method([:wxLocale | _]), do: ~c"en"
1616
def handle_method([:wx_misc, :launchDefaultBrowser | _]), do: :ok
1717
def handle_method([:wx_misc, :getOsDescription | _]), do: ~c"Mock OS"
18+
def handle_method([:custom_event | _]), do: :ok
19+
def handle_method([:wxWebView, :reload | _]), do: :ok
1820

1921
def handle_method([type, :new | args]),
2022
do: [id: System.unique_integer([:positive]), type: type, args: args]

lib/desktop/platform/content.ex

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -14,6 +14,7 @@ defmodule Desktop.Platform.Content do
1414
only_open :: boolean()
1515
) :: :ok
1616
@callback rebuild(frame :: term() | nil, last_url :: String.t() | nil) :: term() | nil
17+
@callback reload(content :: term() | nil) :: :ok
1718
@callback put_webview_backend(name :: String.t()) :: :ok
1819

1920
def attach(frame), do: Helpers.with_wx_env(fn -> impl().attach(frame) end)
@@ -30,6 +31,15 @@ defmodule Desktop.Platform.Content do
3031
def rebuild(frame, last_url),
3132
do: Helpers.with_wx_env(fn -> impl().rebuild(frame, last_url) end)
3233

34+
@doc """
35+
Reloads the webview / native content handle.
36+
37+
Replaces direct `:wxWebView.reload/1` so the call works on Wx and Json
38+
(mobile bridge) backends. No-op when content is `nil` or on Browser.
39+
"""
40+
def reload(content),
41+
do: Helpers.with_wx_env(fn -> impl().reload(content) end)
42+
3343
def put_webview_backend(name),
3444
do: Helpers.with_wx_env(fn -> impl().put_webview_backend(name) end)
3545

lib/desktop/platform/system.ex

Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -18,6 +18,7 @@ defmodule Desktop.Platform.System do
1818
@callback wx_available?() :: boolean()
1919
@callback open_external_url(String.t()) :: :ok
2020
@callback os_description() :: String.t() | charlist() | nil
21+
@callback custom_event(event :: atom(), args :: list()) :: :ok
2122
@callback activate_event_active?(event :: term()) :: boolean()
2223

2324
def init_env, do: impl().init_env()
@@ -57,6 +58,17 @@ defmodule Desktop.Platform.System do
5758
end
5859
end
5960

61+
@doc """
62+
Sends a native-host custom event over the mobile bridge.
63+
64+
Replaces direct `GenServer.call(Bridge, {:bridge_call, …})` with
65+
`[:custom_event, event, args]` JSON. No-op (`:ok`) on Wx/Browser backends
66+
or when the bridge transport is not running.
67+
"""
68+
def custom_event(event, args \\ []) when is_atom(event) and is_list(args) do
69+
Helpers.with_wx_env(fn -> impl().custom_event(event, args) end)
70+
end
71+
6072
def activate_event_active?(event),
6173
do: Helpers.with_wx_env(fn -> impl().activate_event_active?(event) end)
6274

lib/desktop/window.ex

Lines changed: 20 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -271,6 +271,21 @@ defmodule Desktop.Window do
271271
GenServer.cast(pid, {:load_url, url})
272272
end
273273

274+
@doc """
275+
Reload the Window webview / native content.
276+
277+
Replaces direct `:wxWebView.reload/1` on the handle from `webview/1`.
278+
279+
## Examples
280+
281+
iex> Desktop.Window.reload(pid)
282+
:ok
283+
284+
"""
285+
def reload(pid) do
286+
GenServer.cast(pid, :reload)
287+
end
288+
274289
@doc """
275290
Show the Window if not visible with the given url.
276291
@@ -634,6 +649,11 @@ defmodule Desktop.Window do
634649
{:noreply, %Window{ui | webview: Fallback.webview_rebuild(ui)}}
635650
end
636651

652+
def handle_cast(:reload, ui = %Window{webview: webview}) do
653+
Platform.Content.reload(webview)
654+
{:noreply, ui}
655+
end
656+
637657
def handle_cast(
638658
{:show_notification, message, id, type, title, callback, timeout},
639659
ui = %Window{notifications: noties, title: window_title}

test/desktop/backend/browser_test.exs

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -38,6 +38,11 @@ defmodule Desktop.Backend.BrowserTest do
3838
assert Browser.get_env() == nil
3939
assert Browser.locale() == nil
4040
assert Browser.os_description() == nil
41+
assert :ok = Browser.custom_event(:share, [])
4142
refute Browser.wx_available?()
4243
end
44+
45+
test "T-BRW: content reload is no-op" do
46+
assert :ok = Browser.reload(nil)
47+
end
4348
end

0 commit comments

Comments
 (0)