From fc82b55dab4f8ee038e2d0339ad1fed324a299e1 Mon Sep 17 00:00:00 2001 From: Wojtek Mach Date: Fri, 27 May 2022 14:45:20 +0200 Subject: [PATCH] Desktop icon improvements (#1201) --- app_builder/lib/app_builder.ex | 7 ++++ app_builder/lib/app_builder/macos.ex | 41 +++++++++++++++-------- app_builder/lib/app_builder/utils.ex | 8 +++++ app_builder/lib/app_builder/windows.ex | 17 +++++----- lib/livebook_app.ex | 20 ++++------- mix.exs | 14 +++++--- rel/app/{mac-icon.png => icon-macos.png} | Bin rel/app/icon.ico | Bin 0 -> 91550 bytes rel/app/{taskbar_icon.png => icon.png} | Bin 9 files changed, 66 insertions(+), 41 deletions(-) rename rel/app/{mac-icon.png => icon-macos.png} (100%) create mode 100644 rel/app/icon.ico rename rel/app/{taskbar_icon.png => icon.png} (100%) diff --git a/app_builder/lib/app_builder.ex b/app_builder/lib/app_builder.ex index 4e8ba6f97..45c47fe3e 100644 --- a/app_builder/lib/app_builder.ex +++ b/app_builder/lib/app_builder.ex @@ -4,4 +4,11 @@ defmodule AppBuilder do defdelegate build_mac_app_dmg(release, options), to: AppBuilder.MacOS defdelegate build_windows_installer(release, options), to: AppBuilder.Windows + + def os do + case :os.type() do + {:unix, :darwin} -> :macos + {:win32, _} -> :windows + end + end end diff --git a/app_builder/lib/app_builder/macos.ex b/app_builder/lib/app_builder/macos.ex index be6c2e8a4..b8d4d4554 100644 --- a/app_builder/lib/app_builder/macos.ex +++ b/app_builder/lib/app_builder/macos.ex @@ -94,7 +94,7 @@ defmodule AppBuilder.MacOS do Keyword.validate!(options, [ :name, :version, - :logo_path, + :icon_path, :info_plist, :url_schemes, :document_types, @@ -125,8 +125,16 @@ defmodule AppBuilder.MacOS do launcher_src_path ]) - logo_path = options[:logo_path] || Application.app_dir(:wx, "examples/demo/erlang.png") - create_logo(app_bundle_path, logo_path) + icon_path = options[:icon_path] || Application.app_dir(:wx, "examples/demo/erlang.png") + 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) File.write!(Path.join([app_bundle_path, "Contents", "Info.plist"]), info_plist) @@ -180,15 +188,16 @@ defmodule AppBuilder.MacOS do """ end - defp create_logo(app_bundle_path, logo_source_path) do - logo_dest_path = Path.join([app_bundle_path, "Contents", "Resources", "AppIcon.icns"]) + defp create_icon(src_path, dest_path) do + src_path = normalize_icon_path(src_path) - if Path.extname(logo_source_path) == ".icns" do - File.cp!(logo_source_path, logo_dest_path) + if Path.extname(src_path) == ".icns" do + File.cp!(src_path, dest_path) else - logo_dest_tmp_path = "tmp/AppIcon.iconset" - File.rm_rf!(logo_dest_tmp_path) - File.mkdir_p!(logo_dest_tmp_path) + name = Path.basename(dest_path, ".icns") + dest_tmp_path = "tmp/#{name}.iconset" + 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}] @@ -200,12 +209,12 @@ defmodule AppBuilder.MacOS do end size = size * scale - out = "#{logo_dest_tmp_path}/icon_#{size}x#{size}#{suffix}.png" - cmd!("sips", ~w(-z #{size} #{size} #{logo_source_path} --out #{out})) + out = "#{dest_tmp_path}/icon_#{size}x#{size}#{suffix}.png" + cmd!("sips", ~w(-z #{size} #{size} #{src_path} --out #{out})) end - cmd!("iconutil", ~w(-c icns #{logo_dest_tmp_path} -o #{logo_dest_path})) - File.rm_rf!(logo_dest_tmp_path) + cmd!("iconutil", ~w(-c icns #{dest_tmp_path} -o #{dest_path})) + File.rm_rf!(dest_tmp_path) end end @@ -297,6 +306,10 @@ defmodule AppBuilder.MacOS do <%= ext %> <% end %> + <%= if type[:icon_path] do %> + CFBundleTypeIconFile + <%= type.name %>Icon + <% end %> <% end %> diff --git a/app_builder/lib/app_builder/utils.ex b/app_builder/lib/app_builder/utils.ex index e612e8884..c74792b0d 100644 --- a/app_builder/lib/app_builder/utils.ex +++ b/app_builder/lib/app_builder/utils.ex @@ -66,4 +66,12 @@ defmodule AppBuilder.Utils do """ 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 diff --git a/app_builder/lib/app_builder/windows.ex b/app_builder/lib/app_builder/windows.ex index 29c5e79ba..1d25089ee 100644 --- a/app_builder/lib/app_builder/windows.ex +++ b/app_builder/lib/app_builder/windows.ex @@ -43,7 +43,7 @@ defmodule AppBuilder.Windows do :version, :url_schemes, :document_types, - :logo_path, + :icon_path, :module ]) @@ -52,9 +52,9 @@ defmodule AppBuilder.Windows do vcredist_path = ensure_vcredistx64() 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") - 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"]) rcedit_path = ensure_rcedit() @@ -254,14 +254,13 @@ defmodule AppBuilder.Windows do end defp ensure_magick do - url = - "https://download.imagemagick.org/ImageMagick/download/binaries/ImageMagick-7.1.0-portable-Q16-x64.zip" - - sha256 = "b61a726cea1e3bf395b9aeb323fca062f574fbf8f11f4067f88a0e6b984a1391" - AppBuilder.Utils.ensure_executable(url, sha256, "magick.exe") + System.find_executable("magick.exe") || + raise "couldn't find magick.exe in PATH to automatically convert images to .ico" 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 File.cp!(src_path, dest_path) else diff --git a/lib/livebook_app.ex b/lib/livebook_app.ex index 70dc84f44..f5553b081 100644 --- a/lib/livebook_app.ex +++ b/lib/livebook_app.ex @@ -8,16 +8,10 @@ if Mix.target() == :app do defmacro wxID_OSX_HIDE, do: 5250 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 pid = self() - options = if os() == :macos, do: [iconType: 1], else: [] + os = AppBuilder.os() + options = if os == :macos, do: [iconType: 1], else: [] # skip keyboard shortcuts 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 # but on Windows it must be either the same process OR we use the callback. - case os() do + case os do :macos -> env = :wx.get_env() @@ -92,7 +86,7 @@ if Mix.target() == :app do def menubar(app_name, menus) do 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 true = :wxMenuBar.append(menubar, menu(menu_items), title) @@ -125,13 +119,13 @@ if Mix.target() == :app do GenServer.start_link(__MODULE__, arg, name: @name) end - taskbar_icon_path = "rel/app/taskbar_icon.png" + taskbar_icon_path = "rel/app/icon.png" @external_resource taskbar_icon_path @taskbar_icon File.read!(taskbar_icon_path) @impl true def init(_) do - os = os() + os = AppBuilder.os() wx = :wx.new() AppBuilder.Wx.subscribe_to_app_events(@name) @@ -161,7 +155,7 @@ if Mix.target() == :app do # 1. MIX_TARGET=app mix phx.server # 2. mix 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) icon = :wxIcon.new(taskbar_icon_path, type: wxBITMAP_TYPE_PNG()) diff --git a/mix.exs b/mix.exs index 075d1cb29..29afb3901 100644 --- a/mix.exs +++ b/mix.exs @@ -175,7 +175,10 @@ defmodule Livebook.MixProject do @app_options [ name: "Livebook", version: @version, - logo_path: "rel/app/mac-icon.png", + icon_path: [ + macos: "rel/app/icon-macos.png", + windows: "rel/app/icon.ico" + ], additional_paths: [ "/rel/erts-#{:erlang.system_info(:version)}/bin", "/rel/vendor/elixir/bin" @@ -185,6 +188,10 @@ defmodule Livebook.MixProject do %{ name: "LiveMarkdown", extensions: ["livemd"], + icon_path: [ + macos: "rel/app/icon.png", + windows: "rel/app/icon.ico" + ], # macos specific role: "Editor" } @@ -218,10 +225,7 @@ defmodule Livebook.MixProject do end defp build_windows_installer(release) do - options = - Keyword.take(@app_options, [:name, :version, :url_schemes, :document_types]) ++ - [module: LivebookApp, logo_path: "static/images/logo.png"] - + options = Keyword.drop(@app_options, [:additional_paths]) ++ [module: LivebookApp] AppBuilder.build_windows_installer(release, options) end end diff --git a/rel/app/mac-icon.png b/rel/app/icon-macos.png similarity index 100% rename from rel/app/mac-icon.png rename to rel/app/icon-macos.png diff --git a/rel/app/icon.ico b/rel/app/icon.ico new file mode 100644 index 0000000000000000000000000000000000000000..ba93b829cfefa1644ec7eb04b59d74b265773c51 GIT binary patch literal 91550 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 a6tGAgKP3g|yo!93rt#+`8eacZ<^O*&Sg=0; literal 0 HcmV?d00001 diff --git a/rel/app/taskbar_icon.png b/rel/app/icon.png similarity index 100% rename from rel/app/taskbar_icon.png rename to rel/app/icon.png