diff --git a/Source/CMakeLists.txt b/Source/CMakeLists.txt
index 9a18184fd3bd16a68a5acb4392de9557724b0f23..0142c07ecb33ded1b187862370032c108c9a05c1 100644
--- a/Source/CMakeLists.txt
+++ b/Source/CMakeLists.txt
@@ -432,6 +432,8 @@ set(SRCS
   cmQtAutoMocUic.h
   cmQtAutoRcc.cxx
   cmQtAutoRcc.h
+  cmQtAutoUicHelpers.cxx
+  cmQtAutoUicHelpers.h
   cmRST.cxx
   cmRST.h
   cmRuntimeDependencyArchive.cxx
diff --git a/Source/cmQtAutoGen.cxx b/Source/cmQtAutoGen.cxx
index 57fcd2da7688bc430cca22982e864c32f6dfcd2f..898d862221f6f6862caa303c566ced99abf10f90 100644
--- a/Source/cmQtAutoGen.cxx
+++ b/Source/cmQtAutoGen.cxx
@@ -384,3 +384,39 @@ bool cmQtAutoGen::RccLister::list(std::string const& qrcFile,
   }
   return true;
 }
+
+bool cmQtAutoGen::FileRead(std::string& content, std::string const& filename,
+                           std::string* error)
+{
+  content.clear();
+  if (!cmSystemTools::FileExists(filename, true)) {
+    if (error != nullptr) {
+      *error = "Not a file.";
+    }
+    return false;
+  }
+
+  unsigned long const length = cmSystemTools::FileLength(filename);
+  cmsys::ifstream ifs(filename.c_str(), (std::ios::in | std::ios::binary));
+
+  // Use lambda to save destructor calls of ifs
+  return [&ifs, length, &content, error]() -> bool {
+    if (!ifs) {
+      if (error != nullptr) {
+        *error = "Opening the file for reading failed.";
+      }
+      return false;
+    }
+    content.reserve(length);
+    using IsIt = std::istreambuf_iterator<char>;
+    content.assign(IsIt{ ifs }, IsIt{});
+    if (!ifs) {
+      content.clear();
+      if (error != nullptr) {
+        *error = "Reading from the file failed.";
+      }
+      return false;
+    }
+    return true;
+  }();
+}
diff --git a/Source/cmQtAutoGen.h b/Source/cmQtAutoGen.h
index 466a954a981c59285af8f2fcf12cdc097ecbc3b3..b9ae360291eb10cfbe32b7d9a0b171e947e36fe0 100644
--- a/Source/cmQtAutoGen.h
+++ b/Source/cmQtAutoGen.h
@@ -100,6 +100,9 @@ public:
                               std::vector<std::string> const& newOpts,
                               bool isQt5);
 
+  static bool FileRead(std::string& content, std::string const& filename,
+                       std::string* error = nullptr);
+
   /** @class RccLister
    * @brief Lists files in qrc resource files
    */
diff --git a/Source/cmQtAutoGenInitializer.cxx b/Source/cmQtAutoGenInitializer.cxx
index 2894201c9b35513a7342c1319fe11ad03c3bc88e..9d8086397ca17d75de4deee4635b6573a2472c7a 100644
--- a/Source/cmQtAutoGenInitializer.cxx
+++ b/Source/cmQtAutoGenInitializer.cxx
@@ -902,6 +902,13 @@ bool cmQtAutoGenInitializer::InitScanFiles()
   // The reason is that their file names might be discovered from source files
   // at generation time.
   if (this->MocOrUicEnabled()) {
+    std::set<std::string> uicIncludes;
+    auto collectUicIncludes = [&](std::unique_ptr<cmSourceFile> const& sf) {
+      std::string content;
+      FileRead(content, sf->GetFullPath());
+      this->AutoUicHelpers.CollectUicIncludes(uicIncludes, content);
+    };
+
     for (const auto& sf : this->Makefile->GetSourceFiles()) {
       // sf->GetExtension() is only valid after sf->ResolveFullPath() ...
       // Since we're iterating over source files that might be not in the
@@ -914,6 +921,10 @@ bool cmQtAutoGenInitializer::InitScanFiles()
       std::string const& extLower =
         cmSystemTools::LowerCase(sf->GetExtension());
 
+      bool const skipAutogen = sf->GetPropertyAsBool(kw.SKIP_AUTOGEN);
+      bool const skipUic =
+        (skipAutogen || sf->GetPropertyAsBool(kw.SKIP_AUTOUIC) ||
+         !this->Uic.Enabled);
       if (cm->IsAHeaderExtension(extLower)) {
         if (!cm::contains(this->AutogenTarget.Headers, sf.get())) {
           auto muf = makeMUFile(sf.get(), fullPath, {}, false);
@@ -921,6 +932,9 @@ bool cmQtAutoGenInitializer::InitScanFiles()
             addMUHeader(std::move(muf), extLower);
           }
         }
+        if (!skipUic && !sf->GetIsGenerated()) {
+          collectUicIncludes(sf);
+        }
       } else if (cm->IsACLikeSourceExtension(extLower)) {
         if (!cm::contains(this->AutogenTarget.Sources, sf.get())) {
           auto muf = makeMUFile(sf.get(), fullPath, {}, false);
@@ -928,11 +942,11 @@ bool cmQtAutoGenInitializer::InitScanFiles()
             addMUSource(std::move(muf));
           }
         }
+        if (!skipUic && !sf->GetIsGenerated()) {
+          collectUicIncludes(sf);
+        }
       } else if (this->Uic.Enabled && (extLower == kw.ui)) {
         // .ui file
-        bool const skipAutogen = sf->GetPropertyAsBool(kw.SKIP_AUTOGEN);
-        bool const skipUic =
-          (skipAutogen || sf->GetPropertyAsBool(kw.SKIP_AUTOUIC));
         if (!skipUic) {
           // Check if the .ui file has uic options
           std::string const uicOpts = sf->GetSafeProperty(kw.AUTOUIC_OPTIONS);
@@ -942,35 +956,22 @@ bool cmQtAutoGenInitializer::InitScanFiles()
             this->Uic.UiFilesWithOptions.emplace_back(fullPath,
                                                       cmExpandedList(uicOpts));
           }
-
-          auto uiHeaderRelativePath = cmSystemTools::RelativePath(
-            this->LocalGen->GetCurrentSourceDirectory(),
-            cmSystemTools::GetFilenamePath(fullPath));
-
-          // Avoid creating a path containing adjacent slashes
-          if (!uiHeaderRelativePath.empty() &&
-              uiHeaderRelativePath.back() != '/') {
-            uiHeaderRelativePath += '/';
-          }
-
-          auto uiHeaderFilePath = cmStrCat(
-            '/', uiHeaderRelativePath, "ui_"_s,
-            cmSystemTools::GetFilenameWithoutLastExtension(fullPath), ".h"_s);
-
-          ConfigString uiHeader;
-          std::string uiHeaderGenex;
-          this->ConfigFileNamesAndGenex(
-            uiHeader, uiHeaderGenex, cmStrCat(this->Dir.Build, "/include"_s),
-            uiHeaderFilePath);
-
-          this->Uic.UiHeaders.emplace_back(
-            std::make_pair(uiHeader, uiHeaderGenex));
         } else {
           // Register skipped .ui file
           this->Uic.SkipUi.insert(fullPath);
         }
       }
     }
+
+    for (const auto& include : uicIncludes) {
+      ConfigString uiHeader;
+      std::string uiHeaderGenex;
+      this->ConfigFileNamesAndGenex(uiHeader, uiHeaderGenex,
+                                    cmStrCat(this->Dir.Build, "/include"_s),
+                                    cmStrCat("/"_s, include));
+      this->Uic.UiHeaders.emplace_back(
+        std::make_pair(uiHeader, uiHeaderGenex));
+    }
   }
 
   // Process GENERATED sources and headers
diff --git a/Source/cmQtAutoGenInitializer.h b/Source/cmQtAutoGenInitializer.h
index e76817b9c430082e8abb699c2143f60c497cdc81..3ec87d212d4c73e9eb5c3fa7abf223d8b46b9275 100644
--- a/Source/cmQtAutoGenInitializer.h
+++ b/Source/cmQtAutoGenInitializer.h
@@ -17,6 +17,7 @@
 
 #include "cmFilePathChecksum.h"
 #include "cmQtAutoGen.h"
+#include "cmQtAutoUicHelpers.h"
 
 class cmGeneratorTarget;
 class cmGlobalGenerator;
@@ -170,6 +171,7 @@ private:
   std::string ConfigDefault;
   std::vector<std::string> ConfigsList;
   std::string TargetsFolder;
+  cmQtAutoUicHelpers AutoUicHelpers;
 
   /** Common directories.  */
   struct
diff --git a/Source/cmQtAutoGenerator.cxx b/Source/cmQtAutoGenerator.cxx
index 568926e3c5d861eb0b947337b94f1b560006e2c8..0c6b5e6dc53c52a73c0e32afba36e82dc833ea67 100644
--- a/Source/cmQtAutoGenerator.cxx
+++ b/Source/cmQtAutoGenerator.cxx
@@ -121,43 +121,6 @@ bool cmQtAutoGenerator::MakeParentDirectory(std::string const& filename)
   return success;
 }
 
-bool cmQtAutoGenerator::FileRead(std::string& content,
-                                 std::string const& filename,
-                                 std::string* error)
-{
-  content.clear();
-  if (!cmSystemTools::FileExists(filename, true)) {
-    if (error != nullptr) {
-      *error = "Not a file.";
-    }
-    return false;
-  }
-
-  unsigned long const length = cmSystemTools::FileLength(filename);
-  cmsys::ifstream ifs(filename.c_str(), (std::ios::in | std::ios::binary));
-
-  // Use lambda to save destructor calls of ifs
-  return [&ifs, length, &content, error]() -> bool {
-    if (!ifs) {
-      if (error != nullptr) {
-        *error = "Opening the file for reading failed.";
-      }
-      return false;
-    }
-    content.reserve(length);
-    using IsIt = std::istreambuf_iterator<char>;
-    content.assign(IsIt{ ifs }, IsIt{});
-    if (!ifs) {
-      content.clear();
-      if (error != nullptr) {
-        *error = "Reading from the file failed.";
-      }
-      return false;
-    }
-    return true;
-  }();
-}
-
 bool cmQtAutoGenerator::FileWrite(std::string const& filename,
                                   std::string const& content,
                                   std::string* error)
diff --git a/Source/cmQtAutoGenerator.h b/Source/cmQtAutoGenerator.h
index 5c3a8adccb4980ce162ec3845900555cefc7acf7..66399d7e8aeabce047e11d08029a448c0894f682 100644
--- a/Source/cmQtAutoGenerator.h
+++ b/Source/cmQtAutoGenerator.h
@@ -70,8 +70,6 @@ public:
 
   // -- File system methods
   static bool MakeParentDirectory(std::string const& filename);
-  static bool FileRead(std::string& content, std::string const& filename,
-                       std::string* error = nullptr);
   static bool FileWrite(std::string const& filename,
                         std::string const& content,
                         std::string* error = nullptr);
diff --git a/Source/cmQtAutoMocUic.cxx b/Source/cmQtAutoMocUic.cxx
index 2753fd57c021cf1a38355ed8c9b58f3e987e0537..86d54f9e1a7cebee2c6e3234f6a1c26e4f1f27bd 100644
--- a/Source/cmQtAutoMocUic.cxx
+++ b/Source/cmQtAutoMocUic.cxx
@@ -30,6 +30,7 @@
 #include "cmGeneratedFileStream.h"
 #include "cmQtAutoGen.h"
 #include "cmQtAutoGenerator.h"
+#include "cmQtAutoUicHelpers.h"
 #include "cmStringAlgorithms.h"
 #include "cmSystemTools.h"
 #include "cmWorkerPool.h"
@@ -281,7 +282,7 @@ public:
     std::vector<std::string> Options;
     std::unordered_map<std::string, UiFile> UiFiles;
     std::vector<std::string> SearchPaths;
-    cmsys::RegularExpression RegExpInclude;
+    cmQtAutoUicHelpers AutoUicHelpers;
   };
 
   /** Uic shared variables.  */
@@ -761,11 +762,7 @@ std::string cmQtAutoMocUicT::MocSettingsT::MacrosString() const
   return res;
 }
 
-cmQtAutoMocUicT::UicSettingsT::UicSettingsT()
-{
-  this->RegExpInclude.compile("(^|\n)[ \t]*#[ \t]*include[ \t]+"
-                              "[\"<](([^ \">]+/)?ui_[^ \">/]+\\.h)[\">]");
-}
+cmQtAutoMocUicT::UicSettingsT::UicSettingsT() = default;
 
 cmQtAutoMocUicT::UicSettingsT::~UicSettingsT() = default;
 
@@ -1056,16 +1053,7 @@ void cmQtAutoMocUicT::JobParseT::UicIncludes()
   }
 
   std::set<std::string> includes;
-  {
-    const char* contentChars = this->Content.c_str();
-    cmsys::RegularExpression const& regExp = this->UicConst().RegExpInclude;
-    cmsys::RegularExpressionMatch match;
-    while (regExp.find(contentChars, match)) {
-      includes.emplace(match.match(2));
-      // Forward content pointer
-      contentChars += match.end();
-    }
-  }
+  this->UicConst().AutoUicHelpers.CollectUicIncludes(includes, this->Content);
   this->CreateKeys(this->FileHandle->ParseData->Uic.Include, includes,
                    UiUnderscoreLength);
 }
diff --git a/Source/cmQtAutoUicHelpers.cxx b/Source/cmQtAutoUicHelpers.cxx
new file mode 100644
index 0000000000000000000000000000000000000000..751ae08575a807b8bec169d5896437001d9cae33
--- /dev/null
+++ b/Source/cmQtAutoUicHelpers.cxx
@@ -0,0 +1,25 @@
+/* Distributed under the OSI-approved BSD 3-Clause License.  See accompanying
+   file Copyright.txt or https://cmake.org/licensing for details.  */
+#include "cmQtAutoUicHelpers.h"
+
+cmQtAutoUicHelpers::cmQtAutoUicHelpers()
+{
+  RegExpInclude.compile("(^|\n)[ \t]*#[ \t]*include[ \t]+"
+                        "[\"<](([^ \">]+/)?ui_[^ \">/]+\\.h)[\">]");
+}
+
+void cmQtAutoUicHelpers::CollectUicIncludes(std::set<std::string>& includes,
+                                            const std::string& content) const
+{
+  if (content.find("ui_") == std::string::npos) {
+    return;
+  }
+
+  const char* contentChars = content.c_str();
+  cmsys::RegularExpressionMatch match;
+  while (this->RegExpInclude.find(contentChars, match)) {
+    includes.emplace(match.match(2));
+    // Forward content pointer
+    contentChars += match.end();
+  }
+}
diff --git a/Source/cmQtAutoUicHelpers.h b/Source/cmQtAutoUicHelpers.h
new file mode 100644
index 0000000000000000000000000000000000000000..6b09a31daaadbf1c32da782cfbe4d69343ebe845
--- /dev/null
+++ b/Source/cmQtAutoUicHelpers.h
@@ -0,0 +1,20 @@
+/* Distributed under the OSI-approved BSD 3-Clause License.  See accompanying
+   file Copyright.txt or https://cmake.org/licensing for details.  */
+#pragma once
+
+#include <set>
+#include <string>
+
+#include "cmsys/RegularExpression.hxx"
+
+class cmQtAutoUicHelpers
+{
+public:
+  cmQtAutoUicHelpers();
+  virtual ~cmQtAutoUicHelpers() = default;
+  void CollectUicIncludes(std::set<std::string>& includes,
+                          const std::string& content) const;
+
+private:
+  cmsys::RegularExpression RegExpInclude;
+};
diff --git a/Tests/QtAutogen/RerunUicOnFileChange/CMakeLists.txt b/Tests/QtAutogen/RerunUicOnFileChange/CMakeLists.txt
index 1f636af451fb0e5f9429478f67fbcf7ba2ba529c..a9ccece5d5c7de280e2e3cde613fe312f66f156c 100644
--- a/Tests/QtAutogen/RerunUicOnFileChange/CMakeLists.txt
+++ b/Tests/QtAutogen/RerunUicOnFileChange/CMakeLists.txt
@@ -27,10 +27,12 @@ endmacro()
 
 configure_file("${testProjectTemplateDir}/mocwidget.h" "${testProjectSrc}/mocwidget.h" COPYONLY)
 configure_file("${testProjectTemplateDir}/main.cpp" "${testProjectSrc}/main.cpp" COPYONLY)
+configure_file("${testProjectTemplateDir}/subdir/subdircheck.cpp" "${testProjectSrc}/subdir/subdircheck.cpp" COPYONLY)
 configure_file("${testProjectTemplateDir}/CMakeLists.txt.in" "${testProjectSrc}/CMakeLists.txt" @ONLY)
 
 set(Num 1)
 configure_file("${testProjectTemplateDir}/mainwindow.ui.in" "${testProjectSrc}/mainwindow.ui" @ONLY)
+configure_file("${testProjectTemplateDir}/subdir/mainwindowsubdir.ui.in" "${testProjectSrc}/subdir/mainwindowsubdir.ui" @ONLY)
 
 if(CMAKE_GENERATOR_INSTANCE)
     set(_D_CMAKE_GENERATOR_INSTANCE "-DCMAKE_GENERATOR_INSTANCE=${CMAKE_GENERATOR_INSTANCE}")
@@ -94,6 +96,7 @@ sleep()
 
 set(Num 2)
 configure_file("${testProjectTemplateDir}/mainwindow.ui.in" "${testProjectSrc}/mainwindow.ui" @ONLY)
+configure_file("${testProjectTemplateDir}/subdir/mainwindowsubdir.ui.in" "${testProjectSrc}/subdir/mainwindowsubdir.ui" @ONLY)
 rebuild(2)
 
 execute_process(COMMAND "${testProjectBinDir}/${extra_bin_path}UicOnFileChange" RESULT_VARIABLE result)
diff --git a/Tests/QtAutogen/RerunUicOnFileChange/UicOnFileChange/CMakeLists.txt.in b/Tests/QtAutogen/RerunUicOnFileChange/UicOnFileChange/CMakeLists.txt.in
index fa9dd6bee3239b01ea10450191c6bfe00500e294..2a1998d10fee4377d5965ad6e12564e1fab6575e 100644
--- a/Tests/QtAutogen/RerunUicOnFileChange/UicOnFileChange/CMakeLists.txt.in
+++ b/Tests/QtAutogen/RerunUicOnFileChange/UicOnFileChange/CMakeLists.txt.in
@@ -6,6 +6,8 @@ include("@CMAKE_CURRENT_LIST_DIR@/../AutogenGuiTest.cmake")
 # Enable CMAKE_AUTOUIC for all targets
 set(CMAKE_AUTOUIC ON)
 
-add_executable(UicOnFileChange main.cpp mainwindow.ui)
+add_executable(UicOnFileChange main.cpp mainwindow.ui
+    subdir/subdircheck.cpp subdir/mainwindowsubdir.ui
+)
 target_include_directories(UicOnFileChange PRIVATE ${CMAKE_CURRENT_SOURCE_DIR})
 target_link_libraries(UicOnFileChange ${QT_QTCORE_TARGET} ${QT_LIBRARIES})
diff --git a/Tests/QtAutogen/RerunUicOnFileChange/UicOnFileChange/main.cpp b/Tests/QtAutogen/RerunUicOnFileChange/UicOnFileChange/main.cpp
index fd810fa24727e4f2880d0bb9c589a4c3f1621667..39812688463ae0c5b9079b20b05a161e559c41cf 100644
--- a/Tests/QtAutogen/RerunUicOnFileChange/UicOnFileChange/main.cpp
+++ b/Tests/QtAutogen/RerunUicOnFileChange/UicOnFileChange/main.cpp
@@ -1,9 +1,11 @@
 #include "ui_mainwindow.h"
 
+extern bool subdircheck();
+
 int main(int argc, char* argv[])
 {
   MocWidget mw;
   Ui::Widget mwUi;
   mwUi.setupUi(&mw);
-  return mw.objectName() == "Widget2" ? 0 : 1;
+  return mw.objectName() == "Widget2" && subdircheck() ? 0 : 1;
 }
diff --git a/Tests/QtAutogen/RerunUicOnFileChange/UicOnFileChange/subdir/mainwindowsubdir.ui.in b/Tests/QtAutogen/RerunUicOnFileChange/UicOnFileChange/subdir/mainwindowsubdir.ui.in
new file mode 100644
index 0000000000000000000000000000000000000000..a6a31f6d4280a799bc97df4f46d54fc236da9483
--- /dev/null
+++ b/Tests/QtAutogen/RerunUicOnFileChange/UicOnFileChange/subdir/mainwindowsubdir.ui.in
@@ -0,0 +1,7 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<ui version="4.0">
+ <class>WidgetSubdir</class>
+ <widget class="MocWidget" name="WidgetSubdir@Num@"/>
+ <resources/>
+ <connections/>
+</ui>
diff --git a/Tests/QtAutogen/RerunUicOnFileChange/UicOnFileChange/subdir/subdircheck.cpp b/Tests/QtAutogen/RerunUicOnFileChange/UicOnFileChange/subdir/subdircheck.cpp
new file mode 100644
index 0000000000000000000000000000000000000000..3b36a10df8a5ffef57245344961163a06b4a46cc
--- /dev/null
+++ b/Tests/QtAutogen/RerunUicOnFileChange/UicOnFileChange/subdir/subdircheck.cpp
@@ -0,0 +1,9 @@
+#include "ui_mainwindowsubdir.h"
+
+bool subdircheck()
+{
+  MocWidget mw;
+  Ui::WidgetSubdir mwUi;
+  mwUi.setupUi(&mw);
+  return mw.objectName() == "WidgetSubdir2";
+}