mirror of
https://github.com/livebook-dev/livebook.git
synced 2024-11-15 20:37:55 +08:00
179 lines
4.3 KiB
Elixir
179 lines
4.3 KiB
Elixir
if Mix.target() == :app do
|
|
defmodule LivebookApp do
|
|
@moduledoc false
|
|
@name __MODULE__
|
|
@wxID_OPEN 5000
|
|
@wxID_EXIT 5006
|
|
@wxBITMAP_TYPE_PNG 15
|
|
|
|
use GenServer
|
|
|
|
def start_link(arg) do
|
|
GenServer.start_link(__MODULE__, arg, name: @name)
|
|
end
|
|
|
|
taskbar_icon_path = "rel/app/icon.png"
|
|
@external_resource taskbar_icon_path
|
|
@taskbar_icon File.read!(taskbar_icon_path)
|
|
|
|
@impl true
|
|
def init(_) do
|
|
AppBundler.init()
|
|
os = AppBundler.os()
|
|
:wx.new()
|
|
|
|
# TODO: instead of embedding the icon and copying to tmp, copy the file known location.
|
|
# It's a bit tricky because it needs to support all the ways of running the app:
|
|
# 1. MIX_TARGET=app mix phx.server
|
|
# 2. mix app
|
|
# 3. mix release app
|
|
taskbar_icon_path = Path.join(System.tmp_dir!(), "icon.png")
|
|
File.write!(taskbar_icon_path, @taskbar_icon)
|
|
icon = :wxIcon.new(taskbar_icon_path, type: @wxBITMAP_TYPE_PNG)
|
|
|
|
menu_items = [
|
|
{"Open Browser", key: "ctrl+o", id: @wxID_OPEN},
|
|
{"Quit", key: "ctrl+q", id: @wxID_EXIT}
|
|
]
|
|
|
|
taskbar = WxUtils.taskbar("Livebook", icon, menu_items)
|
|
|
|
if os == :windows do
|
|
:wxTaskBarIcon.connect(taskbar, :taskbar_left_down,
|
|
callback: fn _, _ ->
|
|
open_browser()
|
|
end
|
|
)
|
|
end
|
|
|
|
{:ok, nil}
|
|
end
|
|
|
|
@impl true
|
|
def handle_info(:open_app, state) do
|
|
open_browser()
|
|
{:noreply, state}
|
|
end
|
|
|
|
@impl true
|
|
def handle_info({:open_file, path}, state) do
|
|
path
|
|
|> Livebook.Utils.notebook_open_url()
|
|
|> open_browser()
|
|
|
|
{:noreply, state}
|
|
end
|
|
|
|
@impl true
|
|
def handle_info({:open_url, "livebook://" <> rest}, state) do
|
|
"https://#{rest}"
|
|
|> Livebook.Utils.notebook_import_url()
|
|
|> open_browser()
|
|
|
|
{:noreply, state}
|
|
end
|
|
|
|
@impl true
|
|
def handle_info({:wx, @wxID_EXIT, _, _, _}, _state) do
|
|
System.stop(0)
|
|
end
|
|
|
|
@impl true
|
|
def handle_info({:wx, @wxID_OPEN, _, _, _}, state) do
|
|
open_browser()
|
|
{:noreply, state}
|
|
end
|
|
|
|
if Mix.env() == :dev do
|
|
@impl true
|
|
def handle_info(event, state) do
|
|
IO.inspect(event)
|
|
{:noreply, state}
|
|
end
|
|
end
|
|
|
|
defp open_browser(url \\ LivebookWeb.Endpoint.access_url()) do
|
|
Livebook.Utils.browser_open(url)
|
|
end
|
|
end
|
|
|
|
defmodule WxUtils do
|
|
@moduledoc false
|
|
@wxID_ANY -1
|
|
|
|
def taskbar(title, icon, menu_items) do
|
|
pid = self()
|
|
os = AppBundler.os()
|
|
options = if os == :macos, do: [iconType: 1], else: []
|
|
|
|
# skip keyboard shortcuts
|
|
menu_items =
|
|
for item <- menu_items do
|
|
{title, options} = item
|
|
options = Keyword.delete(options, :key)
|
|
{title, options}
|
|
end
|
|
|
|
taskbar =
|
|
:wxTaskBarIcon.new(
|
|
[
|
|
createPopupMenu: fn ->
|
|
menu = menu(menu_items)
|
|
|
|
# For some reason, on macOS the menu event must be handled in another process
|
|
# but on Windows it must be either the same process OR we use the callback.
|
|
case os do
|
|
:macos ->
|
|
env = :wx.get_env()
|
|
|
|
Task.start_link(fn ->
|
|
:wx.set_env(env)
|
|
:wxMenu.connect(menu, :command_menu_selected)
|
|
|
|
receive do
|
|
message ->
|
|
send(pid, message)
|
|
end
|
|
end)
|
|
|
|
:windows ->
|
|
:ok =
|
|
:wxMenu.connect(menu, :command_menu_selected,
|
|
callback: fn wx, _ ->
|
|
send(pid, wx)
|
|
end
|
|
)
|
|
end
|
|
|
|
menu
|
|
end
|
|
] ++ options
|
|
)
|
|
|
|
:wxTaskBarIcon.setIcon(taskbar, icon, tooltip: title)
|
|
taskbar
|
|
end
|
|
|
|
def menu(items) do
|
|
menu = :wxMenu.new()
|
|
|
|
Enum.each(items, fn
|
|
{title, options} ->
|
|
id = Keyword.get(options, :id, @wxID_ANY)
|
|
|
|
title =
|
|
case Keyword.fetch(options, :key) do
|
|
{:ok, key} ->
|
|
title <> "\t" <> key
|
|
|
|
:error ->
|
|
title
|
|
end
|
|
|
|
:wxMenu.append(menu, id, title)
|
|
end)
|
|
|
|
menu
|
|
end
|
|
end
|
|
end
|