Packages
livebook
0.7.2
0.19.8
0.19.7
0.19.6
0.19.5
0.19.4
0.19.3
0.19.2
0.19.1
0.19.0
0.18.6
0.18.5
0.18.4
0.18.3
0.18.2
0.18.1
0.18.0
0.17.3
0.17.2
0.17.1
0.17.0
0.16.4
0.16.3
0.16.2
0.16.1
0.16.0
0.15.5
0.15.4
0.15.3
0.15.2
0.15.1
0.15.0
0.14.7
0.14.6
0.14.5
0.14.4
0.14.3
0.14.2
0.14.1
0.14.0
0.14.0-rc.1
0.14.0-rc.0
0.13.3
0.13.2
0.13.1
0.13.0
0.12.1
0.12.0
0.11.4
0.11.3
0.11.2
0.11.1
0.11.0
0.10.0
0.9.3
0.9.2
0.9.1
0.9.0
0.8.2
0.8.1
0.8.0
0.7.2
0.7.1
0.7.0
0.6.3
0.6.2
0.6.1
0.6.0
0.5.2
0.5.1
0.5.0
0.4.1
0.4.0
0.3.2
0.3.1
0.3.0
0.2.3
0.2.2
0.2.1
0.2.0
0.1.2
0.1.1
0.1.0
Automate code & data workflows with interactive notebooks
Current section
Files
Jump to
Current section
Files
lib/livebook_app.ex
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