defmodule BDS.Desktop.MenuBar do @moduledoc false use BDS.Desktop.MenuCompat alias BDS.Desktop.{ShellData, Shutdown, UILocale} alias BDS.UI.Commands alias BDS.UI.MenuBar, as: ShellMenuBar alias Desktop.OS alias Desktop.Window use Gettext, backend: BDS.Gettext def groups(opts \\ []) do opts |> ShellMenuBar.default_groups() |> Enum.map(fn group -> %{ id: group.id, label: group_label(group.id), items: Enum.map(group.items, &normalize_item/1) } end) end @impl true def mount(menu) do UILocale.put(ShellData.ui_language()) {:ok, Desktop.Menu.assign(menu, :groups, groups(native_menu_opts()))} end @impl true def render(assigns) do ~H""" <%= for group <- @groups do %> <%= for item <- group.items do %> <%= if item.separator do %>
<% else %> {item.native_label} <% end %> <% end %>
<% end %>
""" end @impl true def handle_event("quit", menu) do Shutdown.request_quit() {:noreply, menu} end def handle_event("view_on_github", menu) do OS.launch_default_browser("https://github.com/rfc1437/bDS2") {:noreply, menu} end def handle_event("open_in_browser", menu) do case BDS.Desktop.ShellCommands.execute("open_in_browser") do {:ok, %{url: url}} -> OS.launch_default_browser(url) _other -> :ok end {:noreply, menu} end def handle_event("connect_server", menu) do prompt = dgettext("ui", "Server address (user@host or user@host:port), public-key auth:") with {:ok, target} <- BDS.Desktop.Dialogs.prompt_text(prompt, "user@host"), {:ok, url} <- BDS.Desktop.RemoteConnection.connect(target) do Window.show(BDS.Desktop.MainWindow.window_id(), url) else :cancel -> :ok {:error, reason} -> BDS.Desktop.Dialogs.alert( dgettext("ui", "Connect to Server"), dgettext("ui", "Connection failed: %{reason}", reason: BDS.Desktop.RemoteConnection.format_reason(reason) ) ) end {:noreply, menu} end def handle_event("disconnect_server", menu) do :ok = BDS.Desktop.RemoteConnection.disconnect() Window.show(BDS.Desktop.MainWindow.window_id(), BDS.Desktop.url()) {:noreply, menu} end def handle_event("open_data_folder", menu) do _ = BDS.Desktop.ShellCommands.execute("open_data_folder") {:noreply, menu} end def handle_event("report_issue", menu) do OS.launch_default_browser("https://github.com/rfc1437/bDS2/issues") {:noreply, menu} end def handle_event("minimize", menu) do with_frame(&:wxTopLevelWindow.iconize(&1, iconize: true)) {:noreply, menu} end def handle_event("zoom", menu) do with_frame(fn frame -> :wxTopLevelWindow.maximize(frame, maximize: not :wxTopLevelWindow.isMaximized(frame)) end) {:noreply, menu} end def handle_event("bring_all_to_front", menu) do with_frame(&:wxWindow.raise/1) {:noreply, menu} end def handle_event(command, menu) do dispatch_shell_menu_action(command) {:noreply, menu} end @impl true def handle_info({:set_ui_locale, locale}, menu) do UILocale.put(locale) {:noreply, Desktop.Menu.assign(menu, :groups, groups(native_menu_opts()))} end def handle_info(_, menu) do {:noreply, menu} end defp dispatch_shell_menu_action(command) when is_binary(command) do with webview when not is_nil(webview) <- webview(), payload <- Jason.encode!(%{action: command}), script <- "window.dispatchEvent(new CustomEvent('bds:native-menu-action', { detail: #{payload} })); true;" do :wx.set_env(Desktop.Env.wx_env()) :wxWebView.runScript(webview, script) else _ -> :ok end end defp webview do try do Window.webview(BDS.Desktop.MainWindow.window_id()) catch :exit, _ -> nil end end defp native_menu_opts do [ dev_mode?: Application.get_env(:bds, :dev_routes, false), window_menu?: OS.type() == MacOS ] end defp with_frame(fun) do case frame() do nil -> :ok frame -> :wx.set_env(Desktop.Env.wx_env()) fun.(frame) end end defp frame do try do Window.frame(BDS.Desktop.MainWindow.window_id()) catch :exit, _ -> nil end end defp normalize_item(%{separator: true}), do: %{separator: true} defp normalize_item(item) do label = item_label(item.id) shortcut = Commands.accelerator_label(item.id) %{ id: item.id, label: label, native_label: native_label(label, shortcut), separator: false, shortcut: shortcut } end defp native_label(label, nil), do: label defp native_label(label, shortcut), do: label <> "\t" <> shortcut defp group_label(:file), do: dgettext("ui", "File") defp group_label(:edit), do: dgettext("ui", "Edit") defp group_label(:view), do: dgettext("ui", "View") defp group_label(:blog), do: dgettext("ui", "Blog") defp group_label(:window), do: dgettext("ui", "Window") defp group_label(:help), do: dgettext("ui", "Help") defp item_label(:new_post), do: dgettext("ui", "New Post") defp item_label(:import_media), do: dgettext("ui", "Import Media") defp item_label(:save), do: dgettext("ui", "Save") defp item_label(:open_in_browser), do: dgettext("ui", "Open in Browser") defp item_label(:connect_server), do: dgettext("ui", "Connect to Server…") defp item_label(:disconnect_server), do: dgettext("ui", "Disconnect from Server") defp item_label(:open_data_folder), do: dgettext("ui", "Open Data Folder") defp item_label(:close_tab), do: dgettext("ui", "Close Tab") defp item_label(:quit), do: dgettext("ui", "Quit") defp item_label(:undo), do: dgettext("ui", "Undo") defp item_label(:redo), do: dgettext("ui", "Redo") defp item_label(:cut), do: dgettext("ui", "Cut") defp item_label(:copy), do: dgettext("ui", "Copy") defp item_label(:paste), do: dgettext("ui", "Paste") defp item_label(:delete), do: dgettext("ui", "Delete") defp item_label(:select_all), do: dgettext("ui", "Select All") defp item_label(:find), do: dgettext("ui", "Find") defp item_label(:replace), do: dgettext("ui", "Replace") defp item_label(:edit_preferences), do: dgettext("ui", "Preferences") defp item_label(:view_posts), do: dgettext("ui", "Posts") defp item_label(:view_media), do: dgettext("ui", "Media") defp item_label(:toggle_sidebar), do: dgettext("ui", "Toggle Sidebar") defp item_label(:toggle_panel), do: dgettext("ui", "Toggle Panel") defp item_label(:toggle_assistant_sidebar), do: dgettext("ui", "Toggle Assistant Sidebar") defp item_label(:toggle_dev_tools), do: dgettext("ui", "Toggle Dev Tools") defp item_label(:reload), do: dgettext("ui", "Reload") defp item_label(:force_reload), do: dgettext("ui", "Force Reload") defp item_label(:reset_zoom), do: dgettext("ui", "Reset Zoom") defp item_label(:zoom_in), do: dgettext("ui", "Zoom In") defp item_label(:zoom_out), do: dgettext("ui", "Zoom Out") defp item_label(:toggle_full_screen), do: dgettext("ui", "Toggle Full Screen") defp item_label(:publish_selected), do: dgettext("ui", "Publish Selected") defp item_label(:preview_post), do: dgettext("ui", "Preview Post") defp item_label(:edit_menu), do: dgettext("ui", "Edit Menu") defp item_label(:rebuild_database), do: dgettext("ui", "Rebuild Database") defp item_label(:reindex_text), do: dgettext("ui", "Reindex Text") defp item_label(:rebuild_embedding_index), do: dgettext("ui", "Rebuild Embedding Index") defp item_label(:metadata_diff), do: dgettext("ui", "Metadata Diff") defp item_label(:regenerate_calendar), do: dgettext("ui", "Regenerate Calendar") defp item_label(:validate_translations), do: dgettext("ui", "Validate Translations") defp item_label(:fill_missing_translations), do: dgettext("ui", "Fill Missing Translations") defp item_label(:find_duplicates), do: dgettext("ui", "Find Duplicate Posts") defp item_label(:generate_sitemap), do: dgettext("ui", "Generate Site") defp item_label(:force_render_site), do: dgettext("ui", "Force Render Site") defp item_label(:validate_site), do: dgettext("ui", "Validate Site") defp item_label(:upload_site), do: dgettext("ui", "Upload Site") defp item_label(:minimize), do: dgettext("ui", "Minimize") defp item_label(:zoom), do: dgettext("ui", "Zoom") defp item_label(:bring_all_to_front), do: dgettext("ui", "Bring All to Front") defp item_label(:about), do: dgettext("ui", "About") defp item_label(:documentation), do: dgettext("ui", "Documentation") defp item_label(:api_documentation), do: dgettext("ui", "API Documentation") defp item_label(:view_on_github), do: dgettext("ui", "View on GitHub") defp item_label(:report_issue), do: dgettext("ui", "Report Issue") end