Skip to content

Commit e21510b

Browse files
authored
Basic igniter installer (#62)
* Stub igniter installer * Add Desktop.Window child application * Glue up enough installer to launch a desktop window to the app Bootstrapping needs to be documented, and will be easier once the installer is included in a published release of elixir-desktop. For now, add the {:desktop, path: to_dev_directory} dependency manually and then run `mix desktop.install` in the target project. * Fix quoting; simplify and clean up code Use quote/unquote to wire dynamic values into generated code. Make the installer idempotent, safe to run twice. Remove unused bits. * Update tests * Add minimal docs * Finish documentation for full project generation Note that this won't work until the mix task is in a published release of desktop. * Include igniter but not in production This is untested, the hope is that `mix desktop.install` still works but the dependency doesn't carry through.
1 parent d2ddcc3 commit e21510b

3 files changed

Lines changed: 210 additions & 1 deletion

File tree

lib/mix/tasks/desktop.install.ex

Lines changed: 180 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,180 @@
1+
defmodule Mix.Tasks.Desktop.Install do
2+
@shortdoc "Add Elixir Desktop support to an existing project"
3+
4+
@moduledoc """
5+
#{@shortdoc}
6+
7+
This mix task adds the minimal glue to launch an Elixir Desktop
8+
main window for your application.
9+
10+
To use this task, you'll either need to install Igniter globally, or
11+
manually add the desktop dependency to your project's mix.exs:
12+
13+
```
14+
{:desktop, "~> 1.0"}
15+
```
16+
17+
## Examples
18+
19+
Add desktop support to a project:
20+
21+
```bash
22+
mix desktop.install
23+
```
24+
25+
Create a new project with desktop support:
26+
27+
```bash
28+
mix archive.install hex igniter_new
29+
30+
mix igniter.new my_app --install desktop --with phx.new
31+
```
32+
"""
33+
34+
use Igniter.Mix.Task
35+
36+
@impl Igniter.Mix.Task
37+
def info(_argv, _composing_task) do
38+
%Igniter.Mix.Task.Info{
39+
# Groups allow for overlapping arguments for tasks by the same author
40+
# See the generators guide for more.
41+
group: :desktop
42+
}
43+
end
44+
45+
@impl Igniter.Mix.Task
46+
def igniter(igniter) do
47+
app = Igniter.Project.Application.app_name(igniter)
48+
endpoint = Igniter.Libs.Phoenix.web_module_name(igniter, "Endpoint")
49+
menu = Igniter.Project.Module.module_name(igniter, "Menu")
50+
menubar = Igniter.Project.Module.module_name(igniter, "MenuBar")
51+
gettext = Igniter.Libs.Phoenix.web_module_name(igniter, "Gettext")
52+
main_window = Igniter.Project.Module.module_name(igniter, MainWindow)
53+
54+
igniter
55+
|> Igniter.compose_task("igniter.add", ["desktop"])
56+
|> add_menu_bar(menubar, gettext, endpoint)
57+
|> add_menu(menu, gettext, app, main_window)
58+
|> Igniter.Project.Application.add_new_child(
59+
{
60+
Desktop.Window,
61+
{:code,
62+
quote do
63+
[
64+
app: unquote(app),
65+
id: unquote(Igniter.Project.Module.module_name(igniter, MainWindow)),
66+
title: unquote(to_string(app)),
67+
size: {600, 500},
68+
# icon: "icon.png", # TODO: ship an example taskbar icon here
69+
menubar: unquote(menubar),
70+
icon_menu: unquote(menu),
71+
url: &unquote(endpoint).url/0
72+
]
73+
end}
74+
},
75+
after: [endpoint]
76+
)
77+
78+
# TODO: Add runtime_tools observer if available, or optionall add the dependency
79+
end
80+
81+
defp add_menu_bar(igniter, menubar, gettext, endpoint) do
82+
Igniter.Project.Module.find_and_update_or_create_module(
83+
igniter,
84+
menubar,
85+
"""
86+
@moduledoc \"""
87+
Menu bar that is shown as part of the main window on Windows/Linux. In
88+
MacOS this menu bar appears at the very top of the screen.
89+
\"""
90+
use Gettext, backend: #{inspect(gettext)}
91+
use Desktop.Menu
92+
alias Desktop.Window
93+
94+
@impl true
95+
def render(assigns) do
96+
~H\"""
97+
<menubar>
98+
<menu label={gettext "File"}>
99+
<item onclick="quit">{gettext "Quit"}</item>
100+
</menu>
101+
<menu label={gettext "Extra"}>
102+
<item onclick="browser">{gettext "Open Browser"}</item>
103+
</menu>
104+
</menubar>
105+
\"""
106+
end
107+
108+
@impl true
109+
def handle_event("quit", menu) do
110+
Window.quit()
111+
{:noreply, menu}
112+
end
113+
114+
def handle_event("browser", menu) do
115+
Window.prepare_url(#{inspect(endpoint)}.url())
116+
|> :wx_misc.launchDefaultBrowser()
117+
118+
{:noreply, menu}
119+
end
120+
121+
@impl true
122+
def handle_info(_, menu) do
123+
{:noreply, menu}
124+
end
125+
126+
@impl true
127+
def mount(menu) do
128+
{:ok, menu}
129+
end
130+
""",
131+
fn zipper -> {:ok, zipper} end
132+
)
133+
end
134+
135+
defp add_menu(igniter, menu, gettext, app, main_window) do
136+
Igniter.Project.Module.find_and_update_or_create_module(
137+
igniter,
138+
menu,
139+
"""
140+
@moduledoc \"""
141+
Menu that is shown when a user clicks on the taskbar icon of the #{app}
142+
\"""
143+
use Gettext, backend: #{inspect(gettext)}
144+
use Desktop.Menu
145+
146+
@impl true
147+
def render(assigns) do
148+
~H\"""
149+
<menu>
150+
<item onclick="edit">{gettext "Open"}</item>
151+
<hr/>
152+
<item onclick="quit">{gettext "Quit"}</item>
153+
</menu>
154+
\"""
155+
end
156+
157+
@impl true
158+
def handle_event(command, menu) do
159+
case command do
160+
<<"quit">> -> Desktop.Window.quit()
161+
<<"edit">> -> Desktop.Window.show(#{inspect(main_window)})
162+
end
163+
164+
{:noreply, menu}
165+
end
166+
167+
@impl true
168+
def handle_info(_, menu) do
169+
{:noreply, menu}
170+
end
171+
172+
@impl true
173+
def mount(menu) do
174+
{:ok, menu}
175+
end
176+
""",
177+
fn zipper -> {:ok, zipper} end
178+
)
179+
end
180+
end

mix.exs

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -86,7 +86,8 @@ defmodule Desktop.MixProject do
8686
{:phoenix, "> 1.7.10"},
8787
{:phoenix_live_view, "> 1.0.0"},
8888
{:plug, "> 1.0.0"},
89-
{:gettext, "> 0.10.0"}
89+
{:gettext, "> 0.10.0"},
90+
{:igniter, "~> 0.6", optional: true}
9091
]
9192

9293
if Mix.target() in [:android, :ios] do
Lines changed: 28 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,28 @@
1+
defmodule Mix.Tasks.Desktop.InstallTest do
2+
use ExUnit.Case, async: true
3+
import Igniter.Test
4+
5+
test "it installs desktop dependency and window" do
6+
phx_test_project(app_name: :my_app)
7+
|> Igniter.compose_task("desktop.install", [])
8+
|> assert_has_patch("mix.exs", """
9+
+ | {:desktop,
10+
""")
11+
|> assert_has_patch("lib/my_app/application.ex", """
12+
- | MyAppWeb.Endpoint
13+
+ | MyAppWeb.Endpoint,
14+
+ | {Desktop.Window,
15+
+ | [
16+
+ | app: :my_app,
17+
+ | id: MyApp.MainWindow,
18+
+ | title: "my_app",
19+
+ | size: {600, 500},
20+
+ | menubar: MyApp.MenuBar,
21+
+ | icon_menu: MyApp.Menu,
22+
+ | url: &MyAppWeb.Endpoint.url/0
23+
+ | ]}
24+
""")
25+
|> Igniter.Test.assert_creates("lib/my_app/menu.ex")
26+
|> Igniter.Test.assert_creates("lib/my_app/menu_bar.ex")
27+
end
28+
end

0 commit comments

Comments
 (0)