fix: shutdown moved to standard functionality
This commit is contained in:
@@ -6,16 +6,24 @@ defmodule BDS.Desktop.MainWindow do
|
|||||||
alias Desktop.Window
|
alias Desktop.Window
|
||||||
|
|
||||||
@window_id __MODULE__
|
@window_id __MODULE__
|
||||||
|
@server_name BDS.Desktop.MainWindow.Watcher
|
||||||
@persist_interval_ms 1_000
|
@persist_interval_ms 1_000
|
||||||
@default_size {1280, 780}
|
@default_size {1280, 780}
|
||||||
@default_min_size {800, 600}
|
@default_min_size {800, 600}
|
||||||
@state_file "window-state.json"
|
@state_file "window-state.json"
|
||||||
|
|
||||||
def start_link(_opts) do
|
def start_link(_opts) do
|
||||||
GenServer.start_link(__MODULE__, :ok)
|
GenServer.start_link(__MODULE__, :ok, name: @server_name)
|
||||||
end
|
end
|
||||||
|
|
||||||
def window_id, do: @window_id
|
def window_id, do: @window_id
|
||||||
|
def server_name, do: @server_name
|
||||||
|
|
||||||
|
def persist_now(timeout \\ 100) do
|
||||||
|
GenServer.call(@server_name, :persist_bounds_now, timeout)
|
||||||
|
catch
|
||||||
|
:exit, _reason -> :ok
|
||||||
|
end
|
||||||
|
|
||||||
def window_options(extra_opts \\ []) do
|
def window_options(extra_opts \\ []) do
|
||||||
desktop_config = Application.get_env(:bds, :desktop, [])
|
desktop_config = Application.get_env(:bds, :desktop, [])
|
||||||
@@ -90,6 +98,11 @@ defmodule BDS.Desktop.MainWindow do
|
|||||||
{:noreply, %{state | last_bounds: next_bounds}}
|
{:noreply, %{state | last_bounds: next_bounds}}
|
||||||
end
|
end
|
||||||
|
|
||||||
|
@impl true
|
||||||
|
def handle_call(:persist_bounds_now, _from, state) do
|
||||||
|
{:reply, :ok, persist_current_bounds(state)}
|
||||||
|
end
|
||||||
|
|
||||||
@impl true
|
@impl true
|
||||||
def terminate(_reason, %{last_bounds: last_bounds}) do
|
def terminate(_reason, %{last_bounds: last_bounds}) do
|
||||||
if bounds = last_bounds do
|
if bounds = last_bounds do
|
||||||
@@ -103,6 +116,16 @@ defmodule BDS.Desktop.MainWindow do
|
|||||||
Process.send_after(self(), :persist_bounds, @persist_interval_ms)
|
Process.send_after(self(), :persist_bounds, @persist_interval_ms)
|
||||||
end
|
end
|
||||||
|
|
||||||
|
defp persist_current_bounds(%{frame: frame} = state) do
|
||||||
|
next_bounds = current_bounds(frame) || state.last_bounds
|
||||||
|
|
||||||
|
if next_bounds do
|
||||||
|
_ = persist_bounds(next_bounds)
|
||||||
|
end
|
||||||
|
|
||||||
|
%{state | last_bounds: next_bounds}
|
||||||
|
end
|
||||||
|
|
||||||
defp apply_restored_bounds(frame) do
|
defp apply_restored_bounds(frame) do
|
||||||
case restore_bounds() do
|
case restore_bounds() do
|
||||||
%{x: x, y: y, width: width, height: height} ->
|
%{x: x, y: y, width: width, height: height} ->
|
||||||
@@ -127,23 +150,30 @@ defmodule BDS.Desktop.MainWindow do
|
|||||||
defp current_bounds(nil), do: nil
|
defp current_bounds(nil), do: nil
|
||||||
|
|
||||||
defp current_bounds(frame) do
|
defp current_bounds(frame) do
|
||||||
with_wx_env(fn ->
|
try do
|
||||||
cond do
|
with_wx_env(fn ->
|
||||||
not :wxWindow.isShown(frame) ->
|
cond do
|
||||||
nil
|
not :wxWindow.isShown(frame) ->
|
||||||
|
nil
|
||||||
|
|
||||||
:wxTopLevelWindow.isFullScreen(frame) ->
|
:wxTopLevelWindow.isFullScreen(frame) ->
|
||||||
nil
|
nil
|
||||||
|
|
||||||
:wxTopLevelWindow.isMaximized(frame) ->
|
:wxTopLevelWindow.isMaximized(frame) ->
|
||||||
nil
|
nil
|
||||||
|
|
||||||
true ->
|
true ->
|
||||||
{x, y} = :wxWindow.getPosition(frame)
|
{x, y} = :wxWindow.getPosition(frame)
|
||||||
{width, height} = :wxWindow.getSize(frame)
|
{width, height} = :wxWindow.getSize(frame)
|
||||||
%{x: x, y: y, width: width, height: height}
|
%{x: x, y: y, width: width, height: height}
|
||||||
end
|
end
|
||||||
end)
|
end)
|
||||||
|
rescue
|
||||||
|
ErlangError -> nil
|
||||||
|
FunctionClauseError -> nil
|
||||||
|
catch
|
||||||
|
:exit, _reason -> nil
|
||||||
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
defp with_wx_env(fun) do
|
defp with_wx_env(fun) do
|
||||||
|
|||||||
@@ -4,8 +4,6 @@ defmodule BDS.Desktop.Shutdown do
|
|||||||
alias BDS.Desktop.MainWindow
|
alias BDS.Desktop.MainWindow
|
||||||
alias Desktop.Window
|
alias Desktop.Window
|
||||||
|
|
||||||
@stop_delay_ms 100
|
|
||||||
|
|
||||||
@spec install_handlers(term()) :: :ok
|
@spec install_handlers(term()) :: :ok
|
||||||
def install_handlers(frame) do
|
def install_handlers(frame) do
|
||||||
:wx.set_env(Desktop.Env.wx_env())
|
:wx.set_env(Desktop.Env.wx_env())
|
||||||
@@ -17,13 +15,6 @@ defmodule BDS.Desktop.Shutdown do
|
|||||||
userData: self()
|
userData: self()
|
||||||
)
|
)
|
||||||
|
|
||||||
_ = :wxFrame.disconnect(frame, :command_menu_selected, id: Desktop.Wx.wxID_EXIT())
|
|
||||||
|
|
||||||
:wxFrame.connect(frame, :command_menu_selected,
|
|
||||||
id: Desktop.Wx.wxID_EXIT(),
|
|
||||||
callback: &__MODULE__.command_menu_selected/2
|
|
||||||
)
|
|
||||||
|
|
||||||
:ok
|
:ok
|
||||||
rescue
|
rescue
|
||||||
_error -> :ok
|
_error -> :ok
|
||||||
@@ -51,42 +42,16 @@ defmodule BDS.Desktop.Shutdown do
|
|||||||
request_quit()
|
request_quit()
|
||||||
end
|
end
|
||||||
|
|
||||||
@spec command_menu_selected(tuple(), term()) :: :ok
|
|
||||||
def command_menu_selected(_event, _command_event) do
|
|
||||||
request_quit()
|
|
||||||
end
|
|
||||||
|
|
||||||
defp start_shutdown_task do
|
defp start_shutdown_task do
|
||||||
Task.start(fn ->
|
Task.start(fn ->
|
||||||
close_main_window()
|
MainWindow.persist_now()
|
||||||
Process.sleep(@stop_delay_ms)
|
quit_module().quit()
|
||||||
System.stop(0)
|
|
||||||
end)
|
end)
|
||||||
|
|
||||||
:ok
|
:ok
|
||||||
end
|
end
|
||||||
|
|
||||||
defp close_main_window do
|
defp quit_module do
|
||||||
with frame when not is_nil(frame) <- main_frame() do
|
Application.get_env(:bds, :desktop_window_quit_module, Window)
|
||||||
:wx.set_env(Desktop.Env.wx_env())
|
|
||||||
|
|
||||||
if :wxWindow.isShown(frame) do
|
|
||||||
:wxWindow.hide(frame)
|
|
||||||
end
|
|
||||||
|
|
||||||
:wxWindow.destroy(frame)
|
|
||||||
else
|
|
||||||
_other -> :ok
|
|
||||||
end
|
|
||||||
rescue
|
|
||||||
_error -> :ok
|
|
||||||
catch
|
|
||||||
:exit, _reason -> :ok
|
|
||||||
end
|
|
||||||
|
|
||||||
defp main_frame do
|
|
||||||
Window.frame(MainWindow.window_id())
|
|
||||||
catch
|
|
||||||
:exit, _reason -> nil
|
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|||||||
@@ -44,6 +44,11 @@ defmodule BDS.Desktop.MainWindowTest do
|
|||||||
assert MainWindow.restore_bounds() == %{x: 120, y: 80, width: 1260, height: 820}
|
assert MainWindow.restore_bounds() == %{x: 120, y: 80, width: 1260, height: 820}
|
||||||
end
|
end
|
||||||
|
|
||||||
|
test "window id and watcher process name do not collide" do
|
||||||
|
assert MainWindow.window_id() == BDS.Desktop.MainWindow
|
||||||
|
assert MainWindow.server_name() == BDS.Desktop.MainWindow.Watcher
|
||||||
|
end
|
||||||
|
|
||||||
test "window options clamp oversized startup bounds to the visible client area" do
|
test "window options clamp oversized startup bounds to the visible client area" do
|
||||||
desktop = Application.get_env(:bds, :desktop, [])
|
desktop = Application.get_env(:bds, :desktop, [])
|
||||||
|
|
||||||
@@ -78,4 +83,22 @@ defmodule BDS.Desktop.MainWindowTest do
|
|||||||
"height" => 700
|
"height" => 700
|
||||||
}
|
}
|
||||||
end
|
end
|
||||||
|
|
||||||
|
test "persist timer keeps last bounds when the wx frame is already gone", %{path: path} do
|
||||||
|
bounds = %{x: 166, y: 57, width: 1280, height: 780}
|
||||||
|
|
||||||
|
assert {:noreply, state} =
|
||||||
|
MainWindow.handle_info(:persist_bounds, %{
|
||||||
|
frame: :invalid_wx_frame,
|
||||||
|
last_bounds: bounds
|
||||||
|
})
|
||||||
|
|
||||||
|
assert state.last_bounds == bounds
|
||||||
|
|
||||||
|
refute File.exists?(path)
|
||||||
|
end
|
||||||
|
|
||||||
|
test "persist now is harmless when the window watcher is not running" do
|
||||||
|
assert :ok = MainWindow.persist_now()
|
||||||
|
end
|
||||||
end
|
end
|
||||||
|
|||||||
@@ -10,6 +10,13 @@ defmodule BDS.DesktopTest do
|
|||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
|
defmodule FakeWindowQuit do
|
||||||
|
def quit do
|
||||||
|
send(Application.fetch_env!(:bds, :desktop_shutdown_test_pid), :window_quit_requested)
|
||||||
|
:ok
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
test "desktop configuration no longer uses a pending adapter" do
|
test "desktop configuration no longer uses a pending adapter" do
|
||||||
assert Application.get_env(:bds, BDS.Application)[:desktop_adapter] == :desktop
|
assert Application.get_env(:bds, BDS.Application)[:desktop_adapter] == :desktop
|
||||||
end
|
end
|
||||||
@@ -138,6 +145,29 @@ defmodule BDS.DesktopTest do
|
|||||||
assert_receive :quit_requested
|
assert_receive :quit_requested
|
||||||
end
|
end
|
||||||
|
|
||||||
|
test "cmd-q remains handled by the desktop window quit handler" do
|
||||||
|
refute function_exported?(BDS.Desktop.Shutdown, :command_menu_selected, 2)
|
||||||
|
end
|
||||||
|
|
||||||
|
test "app-owned shutdown delegates final termination to the desktop hard quit path" do
|
||||||
|
previous_module = Application.get_env(:bds, :desktop_shutdown_module)
|
||||||
|
previous_quit_module = Application.get_env(:bds, :desktop_window_quit_module)
|
||||||
|
previous_pid = Application.get_env(:bds, :desktop_shutdown_test_pid)
|
||||||
|
|
||||||
|
Application.put_env(:bds, :desktop_shutdown_module, BDS.Desktop.Shutdown)
|
||||||
|
Application.put_env(:bds, :desktop_window_quit_module, FakeWindowQuit)
|
||||||
|
Application.put_env(:bds, :desktop_shutdown_test_pid, self())
|
||||||
|
|
||||||
|
on_exit(fn ->
|
||||||
|
restore_env(:desktop_shutdown_module, previous_module)
|
||||||
|
restore_env(:desktop_window_quit_module, previous_quit_module)
|
||||||
|
restore_env(:desktop_shutdown_test_pid, previous_pid)
|
||||||
|
end)
|
||||||
|
|
||||||
|
assert :ok = BDS.Desktop.Shutdown.request_quit()
|
||||||
|
assert_receive :window_quit_requested
|
||||||
|
end
|
||||||
|
|
||||||
test "desktop root html is a LiveView shell and references only the live bootstrap assets" do
|
test "desktop root html is a LiveView shell and references only the live bootstrap assets" do
|
||||||
conn = conn(:get, "/?k=#{Desktop.Auth.login_key()}")
|
conn = conn(:get, "/?k=#{Desktop.Auth.login_key()}")
|
||||||
conn = BDS.Desktop.Endpoint.call(conn, BDS.Desktop.Endpoint.init([]))
|
conn = BDS.Desktop.Endpoint.call(conn, BDS.Desktop.Endpoint.init([]))
|
||||||
|
|||||||
Reference in New Issue
Block a user