diff --git a/lib/mix/tasks/desktop.install.ex b/lib/mix/tasks/desktop.install.ex new file mode 100644 index 0000000..332bb91 --- /dev/null +++ b/lib/mix/tasks/desktop.install.ex @@ -0,0 +1,180 @@ +defmodule Mix.Tasks.Desktop.Install do + @shortdoc "Add Elixir Desktop support to an existing project" + + @moduledoc """ + #{@shortdoc} + + This mix task adds the minimal glue to launch an Elixir Desktop + main window for your application. + + To use this task, you'll either need to install Igniter globally, or + manually add the desktop dependency to your project's mix.exs: + + ``` + {:desktop, "~> 1.0"} + ``` + + ## Examples + + Add desktop support to a project: + + ```bash + mix desktop.install + ``` + + Create a new project with desktop support: + + ```bash + mix archive.install hex igniter_new + + mix igniter.new my_app --install desktop --with phx.new + ``` + """ + + use Igniter.Mix.Task + + @impl Igniter.Mix.Task + def info(_argv, _composing_task) do + %Igniter.Mix.Task.Info{ + # Groups allow for overlapping arguments for tasks by the same author + # See the generators guide for more. + group: :desktop + } + end + + @impl Igniter.Mix.Task + def igniter(igniter) do + app = Igniter.Project.Application.app_name(igniter) + endpoint = Igniter.Libs.Phoenix.web_module_name(igniter, "Endpoint") + menu = Igniter.Project.Module.module_name(igniter, "Menu") + menubar = Igniter.Project.Module.module_name(igniter, "MenuBar") + gettext = Igniter.Libs.Phoenix.web_module_name(igniter, "Gettext") + main_window = Igniter.Project.Module.module_name(igniter, MainWindow) + + igniter + |> Igniter.compose_task("igniter.add", ["desktop"]) + |> add_menu_bar(menubar, gettext, endpoint) + |> add_menu(menu, gettext, app, main_window) + |> Igniter.Project.Application.add_new_child( + { + Desktop.Window, + {:code, + quote do + [ + app: unquote(app), + id: unquote(Igniter.Project.Module.module_name(igniter, MainWindow)), + title: unquote(to_string(app)), + size: {600, 500}, + # icon: "icon.png", # TODO: ship an example taskbar icon here + menubar: unquote(menubar), + icon_menu: unquote(menu), + url: &unquote(endpoint).url/0 + ] + end} + }, + after: [endpoint] + ) + + # TODO: Add runtime_tools observer if available, or optionall add the dependency + end + + defp add_menu_bar(igniter, menubar, gettext, endpoint) do + Igniter.Project.Module.find_and_update_or_create_module( + igniter, + menubar, + """ + @moduledoc \""" + Menu bar that is shown as part of the main window on Windows/Linux. In + MacOS this menu bar appears at the very top of the screen. + \""" + use Gettext, backend: #{inspect(gettext)} + use Desktop.Menu + alias Desktop.Window + + @impl true + def render(assigns) do + ~H\""" + + + {gettext "Quit"} + + + {gettext "Open Browser"} + + + \""" + end + + @impl true + def handle_event("quit", menu) do + Window.quit() + {:noreply, menu} + end + + def handle_event("browser", menu) do + Window.prepare_url(#{inspect(endpoint)}.url()) + |> :wx_misc.launchDefaultBrowser() + + {:noreply, menu} + end + + @impl true + def handle_info(_, menu) do + {:noreply, menu} + end + + @impl true + def mount(menu) do + {:ok, menu} + end + """, + fn zipper -> {:ok, zipper} end + ) + end + + defp add_menu(igniter, menu, gettext, app, main_window) do + Igniter.Project.Module.find_and_update_or_create_module( + igniter, + menu, + """ + @moduledoc \""" + Menu that is shown when a user clicks on the taskbar icon of the #{app} + \""" + use Gettext, backend: #{inspect(gettext)} + use Desktop.Menu + + @impl true + def render(assigns) do + ~H\""" + + {gettext "Open"} +
+ {gettext "Quit"} +
+ \""" + end + + @impl true + def handle_event(command, menu) do + case command do + <<"quit">> -> Desktop.Window.quit() + <<"edit">> -> Desktop.Window.show(#{inspect(main_window)}) + end + + {:noreply, menu} + end + + @impl true + def handle_info(_, menu) do + {:noreply, menu} + end + + @impl true + def mount(menu) do + {:ok, menu} + end + """, + fn zipper -> {:ok, zipper} end + ) + end +end diff --git a/mix.exs b/mix.exs index 8a7b2e7..ff3bc0d 100644 --- a/mix.exs +++ b/mix.exs @@ -86,7 +86,8 @@ defmodule Desktop.MixProject do {:phoenix, "> 1.7.10"}, {:phoenix_live_view, "> 1.0.0"}, {:plug, "> 1.0.0"}, - {:gettext, "> 0.10.0"} + {:gettext, "> 0.10.0"}, + {:igniter, "~> 0.6", optional: true} ] if Mix.target() in [:android, :ios] do diff --git a/test/mix/tasks/desktop.install_test.exs b/test/mix/tasks/desktop.install_test.exs new file mode 100644 index 0000000..f978373 --- /dev/null +++ b/test/mix/tasks/desktop.install_test.exs @@ -0,0 +1,28 @@ +defmodule Mix.Tasks.Desktop.InstallTest do + use ExUnit.Case, async: true + import Igniter.Test + + test "it installs desktop dependency and window" do + phx_test_project(app_name: :my_app) + |> Igniter.compose_task("desktop.install", []) + |> assert_has_patch("mix.exs", """ + + | {:desktop, + """) + |> assert_has_patch("lib/my_app/application.ex", """ + - | MyAppWeb.Endpoint + + | MyAppWeb.Endpoint, + + | {Desktop.Window, + + | [ + + | app: :my_app, + + | id: MyApp.MainWindow, + + | title: "my_app", + + | size: {600, 500}, + + | menubar: MyApp.MenuBar, + + | icon_menu: MyApp.Menu, + + | url: &MyAppWeb.Endpoint.url/0 + + | ]} + """) + |> Igniter.Test.assert_creates("lib/my_app/menu.ex") + |> Igniter.Test.assert_creates("lib/my_app/menu_bar.ex") + end +end