diff --git a/docs_sphinx/CMakeLists.txt b/docs_sphinx/CMakeLists.txt index 46aed52ed1..c08d8a314d 100644 --- a/docs_sphinx/CMakeLists.txt +++ b/docs_sphinx/CMakeLists.txt @@ -20,13 +20,92 @@ if(NOT SPHINX_BUILD) return() endif() -set(_SPHINX_SOURCE "${CMAKE_SOURCE_DIR}/doc") -set(_SPHINX_CONFDIR "${CMAKE_CURRENT_SOURCE_DIR}") -set(_SPHINX_OUTDIR "${CMAKE_CURRENT_BINARY_DIR}/html") +set(_SPHINX_CONFDIR "${CMAKE_CURRENT_SOURCE_DIR}") +set(_SPHINX_OUTDIR "${CMAKE_CURRENT_BINARY_DIR}/html") +set(_SPHINX_INPUT_ROOT "${CMAKE_CURRENT_BINARY_DIR}/docs_sphinx_input") + +# --------------------------------------------------------------------------- +# Stage a merged input tree at ${CMAKE_BINARY_DIR}/docs_sphinx_input/. +# Sphinx requires a single srcdir, but the wrapper needs to see both +# opencv/doc/tutorials/ and (optionally) opencv_contrib/modules//tutorials/. +# We symlink each main module subtree under tutorials/ and each enabled contrib +# module subtree under tutorials_contrib/, then auto-generate the contrib root +# page — the same pattern opencv/doc/CMakeLists.txt already uses for Doxygen. +# Nothing under opencv/doc/ or opencv_contrib/modules/ is modified. +# --------------------------------------------------------------------------- +set(_SPHINX_INPUT_TUTORIALS "${_SPHINX_INPUT_ROOT}/tutorials") +set(_SPHINX_INPUT_CONTRIB "${_SPHINX_INPUT_ROOT}/tutorials_contrib") +file(REMOVE_RECURSE "${_SPHINX_INPUT_ROOT}") +file(MAKE_DIRECTORY "${_SPHINX_INPUT_TUTORIALS}") + +# Main tree: symlink the master file + each main module subtree. +file(CREATE_LINK + "${CMAKE_SOURCE_DIR}/doc/tutorials/tutorials.markdown" + "${_SPHINX_INPUT_TUTORIALS}/tutorials.markdown" + SYMBOLIC COPY_ON_ERROR) +file(GLOB _main_tutorial_children + RELATIVE "${CMAKE_SOURCE_DIR}/doc/tutorials" + "${CMAKE_SOURCE_DIR}/doc/tutorials/*") +foreach(_d ${_main_tutorial_children}) + if(IS_DIRECTORY "${CMAKE_SOURCE_DIR}/doc/tutorials/${_d}") + file(CREATE_LINK + "${CMAKE_SOURCE_DIR}/doc/tutorials/${_d}" + "${_SPHINX_INPUT_TUTORIALS}/${_d}" + SYMBOLIC COPY_ON_ERROR) + endif() +endforeach() + +# Contrib tree: only when OPENCV_EXTRA_MODULES_PATH is set, matching the legacy +# Doxygen contrib build's activation gate exactly. Stage every contrib module +# that has a tutorials/ dir — the canonical "which modules to compile" list +# lives in conf.py's CONTRIB_MODULES, exactly parallel to DOC_MODULES for main. +# Staged-but-unselected modules have their @subpage entries silently dropped +# by the toctree machinery (conf.py only populates _ANCHOR_TO_DOC for enabled +# modules), so listing them all in contrib_root.markdown is safe and means +# adding a new contrib module is a one-line conf.py edit (no reconfigure). +if(OPENCV_EXTRA_MODULES_PATH AND EXISTS "${OPENCV_EXTRA_MODULES_PATH}") + file(MAKE_DIRECTORY "${_SPHINX_INPUT_CONTRIB}") + set(_contrib_root_md "${_SPHINX_INPUT_CONTRIB}/contrib_root.markdown") + file(WRITE "${_contrib_root_md}" + "Tutorials for contrib modules {#tutorial_contrib_root}\n" + "=============================\n\n") + file(GLOB _contrib_subdirs RELATIVE "${OPENCV_EXTRA_MODULES_PATH}" + "${OPENCV_EXTRA_MODULES_PATH}/*") + foreach(_m ${_contrib_subdirs}) + set(_tut_dir "${OPENCV_EXTRA_MODULES_PATH}/${_m}/tutorials") + if(IS_DIRECTORY "${_tut_dir}") + file(CREATE_LINK "${_tut_dir}" "${_SPHINX_INPUT_CONTRIB}/${_m}" + SYMBOLIC COPY_ON_ERROR) + # Match every top-level *.markdown in /tutorials/, the same shape the + # legacy Doxygen build uses (opencv/doc/CMakeLists.txt) — many contrib + # modules use names like alphamat_tutorial.markdown or fuzzy.markdown + # instead of the table_of_content_.markdown convention. + file(GLOB _tocs RELATIVE "${_tut_dir}" "${_tut_dir}/*.markdown") + foreach(_t ${_tocs}) + file(STRINGS "${_tut_dir}/${_t}" _id LIMIT_COUNT 1 REGEX ".*\\{#[^}]+\\}") + string(REGEX REPLACE ".*\\{#([^}]+)\\}.*" "\\1" _id "${_id}") + if(_id) + file(APPEND "${_contrib_root_md}" "- ${_m}. @subpage ${_id}\n") + endif() + endforeach() + endif() + endforeach() +endif() + +# Forward staging info to conf.py via env. OPENCV_CONTRIB_MODULES is NOT +# forwarded — conf.py's default list wins unless the caller sets it in their +# shell env (OPENCV_CONTRIB_MODULES=ml cmake --build ...) to override per-build. +set(_SPHINX_ENV + "OPENCV_SPHINX_INPUT_ROOT=${_SPHINX_INPUT_ROOT}" + "OPENCV_CONTRIB_ROOT=${OPENCV_EXTRA_MODULES_PATH}" +) add_custom_target(sphinx COMMAND ${CMAKE_COMMAND} -E make_directory ${_SPHINX_OUTDIR} - COMMAND ${SPHINX_BUILD} -c ${_SPHINX_CONFDIR} ${_SPHINX_SOURCE} ${_SPHINX_OUTDIR} + COMMAND ${CMAKE_COMMAND} -E env ${_SPHINX_ENV} + ${SPHINX_BUILD} -c ${_SPHINX_CONFDIR} + ${_SPHINX_INPUT_ROOT} + ${_SPHINX_OUTDIR} WORKING_DIRECTORY ${_SPHINX_CONFDIR} COMMENT "Building Sphinx HTML site -> ${_SPHINX_OUTDIR}" VERBATIM) diff --git a/docs_sphinx/_static/custom.css b/docs_sphinx/_static/custom.css index fe2d4ed5aa..0201c2a85a 100644 --- a/docs_sphinx/_static/custom.css +++ b/docs_sphinx/_static/custom.css @@ -417,3 +417,92 @@ dl.cpp.function .sig-prename.descclassname { color: var(--pst-color-text-muted); /* --- MathJax inline alignment tweak ----------------------------------- */ mjx-container.MathJax:not([display="true"]) { vertical-align: -0.15em !important; } + +/* --- Copy-to-clipboard button ----------------------------------------- + * Provided by the `sphinx-copybutton` extension, which injects + * `