mirror of
https://github.com/livebook-dev/livebook.git
synced 2025-12-17 21:50:25 +08:00
Desktop icon improvements (#1201)
This commit is contained in:
parent
2fdcc60dd2
commit
fc82b55dab
9 changed files with 66 additions and 41 deletions
|
|
@ -4,4 +4,11 @@ defmodule AppBuilder do
|
||||||
defdelegate build_mac_app_dmg(release, options), to: AppBuilder.MacOS
|
defdelegate build_mac_app_dmg(release, options), to: AppBuilder.MacOS
|
||||||
|
|
||||||
defdelegate build_windows_installer(release, options), to: AppBuilder.Windows
|
defdelegate build_windows_installer(release, options), to: AppBuilder.Windows
|
||||||
|
|
||||||
|
def os do
|
||||||
|
case :os.type() do
|
||||||
|
{:unix, :darwin} -> :macos
|
||||||
|
{:win32, _} -> :windows
|
||||||
|
end
|
||||||
|
end
|
||||||
end
|
end
|
||||||
|
|
|
||||||
|
|
@ -94,7 +94,7 @@ defmodule AppBuilder.MacOS do
|
||||||
Keyword.validate!(options, [
|
Keyword.validate!(options, [
|
||||||
:name,
|
:name,
|
||||||
:version,
|
:version,
|
||||||
:logo_path,
|
:icon_path,
|
||||||
:info_plist,
|
:info_plist,
|
||||||
:url_schemes,
|
:url_schemes,
|
||||||
:document_types,
|
:document_types,
|
||||||
|
|
@ -125,8 +125,16 @@ defmodule AppBuilder.MacOS do
|
||||||
launcher_src_path
|
launcher_src_path
|
||||||
])
|
])
|
||||||
|
|
||||||
logo_path = options[:logo_path] || Application.app_dir(:wx, "examples/demo/erlang.png")
|
icon_path = options[:icon_path] || Application.app_dir(:wx, "examples/demo/erlang.png")
|
||||||
create_logo(app_bundle_path, logo_path)
|
dest_path = Path.join([app_bundle_path, "Contents", "Resources", "AppIcon.icns"])
|
||||||
|
create_icon(icon_path, dest_path)
|
||||||
|
|
||||||
|
for type <- options[:document_types] || [] do
|
||||||
|
if src_path = type[:icon_path] do
|
||||||
|
dest_path = Path.join([app_bundle_path, "Contents", "Resources", "#{type.name}Icon.icns"])
|
||||||
|
create_icon(src_path, dest_path)
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
info_plist = options[:info_plist] || info_plist(options)
|
info_plist = options[:info_plist] || info_plist(options)
|
||||||
File.write!(Path.join([app_bundle_path, "Contents", "Info.plist"]), info_plist)
|
File.write!(Path.join([app_bundle_path, "Contents", "Info.plist"]), info_plist)
|
||||||
|
|
@ -180,15 +188,16 @@ defmodule AppBuilder.MacOS do
|
||||||
"""
|
"""
|
||||||
end
|
end
|
||||||
|
|
||||||
defp create_logo(app_bundle_path, logo_source_path) do
|
defp create_icon(src_path, dest_path) do
|
||||||
logo_dest_path = Path.join([app_bundle_path, "Contents", "Resources", "AppIcon.icns"])
|
src_path = normalize_icon_path(src_path)
|
||||||
|
|
||||||
if Path.extname(logo_source_path) == ".icns" do
|
if Path.extname(src_path) == ".icns" do
|
||||||
File.cp!(logo_source_path, logo_dest_path)
|
File.cp!(src_path, dest_path)
|
||||||
else
|
else
|
||||||
logo_dest_tmp_path = "tmp/AppIcon.iconset"
|
name = Path.basename(dest_path, ".icns")
|
||||||
File.rm_rf!(logo_dest_tmp_path)
|
dest_tmp_path = "tmp/#{name}.iconset"
|
||||||
File.mkdir_p!(logo_dest_tmp_path)
|
File.rm_rf!(dest_tmp_path)
|
||||||
|
File.mkdir_p!(dest_tmp_path)
|
||||||
|
|
||||||
sizes = for(i <- [16, 32, 64, 128], j <- [1, 2], do: {i, j}) ++ [{512, 1}]
|
sizes = for(i <- [16, 32, 64, 128], j <- [1, 2], do: {i, j}) ++ [{512, 1}]
|
||||||
|
|
||||||
|
|
@ -200,12 +209,12 @@ defmodule AppBuilder.MacOS do
|
||||||
end
|
end
|
||||||
|
|
||||||
size = size * scale
|
size = size * scale
|
||||||
out = "#{logo_dest_tmp_path}/icon_#{size}x#{size}#{suffix}.png"
|
out = "#{dest_tmp_path}/icon_#{size}x#{size}#{suffix}.png"
|
||||||
cmd!("sips", ~w(-z #{size} #{size} #{logo_source_path} --out #{out}))
|
cmd!("sips", ~w(-z #{size} #{size} #{src_path} --out #{out}))
|
||||||
end
|
end
|
||||||
|
|
||||||
cmd!("iconutil", ~w(-c icns #{logo_dest_tmp_path} -o #{logo_dest_path}))
|
cmd!("iconutil", ~w(-c icns #{dest_tmp_path} -o #{dest_path}))
|
||||||
File.rm_rf!(logo_dest_tmp_path)
|
File.rm_rf!(dest_tmp_path)
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
|
|
@ -297,6 +306,10 @@ defmodule AppBuilder.MacOS do
|
||||||
<string><%= ext %></string>
|
<string><%= ext %></string>
|
||||||
<% end %>
|
<% end %>
|
||||||
</array>
|
</array>
|
||||||
|
<%= if type[:icon_path] do %>
|
||||||
|
<key>CFBundleTypeIconFile</key>
|
||||||
|
<string><%= type.name %>Icon</string>
|
||||||
|
<% end %>
|
||||||
</dict>
|
</dict>
|
||||||
<% end %>
|
<% end %>
|
||||||
</array>
|
</array>
|
||||||
|
|
|
||||||
|
|
@ -66,4 +66,12 @@ defmodule AppBuilder.Utils do
|
||||||
"""
|
"""
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
|
def normalize_icon_path(path) when is_binary(path) do
|
||||||
|
path
|
||||||
|
end
|
||||||
|
|
||||||
|
def normalize_icon_path(path_per_os) when is_list(path_per_os) do
|
||||||
|
Keyword.fetch!(path_per_os, AppBuilder.os())
|
||||||
|
end
|
||||||
end
|
end
|
||||||
|
|
|
||||||
|
|
@ -43,7 +43,7 @@ defmodule AppBuilder.Windows do
|
||||||
:version,
|
:version,
|
||||||
:url_schemes,
|
:url_schemes,
|
||||||
:document_types,
|
:document_types,
|
||||||
:logo_path,
|
:icon_path,
|
||||||
:module
|
:module
|
||||||
])
|
])
|
||||||
|
|
||||||
|
|
@ -52,9 +52,9 @@ defmodule AppBuilder.Windows do
|
||||||
vcredist_path = ensure_vcredistx64()
|
vcredist_path = ensure_vcredistx64()
|
||||||
File.cp!(vcredist_path, Path.join(tmp_dir, "vcredist_x64.exe"))
|
File.cp!(vcredist_path, Path.join(tmp_dir, "vcredist_x64.exe"))
|
||||||
|
|
||||||
logo_path = options[:logo_path] || Application.app_dir(:wx, "examples/demo/erlang.png")
|
icon_path = options[:icon_path] || Application.app_dir(:wx, "examples/demo/erlang.png")
|
||||||
app_icon_path = Path.join(tmp_dir, "app_icon.ico")
|
app_icon_path = Path.join(tmp_dir, "app_icon.ico")
|
||||||
copy_image(logo_path, app_icon_path)
|
create_icon(icon_path, app_icon_path)
|
||||||
|
|
||||||
erl_exe = Path.join([tmp_dir, "rel", "erts-#{release.erts_version}", "bin", "erl.exe"])
|
erl_exe = Path.join([tmp_dir, "rel", "erts-#{release.erts_version}", "bin", "erl.exe"])
|
||||||
rcedit_path = ensure_rcedit()
|
rcedit_path = ensure_rcedit()
|
||||||
|
|
@ -254,14 +254,13 @@ defmodule AppBuilder.Windows do
|
||||||
end
|
end
|
||||||
|
|
||||||
defp ensure_magick do
|
defp ensure_magick do
|
||||||
url =
|
System.find_executable("magick.exe") ||
|
||||||
"https://download.imagemagick.org/ImageMagick/download/binaries/ImageMagick-7.1.0-portable-Q16-x64.zip"
|
raise "couldn't find magick.exe in PATH to automatically convert images to .ico"
|
||||||
|
|
||||||
sha256 = "b61a726cea1e3bf395b9aeb323fca062f574fbf8f11f4067f88a0e6b984a1391"
|
|
||||||
AppBuilder.Utils.ensure_executable(url, sha256, "magick.exe")
|
|
||||||
end
|
end
|
||||||
|
|
||||||
defp copy_image(src_path, dest_path) do
|
defp create_icon(src_path, dest_path) do
|
||||||
|
src_path = normalize_icon_path(src_path)
|
||||||
|
|
||||||
if Path.extname(src_path) == ".ico" do
|
if Path.extname(src_path) == ".ico" do
|
||||||
File.cp!(src_path, dest_path)
|
File.cp!(src_path, dest_path)
|
||||||
else
|
else
|
||||||
|
|
|
||||||
|
|
@ -8,16 +8,10 @@ if Mix.target() == :app do
|
||||||
defmacro wxID_OSX_HIDE, do: 5250
|
defmacro wxID_OSX_HIDE, do: 5250
|
||||||
defmacro wxBITMAP_TYPE_PNG, do: 15
|
defmacro wxBITMAP_TYPE_PNG, do: 15
|
||||||
|
|
||||||
def os do
|
|
||||||
case :os.type() do
|
|
||||||
{:unix, :darwin} -> :macos
|
|
||||||
{:win32, _} -> :windows
|
|
||||||
end
|
|
||||||
end
|
|
||||||
|
|
||||||
def taskbar(title, icon, menu_items) do
|
def taskbar(title, icon, menu_items) do
|
||||||
pid = self()
|
pid = self()
|
||||||
options = if os() == :macos, do: [iconType: 1], else: []
|
os = AppBuilder.os()
|
||||||
|
options = if os == :macos, do: [iconType: 1], else: []
|
||||||
|
|
||||||
# skip keyboard shortcuts
|
# skip keyboard shortcuts
|
||||||
menu_items =
|
menu_items =
|
||||||
|
|
@ -35,7 +29,7 @@ if Mix.target() == :app do
|
||||||
|
|
||||||
# For some reason, on macOS the menu event must be handled in another process
|
# 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.
|
# but on Windows it must be either the same process OR we use the callback.
|
||||||
case os() do
|
case os do
|
||||||
:macos ->
|
:macos ->
|
||||||
env = :wx.get_env()
|
env = :wx.get_env()
|
||||||
|
|
||||||
|
|
@ -92,7 +86,7 @@ if Mix.target() == :app do
|
||||||
def menubar(app_name, menus) do
|
def menubar(app_name, menus) do
|
||||||
menubar = :wxMenuBar.new()
|
menubar = :wxMenuBar.new()
|
||||||
|
|
||||||
if os() == :macos, do: fixup_macos_menubar(menubar, app_name)
|
if AppBuilder.os() == :macos, do: fixup_macos_menubar(menubar, app_name)
|
||||||
|
|
||||||
for {title, menu_items} <- menus do
|
for {title, menu_items} <- menus do
|
||||||
true = :wxMenuBar.append(menubar, menu(menu_items), title)
|
true = :wxMenuBar.append(menubar, menu(menu_items), title)
|
||||||
|
|
@ -125,13 +119,13 @@ if Mix.target() == :app do
|
||||||
GenServer.start_link(__MODULE__, arg, name: @name)
|
GenServer.start_link(__MODULE__, arg, name: @name)
|
||||||
end
|
end
|
||||||
|
|
||||||
taskbar_icon_path = "rel/app/taskbar_icon.png"
|
taskbar_icon_path = "rel/app/icon.png"
|
||||||
@external_resource taskbar_icon_path
|
@external_resource taskbar_icon_path
|
||||||
@taskbar_icon File.read!(taskbar_icon_path)
|
@taskbar_icon File.read!(taskbar_icon_path)
|
||||||
|
|
||||||
@impl true
|
@impl true
|
||||||
def init(_) do
|
def init(_) do
|
||||||
os = os()
|
os = AppBuilder.os()
|
||||||
wx = :wx.new()
|
wx = :wx.new()
|
||||||
AppBuilder.Wx.subscribe_to_app_events(@name)
|
AppBuilder.Wx.subscribe_to_app_events(@name)
|
||||||
|
|
||||||
|
|
@ -161,7 +155,7 @@ if Mix.target() == :app do
|
||||||
# 1. MIX_TARGET=app mix phx.server
|
# 1. MIX_TARGET=app mix phx.server
|
||||||
# 2. mix app
|
# 2. mix app
|
||||||
# 3. mix release app
|
# 3. mix release app
|
||||||
taskbar_icon_path = Path.join(System.tmp_dir!(), "taskbar_icon.png")
|
taskbar_icon_path = Path.join(System.tmp_dir!(), "icon.png")
|
||||||
File.write!(taskbar_icon_path, @taskbar_icon)
|
File.write!(taskbar_icon_path, @taskbar_icon)
|
||||||
icon = :wxIcon.new(taskbar_icon_path, type: wxBITMAP_TYPE_PNG())
|
icon = :wxIcon.new(taskbar_icon_path, type: wxBITMAP_TYPE_PNG())
|
||||||
|
|
||||||
|
|
|
||||||
14
mix.exs
14
mix.exs
|
|
@ -175,7 +175,10 @@ defmodule Livebook.MixProject do
|
||||||
@app_options [
|
@app_options [
|
||||||
name: "Livebook",
|
name: "Livebook",
|
||||||
version: @version,
|
version: @version,
|
||||||
logo_path: "rel/app/mac-icon.png",
|
icon_path: [
|
||||||
|
macos: "rel/app/icon-macos.png",
|
||||||
|
windows: "rel/app/icon.ico"
|
||||||
|
],
|
||||||
additional_paths: [
|
additional_paths: [
|
||||||
"/rel/erts-#{:erlang.system_info(:version)}/bin",
|
"/rel/erts-#{:erlang.system_info(:version)}/bin",
|
||||||
"/rel/vendor/elixir/bin"
|
"/rel/vendor/elixir/bin"
|
||||||
|
|
@ -185,6 +188,10 @@ defmodule Livebook.MixProject do
|
||||||
%{
|
%{
|
||||||
name: "LiveMarkdown",
|
name: "LiveMarkdown",
|
||||||
extensions: ["livemd"],
|
extensions: ["livemd"],
|
||||||
|
icon_path: [
|
||||||
|
macos: "rel/app/icon.png",
|
||||||
|
windows: "rel/app/icon.ico"
|
||||||
|
],
|
||||||
# macos specific
|
# macos specific
|
||||||
role: "Editor"
|
role: "Editor"
|
||||||
}
|
}
|
||||||
|
|
@ -218,10 +225,7 @@ defmodule Livebook.MixProject do
|
||||||
end
|
end
|
||||||
|
|
||||||
defp build_windows_installer(release) do
|
defp build_windows_installer(release) do
|
||||||
options =
|
options = Keyword.drop(@app_options, [:additional_paths]) ++ [module: LivebookApp]
|
||||||
Keyword.take(@app_options, [:name, :version, :url_schemes, :document_types]) ++
|
|
||||||
[module: LivebookApp, logo_path: "static/images/logo.png"]
|
|
||||||
|
|
||||||
AppBuilder.build_windows_installer(release, options)
|
AppBuilder.build_windows_installer(release, options)
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
|
||||||
|
Before Width: | Height: | Size: 26 KiB After Width: | Height: | Size: 26 KiB |
BIN
rel/app/icon.ico
Normal file
BIN
rel/app/icon.ico
Normal file
Binary file not shown.
|
After Width: | Height: | Size: 89 KiB |
|
Before Width: | Height: | Size: 21 KiB After Width: | Height: | Size: 21 KiB |
Loading…
Add table
Reference in a new issue