diff --git a/CMakeLists.txt b/CMakeLists.txt index 837e39df..35146f3f 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -588,6 +588,11 @@ message("") message(" musrview (ROOT7 web graphics):") if (MUSRVIEW_HAS_QT6WEB) message(" embedded window (ROOT qt6web) : yes") + if (MUSRVIEW_HAS_QT_APP_ICON) + message(" application icon (Qt6Widgets) : yes") + else () + message(" application icon (Qt6Widgets) : no, Qt6Widgets not found") + endif () else () message(" embedded window (ROOT qt6web) : no, will use the standard browser") endif () diff --git a/cmake/CPackOptions.cmake.in b/cmake/CPackOptions.cmake.in index 63ae75bb..e76f9f78 100644 --- a/cmake/CPackOptions.cmake.in +++ b/cmake/CPackOptions.cmake.in @@ -17,7 +17,7 @@ set (CPACK_RESOURCE_FILE_LICENSE "@PROJECT_SOURCE_DIR@/COPYING") set (CPACK_RESOURCE_FILE_README "@PROJECT_SOURCE_DIR@/README.md") set (CPACK_RESOURCE_FILE_WELCOME "@PROJECT_SOURCE_DIR@/cmake/welcome.txt") -set (CPACK_RPM_EXCLUDE_FROM_AUTO_FILELIST_ADDITION "@CMAKE_INSTALL_PREFIX@;@CMAKE_INSTALL_PREFIX@/bin;@CMAKE_INSTALL_PREFIX@/include;@CMAKE_INSTALL_PREFIX@/share;@CMAKE_INSTALL_PREFIX@/lib64;@CMAKE_INSTALL_PREFIX@/lib;@CMAKE_INSTALL_PREFIX@/pkgconfig;@CMAKE_INSTALL_PREFIX@/share/applications;@CMAKE_INSTALL_PREFIX@/share/icons;@CMAKE_INSTALL_PREFIX@/share/icons/hicolor;@CMAKE_INSTALL_PREFIX@/share/icons/hicolor/48x48;@CMAKE_INSTALL_PREFIX@/share/icons/hicolor/48x48/apps") +set (CPACK_RPM_EXCLUDE_FROM_AUTO_FILELIST_ADDITION "@CMAKE_INSTALL_PREFIX@;@CMAKE_INSTALL_PREFIX@/bin;@CMAKE_INSTALL_PREFIX@/include;@CMAKE_INSTALL_PREFIX@/share;@CMAKE_INSTALL_PREFIX@/lib64;@CMAKE_INSTALL_PREFIX@/lib;@CMAKE_INSTALL_PREFIX@/pkgconfig;@CMAKE_INSTALL_PREFIX@/share/applications;@CMAKE_INSTALL_PREFIX@/share/icons;@CMAKE_INSTALL_PREFIX@/share/icons/hicolor;@CMAKE_INSTALL_PREFIX@/share/icons/hicolor/48x48;@CMAKE_INSTALL_PREFIX@/share/icons/hicolor/48x48/apps;@CMAKE_INSTALL_PREFIX@/share/icons/hicolor/512x512;@CMAKE_INSTALL_PREFIX@/share/icons/hicolor/512x512/apps;@CMAKE_INSTALL_PREFIX@/share/icons/hicolor/scalable;@CMAKE_INSTALL_PREFIX@/share/icons/hicolor/scalable/apps") # we do not have any absolute paths, so do not need DESTDIR set (CPACK_SET_DESTDIR "OFF") diff --git a/cmake/musrfit-root6.spec.in b/cmake/musrfit-root6.spec.in index 85657332..a45888b6 100644 --- a/cmake/musrfit-root6.spec.in +++ b/cmake/musrfit-root6.spec.in @@ -93,6 +93,10 @@ rm -rf $RPM_BUILD_ROOT %doc %_prefix/share/doc/musrfit %{_datadir}/applications/musredit.desktop %{_datadir}/icons/hicolor/48x48/apps/musredit.png +%{_datadir}/applications/musrview.desktop +%{_datadir}/icons/hicolor/512x512/apps/musrview.png +%{_datadir}/icons/hicolor/scalable/apps/musrview.svg +%_prefix/share/musrfit diff --git a/cmake/musrview.desktop b/cmake/musrview.desktop new file mode 100644 index 00000000..7951308c --- /dev/null +++ b/cmake/musrview.desktop @@ -0,0 +1,12 @@ +[Desktop Entry] +Name=musrview +Comment=viewer for musrfit data and fit results +GenericName=musrfit viewer +Exec=musrview %f +Icon=musrview +Terminal=false +Type=Application +NoDisplay=true +StartupWMClass=musrview +Categories=Science;DataVisualization; +Keywords=muSR;fitting;musrfit;physics;muon; diff --git a/src/CMakeLists.txt b/src/CMakeLists.txt index 58c7058a..8af496df 100644 --- a/src/CMakeLists.txt +++ b/src/CMakeLists.txt @@ -141,10 +141,61 @@ target_link_libraries(musrview FFTW3::FFTW3 ${ROOT_LIBRARIES} ROOT::ROOTWebDispl #--- MUSRVIEW_WEB_DIR (see src/classes/CMakeLists.txt) points here. During #--- development the environment variable MUSRFIT_WEB_DIR overrides it. ------- install( - FILES ${CMAKE_SOURCE_DIR}/src/musrview_web/musrview.html + FILES + ${CMAKE_SOURCE_DIR}/src/musrview_web/musrview.html + ${CMAKE_SOURCE_DIR}/src/musrview_web/musrview.png DESTINATION ${CMAKE_INSTALL_DATAROOTDIR}/musrfit/musrview ) +#--- application icon of the embedded window: ROOT creates the QApplication, +#--- musrview only sets its icon, hence it has to use the very same Qt6 ROOT's +#--- qt6web display links against. Without it macOS shows the generic 'exec'. +if (MUSRVIEW_HAS_QT6WEB) + find_package(Qt6 COMPONENTS Widgets QUIET) + if (TARGET Qt6::Widgets) + # QT_NO_KEYWORDS: Qt's emit/signals/slots macros break ROOT's TGButton.h (PMsgBox) + target_compile_definitions(musrview PRIVATE MUSRVIEW_QT_APP_ICON QT_NO_KEYWORDS) + target_link_libraries(musrview Qt6::Widgets) + set(MUSRVIEW_HAS_QT_APP_ICON TRUE CACHE INTERNAL "musrview sets the icon of its embedded window") + else () + set(MUSRVIEW_HAS_QT_APP_ICON FALSE CACHE INTERNAL "musrview sets the icon of its embedded window") + endif () +endif () + +#--- macOS: musrview is a plain executable, not an application bundle. An +#--- Info.plist embedded into the binary nevertheless gives it a proper name +#--- (Dock, app menu) and bundle identifier. ---------------------------------- +if (APPLE) + configure_file( + ${CMAKE_SOURCE_DIR}/src/musrview_web/musrview-Info.plist.in + ${CMAKE_CURRENT_BINARY_DIR}/musrview-Info.plist + @ONLY + ) + target_link_options(musrview PRIVATE + "LINKER:-sectcreate,__TEXT,__info_plist,${CMAKE_CURRENT_BINARY_DIR}/musrview-Info.plist") + set_property(TARGET musrview APPEND PROPERTY + LINK_DEPENDS ${CMAKE_CURRENT_BINARY_DIR}/musrview-Info.plist) +endif () + +#--- Linux: on Wayland the compositor ignores the icon a window sets and takes +#--- it from the .desktop file matching the app-id instead (musrview.cpp sets +#--- it via setDesktopFileName). NoDisplay keeps it out of the launchers, since +#--- musrview needs an msr-file to start with. -------------------------------- +if (UNIX AND NOT APPLE) + install( + FILES ${CMAKE_SOURCE_DIR}/cmake/musrview.desktop + DESTINATION ${CMAKE_INSTALL_DATADIR}/applications + ) + install( + FILES ${CMAKE_SOURCE_DIR}/src/musrview_web/musrview.png + DESTINATION ${CMAKE_INSTALL_DATADIR}/icons/hicolor/512x512/apps + ) + install( + FILES ${CMAKE_SOURCE_DIR}/src/musrview_web/musrview.svg + DESTINATION ${CMAKE_INSTALL_DATADIR}/icons/hicolor/scalable/apps + ) +endif () + add_executable(write_musrRoot_runHeader write_musrRoot_runHeader.cpp) target_compile_options(write_musrRoot_runHeader BEFORE PRIVATE "-DHAVE_CONFIG_H" "${HAVE_GIT_REV_H}") target_include_directories(write_musrRoot_runHeader diff --git a/src/classes/PMusrWebCanvas.cpp b/src/classes/PMusrWebCanvas.cpp index c8ceb078..2a9f04f0 100644 --- a/src/classes/PMusrWebCanvas.cpp +++ b/src/classes/PMusrWebCanvas.cpp @@ -1097,6 +1097,20 @@ void PMusrWebCanvas::ProcessMessage(unsigned connid, const std::string &msg) } } +//-------------------------------------------------------------------------- +// WebDir (public, static) +//-------------------------------------------------------------------------- +/** + *
Locates the directory holding the musrview web assets, i.e. the page
+ * and the application icon, see WebDirCandidates() for the search order.
+ *
+ * \return the directory, or an empty string if none was found
+ */
+std::string PMusrWebCanvas::WebDir()
+{
+ return FindWebDir();
+}
+
//--------------------------------------------------------------------------
// Show (public)
//--------------------------------------------------------------------------
diff --git a/src/include/PMusrWebCanvas.h b/src/include/PMusrWebCanvas.h
index 8f94b76e..23403a77 100644
--- a/src/include/PMusrWebCanvas.h
+++ b/src/include/PMusrWebCanvas.h
@@ -113,6 +113,9 @@ class PMusrWebCanvas
/// Opens the window. \p where is "" (standard browser), "qt6", "chrome", ...
Bool_t Show(const std::string &where);
+ /// Directory holding the musrview web assets (page, application icon),
+ /// empty if none was found.
+ static std::string WebDir();
/// True once the user has closed the window.
Bool_t IsDone() const { return fDone; }
/// Closes the window and drops the connection. Needed so that musrview can
diff --git a/src/musrview.cpp b/src/musrview.cpp
index 82261155..a64a35c8 100644
--- a/src/musrview.cpp
+++ b/src/musrview.cpp
@@ -49,6 +49,11 @@
#include Gives the embedded window a proper application name and icon.
+ *
+ * The QApplication behind the embedded display is created by ROOT the
+ * moment the first window is shown, and nobody sets an icon on it. Since
+ * musrview is a plain executable rather than an application bundle, macOS
+ * then shows the generic 'exec' icon in the Dock and the window switcher.
+ * QApplication::setWindowIcon() replaces the Dock icon as well. Hence this
+ * has to be called after the first PMusrWebCanvas::Show("qt6").
+ *
+ * On X11 the window icon is all it takes. Wayland compositors ignore it and
+ * take the icon from the .desktop file matching the app-id instead, which is
+ * what setDesktopFileName() provides (cmake/musrview.desktop).
+ */
+void musrview_set_app_icon()
+{
+#ifdef MUSRVIEW_QT_APP_ICON
+ if (QApplication::instance() == nullptr)
+ return;
+
+ QApplication::setApplicationName("musrview");
+ QApplication::setApplicationDisplayName("musrview");
+ QApplication::setDesktopFileName("musrview");
+
+ const std::string webDir = musrview::PMusrWebCanvas::WebDir();
+ if (webDir.empty())
+ return;
+
+ const QIcon icon(QString::fromStdString(webDir + "/musrview.png"));
+ if (!icon.isNull())
+ QApplication::setWindowIcon(icon);
+#endif
+}
+
//--------------------------------------------------------------------------
/**
* The musrview program is used to show muSR fit results in graphical form.
@@ -486,6 +526,9 @@ int main(int argc, char *argv[])
}
}
+ if (ok && (where == "qt6"))
+ musrview_set_app_icon();
+
if (ok) {
// run until every window has been closed, or until the timeout expired
const auto start = std::chrono::steady_clock::now();
diff --git a/src/musrview_web/musrview-Info.plist.in b/src/musrview_web/musrview-Info.plist.in
new file mode 100644
index 00000000..b1ed9d89
--- /dev/null
+++ b/src/musrview_web/musrview-Info.plist.in
@@ -0,0 +1,27 @@
+
+
+
+