Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
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
180 changes: 180 additions & 0 deletions lib/mix/tasks/desktop.install.ex
Original file line number Diff line number Diff line change
@@ -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\"""
<menubar>
<menu label={gettext "File"}>
<item onclick="quit">{gettext "Quit"}</item>
</menu>
<menu label={gettext "Extra"}>
<item onclick="browser">{gettext "Open Browser"}</item>
</menu>
</menubar>
\"""
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\"""
<menu>
<item onclick="edit">{gettext "Open"}</item>
<hr/>
<item onclick="quit">{gettext "Quit"}</item>
</menu>
\"""
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
3 changes: 2 additions & 1 deletion mix.exs
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand Down
28 changes: 28 additions & 0 deletions test/mix/tasks/desktop.install_test.exs
Original file line number Diff line number Diff line change
@@ -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
Loading