From d6c93f5206b5814028b711cbb389866c616f7054 Mon Sep 17 00:00:00 2001 From: "Chris H. Meyer" Date: Tue, 13 Jan 2026 17:51:45 +0100 Subject: [PATCH] [desktop] Fix designer Signed-off-by: Chris H. Meyer Co-authored-by: Yannik Bloscheck Co-authored-by: Chris H. Meyer Co-committed-by: Chris H. Meyer --- .gitignore | 7 +++--- android/sdk/build.gradle | 6 +++++ docs/INSTALL_DESKTOP.md | 14 +++++++++++ docs/STYLES.md | 7 +++--- libs/drape_frontend/frontend_renderer.cpp | 1 + libs/indexer/map_style_reader.cpp | 4 +-- qt/build_style/build_common.cpp | 21 ++++++++-------- qt/build_style/build_drules.cpp | 24 +++++++++++++++--- qt/build_style/build_drules.h | 2 +- qt/build_style/build_phone_pack.cpp | 5 ++++ qt/build_style/build_skins.cpp | 19 +++++--------- qt/build_style/build_statistics.cpp | 10 ++++++++ qt/build_style/build_style.cpp | 16 ++++++++++-- qt/main.cpp | 30 +++++++++++------------ qt/preferences_dialog.cpp | 2 +- tools/python/recalculate_geom_index.py | 14 +++++------ tools/unix/build_omim.sh | 16 ++++++++++-- tools/unix/generate_symbols.sh | 8 +++--- 18 files changed, 137 insertions(+), 69 deletions(-) diff --git a/.gitignore b/.gitignore index 61c829872..0daf445aa 100644 --- a/.gitignore +++ b/.gitignore @@ -31,10 +31,9 @@ data/types.txt* data/visibility.txt* data/colors.txt* data/patterns.txt* -# TODO: designer is not used at the moment -# data/symbols/*/design/ -# data/colors_design.txt -# data/patterns_design.txt +data/symbols/*/design/ +data/colors_design.txt +data/patterns_design.txt # Auto-generated from data/categories-strings/* by tools/unix/generate_categories.sh data/categories.txt diff --git a/android/sdk/build.gradle b/android/sdk/build.gradle index 827f57f4c..b85c933ea 100644 --- a/android/sdk/build.gradle +++ b/android/sdk/build.gradle @@ -131,6 +131,12 @@ dependencies { testImplementation libs.junit } +android { + androidResources { + ignoreAssetsPatterns.add("!design") + } +} + // TODO: Running lint task triggers native build. Find a better solution. project.afterEvaluate { boolean isLintRun = project.gradle.startParameter.taskNames.any { it.toLowerCase().contains('lint') } diff --git a/docs/INSTALL_DESKTOP.md b/docs/INSTALL_DESKTOP.md index dca50c569..b315b5ed9 100644 --- a/docs/INSTALL_DESKTOP.md +++ b/docs/INSTALL_DESKTOP.md @@ -175,6 +175,11 @@ To build a desktop app: tools/unix/build_omim.sh -r desktop ``` +To build the "Designer" version of the desktop app, add the parameter `-t`: +```bash +tools/unix/build_omim.sh -r -t desktop +``` + The output binary will go into `../omim-build-release`. Check `tools/unix/build_omim.sh -h` for more build options, e.g. to build a debug version. @@ -205,6 +210,15 @@ _Linux:_ ../omim-build-release/CoMaps ``` +For running the "Designer" version instead, use the following command and +optionally add the path to the style file to be loaded. If no path is provided, +the application will ask for it by opening a file dialog on startup. + + +```bash +../omim-build-release/CoMaps.Designer optional/path/to/style.mapcss +``` + _macOS:_ ```bash diff --git a/docs/STYLES.md b/docs/STYLES.md index ae1ef7806..b64e756e5 100644 --- a/docs/STYLES.md +++ b/docs/STYLES.md @@ -77,9 +77,10 @@ e strings ## Testing your changes -The most convenient way is using [the desktop app](INSTALL.md#desktop-app). -(there is a "Designer" version of it also, which facilitates development -by rebuilding styles and symbols quickly, but it's broken as of now, please help fix it!) +The most convenient way is using [the desktop app](INSTALL_DESKTOP.md#desktop-app). +There is a "Designer" version of it also, which facilitates development +by rebuilding styles and symbols quickly. See the guide referenced above to find +details on how to build and execute it. To test on Android or iOS device either re-build the app or put the compiled style files (e.g. `drules_proto_default_light.bin`) into diff --git a/libs/drape_frontend/frontend_renderer.cpp b/libs/drape_frontend/frontend_renderer.cpp index 37f07850a..50a8c0b8a 100644 --- a/libs/drape_frontend/frontend_renderer.cpp +++ b/libs/drape_frontend/frontend_renderer.cpp @@ -24,6 +24,7 @@ #include "indexer/drawing_rules.hpp" #include "indexer/scales.hpp" +#include "indexer/classificator_loader.hpp" #include "geometry/any_rect2d.hpp" diff --git a/libs/indexer/map_style_reader.cpp b/libs/indexer/map_style_reader.cpp index f7db3652a..6ed0ec304 100644 --- a/libs/indexer/map_style_reader.cpp +++ b/libs/indexer/map_style_reader.cpp @@ -19,13 +19,13 @@ std::string const kSuffixOutdoorsDark = "_outdoors_dark"; std::string const kStylesOverrideDir = "styles"; #ifdef BUILD_DESIGNER -std::string const kSuffixDesignTool = "_design"; +std::string const kSuffixDesignTool = "design"; #endif // BUILD_DESIGNER std::string GetStyleRulesSuffix(MapStyle mapStyle) { #ifdef BUILD_DESIGNER - return kSuffixDesignTool; + return "_" + kSuffixDesignTool; #else switch (mapStyle) { diff --git a/qt/build_style/build_common.cpp b/qt/build_style/build_common.cpp index eea4aee11..ceea41ad3 100644 --- a/qt/build_style/build_common.cpp +++ b/qt/build_style/build_common.cpp @@ -3,6 +3,7 @@ #include "platform/platform.hpp" #include "base/file_name_utils.hpp" +#include "base/logging.hpp" #include #include @@ -17,11 +18,7 @@ QString ExecProcess(QString const & program, std::initializer_list args, QProcessEnvironment const * env) { - // Quote all arguments. QStringList qargs(args); - for (auto i = qargs.begin(); i != qargs.end(); ++i) - *i = "\"" + *i + "\""; - QProcess p; if (nullptr != env) p.setProcessEnvironment(*env); @@ -43,8 +40,9 @@ QString ExecProcess(QString const & program, std::initializer_list args } if (!error.isEmpty()) { - QString const msg = "STDERR with a zero exit code:\n" + program + " " + qargs.join(" "); - throw std::runtime_error(msg.toStdString()); + QString msg = "STDERR with a zero exit code:\n" + program + " " + qargs.join(" "); + msg += "\nSTDERR:\n" + error; + LOG(LINFO, (msg.toStdString())); } return output; } @@ -102,12 +100,13 @@ QString GetExternalPath(QString const & name, QString const & primaryPath, QStri // Special case for looking for in application folder. if (!QFileInfo::exists(path) && secondaryPath.isEmpty()) { - std::string const appPath = QCoreApplication::applicationDirPath().toStdString(); + QString appPath = QCoreApplication::applicationDirPath(); - std::regex re("(/[^/]*\\.app)"); - std::smatch m; - if (std::regex_search(appPath, m, re) && m.size() > 0) - path.fromStdString(base::JoinPath(m[0], name.toStdString())); +#if defined(OMIM_OS_MAC) + path = JoinPathQt({appPath, "..", "..", "..", name}); +#elif defined(OMIM_OS_LINUX) + path = JoinPathQt({appPath, name}); +#endif } return path; } diff --git a/qt/build_style/build_drules.cpp b/qt/build_style/build_drules.cpp index 431bf9dc1..b7604ed22 100644 --- a/qt/build_style/build_drules.cpp +++ b/qt/build_style/build_drules.cpp @@ -15,7 +15,7 @@ namespace build_style { -void BuildDrawingRulesImpl(QString const & mapcssFile, QString const & outputDir) +void BuildDrawingRulesImpl(QString const & mapcssFile, QString const & outputDir, QString const & prioDir) { QString const outputTemplate = JoinPathQt({outputDir, "drules_proto_design"}); QString const outputFile = outputTemplate + ".bin"; @@ -29,6 +29,21 @@ void BuildDrawingRulesImpl(QString const & mapcssFile, QString const & outputDir env.insert("PROTOBUF_EGG_PATH", GetProtobufEggPath()); // Run the script +#if defined(OMIM_OS_MAC) + (void)ExecProcess("python3", + { + GetExternalPath("libkomwm.py", "kothic/src", "../tools/kothic/src"), + "-s", + mapcssFile, + "-o", + outputTemplate, + "-p", + prioDir, + "-x", + "True", + }, + &env); +#else (void)ExecProcess("python", { GetExternalPath("libkomwm.py", "kothic/src", "../tools/kothic/src"), @@ -36,21 +51,24 @@ void BuildDrawingRulesImpl(QString const & mapcssFile, QString const & outputDir mapcssFile, "-o", outputTemplate, + "-p", + prioDir, "-x", "True", }, &env); +#endif // Ensure that generated file is not empty. if (QFile(outputFile).size() == 0) throw std::runtime_error("Drawing rules file has zero size"); } -void BuildDrawingRules(QString const & mapcssFile, QString const & outputDir) +void BuildDrawingRules(QString const & mapcssFile, QString const & outputDir, QString const & prioDir) { CopyFromResources("mapcss-mapping.csv", outputDir); CopyFromResources("mapcss-dynamic.txt", outputDir); - BuildDrawingRulesImpl(mapcssFile, outputDir); + BuildDrawingRulesImpl(mapcssFile, outputDir, prioDir); } void ApplyDrawingRules(QString const & outputDir) diff --git a/qt/build_style/build_drules.h b/qt/build_style/build_drules.h index d9b0dba82..7e87d7e5d 100644 --- a/qt/build_style/build_drules.h +++ b/qt/build_style/build_drules.h @@ -4,6 +4,6 @@ namespace build_style { -void BuildDrawingRules(QString const & mapcssFile, QString const & outputDir); +void BuildDrawingRules(QString const & mapcssFile, QString const & outputDir, QString const & prioDir); void ApplyDrawingRules(QString const & outputDir); } // build_style diff --git a/qt/build_style/build_phone_pack.cpp b/qt/build_style/build_phone_pack.cpp index c3bda0b79..e45b3f087 100644 --- a/qt/build_style/build_phone_pack.cpp +++ b/qt/build_style/build_phone_pack.cpp @@ -21,7 +21,12 @@ QString RunBuildingPhonePack(QString const & stylesDir, QString const & targetDi if (!QDir(targetDir).exists()) throw runtime_error("target directory does not exist" + targetDir.toStdString()); +#if defined(OMIM_OS_MAC) + return ExecProcess("python3", + {GetExternalPath("generate_styles_override.py", "", "../tools/python"), stylesDir, targetDir}); +#else return ExecProcess("python", {GetExternalPath("generate_styles_override.py", "", "../tools/python"), stylesDir, targetDir}); +#endif } } // namespace build_style diff --git a/qt/build_style/build_skins.cpp b/qt/build_style/build_skins.cpp index 9725d2bda..f67584245 100644 --- a/qt/build_style/build_skins.cpp +++ b/qt/build_style/build_skins.cpp @@ -31,10 +31,10 @@ enum SkinType SkinCount }; -using SkinInfo = std::tuple; +using SkinInfo = std::tuple; SkinInfo const g_skinInfo[SkinCount] = { - std::make_tuple("mdpi", 18, false), std::make_tuple("hdpi", 27, false), std::make_tuple("xhdpi", 36, false), - std::make_tuple("6plus", 43, false), std::make_tuple("xxhdpi", 54, false), std::make_tuple("xxxhdpi", 64, false), + std::make_tuple("mdpi", 18), std::make_tuple("hdpi", 27), std::make_tuple("xhdpi", 36), + std::make_tuple("6plus", 43), std::make_tuple("xxhdpi", 54), std::make_tuple("xxxhdpi", 64), }; std::array const g_skinTypes = {{ @@ -54,10 +54,6 @@ inline int SkinSize(SkinType s) { return std::get<1>(g_skinInfo[s]); } -inline bool SkinCoorrectColor(SkinType s) -{ - return std::get<2>(g_skinInfo[s]); -} QString GetSkinGeneratorPath() { @@ -127,7 +123,7 @@ std::unordered_map GetSkinSizes(QString const & file) return skinSizes; } -void BuildSkinImpl(QString const & styleDir, QString const & suffix, int size, bool colorCorrection, +void BuildSkinImpl(QString const & styleDir, QString const & suffix, int size, QString const & outputDir) { QString const symbolsDir = JoinPathQt({styleDir, "symbols"}); @@ -141,7 +137,7 @@ void BuildSkinImpl(QString const & styleDir, QString const & suffix, int size, b throw std::runtime_error("Output directory is not clear"); // Create output skin directory - if (!QDir().mkdir(outputDir)) + if (!QDir().mkpath(outputDir)) throw std::runtime_error("Cannot create output skin directory"); // Create symbolic link for symbols/png @@ -164,8 +160,6 @@ void BuildSkinImpl(QString const & styleDir, QString const & suffix, int size, b "--skinName", JoinPathQt({outputDir, "basic"}), "--skinSuffix=", - "--colorCorrection", - (colorCorrection ? "true" : "false"), }); // Check if files were created. @@ -187,9 +181,8 @@ void BuildSkins(QString const & styleDir, QString const & outputDir) QString const suffix = SkinSuffix(s); QString const outputSkinDir = JoinPathQt({outputDir, "symbols", suffix, "design"}); int const size = resolution2size.at(suffix.toStdString()); // SkinSize(s); - bool const colorCorrection = SkinCoorrectColor(s); - BuildSkinImpl(styleDir, suffix, size, colorCorrection, outputSkinDir); + BuildSkinImpl(styleDir, suffix, size, outputSkinDir); } } diff --git a/qt/build_style/build_statistics.cpp b/qt/build_style/build_statistics.cpp index 19c71bba8..3f59a1b5e 100644 --- a/qt/build_style/build_statistics.cpp +++ b/qt/build_style/build_statistics.cpp @@ -27,6 +27,15 @@ QString GetStyleStatistics(QString const & mapcssMappingFile, QString const & dr env.insert("PROTOBUF_EGG_PATH", GetProtobufEggPath()); // Run the script. +#if defined(OMIM_OS_MAC) + return ExecProcess("python3", + { + GetExternalPath("drules_info.py", "kothic/src", "../tools/python/stylesheet"), + mapcssMappingFile, + drulesFile, + }, + &env); +#else return ExecProcess("python", { GetExternalPath("drules_info.py", "kothic/src", "../tools/python/stylesheet"), @@ -34,6 +43,7 @@ QString GetStyleStatistics(QString const & mapcssMappingFile, QString const & dr drulesFile, }, &env); +#endif } QString GetCurrentStyleStatistics() diff --git a/qt/build_style/build_style.cpp b/qt/build_style/build_style.cpp index 9a64e9fe3..11c4f36ee 100644 --- a/qt/build_style/build_style.cpp +++ b/qt/build_style/build_style.cpp @@ -41,6 +41,7 @@ void BuildAndApply(QString const & mapcssFile) QDir const projectDir = QFileInfo(mapcssFile).absoluteDir(); QString const styleDir = projectDir.absolutePath() + QDir::separator(); + QString const prioDir = styleDir + ".." + QDir::separator() + "include"; QString const outputDir = styleDir + "out" + QDir::separator(); // Ensure output directory is clear @@ -53,7 +54,7 @@ void BuildAndApply(QString const & mapcssFile) if (hasSymbols) { auto future = std::async(std::launch::async, BuildSkins, styleDir, outputDir); - BuildDrawingRules(mapcssFile, outputDir); + BuildDrawingRules(mapcssFile, outputDir, prioDir); future.get(); // may rethrow exception from the BuildSkin ApplyDrawingRules(outputDir); @@ -61,7 +62,7 @@ void BuildAndApply(QString const & mapcssFile) } else { - BuildDrawingRules(mapcssFile, outputDir); + BuildDrawingRules(mapcssFile, outputDir, prioDir); ApplyDrawingRules(outputDir); } } @@ -107,6 +108,16 @@ void RunRecalculationGeometryScript(QString const & mapcssFile) CopyFromResources("classificator.txt", geometryToolResourceDir); CopyFromResources("types.txt", geometryToolResourceDir); +#if defined(OMIM_OS_MAC) + (void)ExecProcess("python3", { + GetRecalculateGeometryScriptPath(), + resourceDir, + writableDir, + generatorToolPath, + appPath, + mapcssFile, + }); +#else (void)ExecProcess("python", { GetRecalculateGeometryScriptPath(), resourceDir, @@ -115,6 +126,7 @@ void RunRecalculationGeometryScript(QString const & mapcssFile) appPath, mapcssFile, }); +#endif } bool NeedRecalculate = false; diff --git a/qt/main.cpp b/qt/main.cpp index 675f1d2a7..8058692fd 100644 --- a/qt/main.cpp +++ b/qt/main.cpp @@ -244,26 +244,26 @@ int main(int argc, char * argv[]) ); w.show(); returnCode = QApplication::exec(); - } #ifdef BUILD_DESIGNER - if (build_style::NeedRecalculate && !mapcssFilePath.isEmpty()) - { - try + if (build_style::NeedRecalculate && !mapcssFilePath.isEmpty()) { - build_style::RunRecalculationGeometryScript(mapcssFilePath); + try + { + build_style::RunRecalculationGeometryScript(mapcssFilePath); + } + catch (std::exception & e) + { + QMessageBox msgBox; + msgBox.setWindowTitle("Error"); + msgBox.setText(e.what()); + msgBox.setStandardButtons(QMessageBox::Ok); + msgBox.setDefaultButton(QMessageBox::Ok); + msgBox.exec(); + } } - catch (std::exception & e) - { - QMessageBox msgBox; - msgBox.setWindowTitle("Error"); - msgBox.setText(e.what()); - msgBox.setStandardButtons(QMessageBox::Ok); - msgBox.setDefaultButton(QMessageBox::Ok); - msgBox.exec(); - } - } #endif // BUILD_DESIGNER + } LOG_SHORT(LINFO, ("Finished with code", returnCode)); return returnCode; diff --git a/qt/preferences_dialog.cpp b/qt/preferences_dialog.cpp index 4f8d77153..8a9318dec 100644 --- a/qt/preferences_dialog.cpp +++ b/qt/preferences_dialog.cpp @@ -190,7 +190,7 @@ PreferencesDialog::PreferencesDialog(QWidget * parent, Framework & framework) settings::Set(kEnabledAutoRegenGeomIndex, false); indexRegenCheckBox->setChecked(enabled); connect(indexRegenCheckBox, &QCheckBox::stateChanged, - [](int i) { settings::Set(kEnabledAutoRegenGeomIndex, static_cast(i)) }); + [](int i) { settings::Set(kEnabledAutoRegenGeomIndex, static_cast(i)); }); } #endif diff --git a/tools/python/recalculate_geom_index.py b/tools/python/recalculate_geom_index.py index 0eb9f5c33..02ce4ef67 100644 --- a/tools/python/recalculate_geom_index.py +++ b/tools/python/recalculate_geom_index.py @@ -8,7 +8,7 @@ Uses generator_tool for index calculation. After all it runs designer_tool if ha import os import subprocess import sys -from Queue import Queue, Empty +from queue import Queue, Empty from threading import Thread WORKERS = 8 @@ -27,7 +27,7 @@ def FindAllMwms(data_path): return result def ProcessMwm(generator_tool, task, error_queue): - print "Processing ", task[0] + print("Processing ", task[0]) try: subprocess.call((generator_tool, '--data_path={0}'.format(task[1]), '--output={0}'.format(task[0][:-4]), "--generate_index=true", "--intermediate_data_path=/tmp/")) except subprocess.CalledProcessError as e: @@ -38,7 +38,7 @@ def parallel_worker(tasks, generator_tool, error_queue): try: task = tasks.get_nowait() except Empty: - print "Process done!" + print("Process done!") return ProcessMwm(generator_tool, task, error_queue) tasks.task_done() @@ -46,7 +46,7 @@ def parallel_worker(tasks, generator_tool, error_queue): if __name__ == "__main__": if len(sys.argv) < 3: - print "{0} [ ]".format(sys.argv[0]) + print("{0} [ ]".format(sys.argv[0])) exit(1) mwms = FindAllMwms(sys.argv[1]) @@ -63,14 +63,14 @@ if __name__ == "__main__": t.start() tasks.join() - print "Processing done." + print("Processing done.") if len(sys.argv) > 4: - print "Starting app" + print("Starting app") subprocess.Popen(sys.argv[4:]) if not error_queue.qsize() == 0: while error_queue.qsize(): error = error_queue.get() - print error + print(error) exit(1) diff --git a/tools/unix/build_omim.sh b/tools/unix/build_omim.sh index c4d1e8721..081f17304 100755 --- a/tools/unix/build_omim.sh +++ b/tools/unix/build_omim.sh @@ -161,8 +161,20 @@ build() done fi else - "$CMAKE" "$CMAKE_GENERATOR" "$OMIM_PATH" -DCMAKE_BUILD_TYPE="$CONF" -DBUILD_DESIGNER:BOOL=ON ${CMAKE_CONFIG:-} - $MAKE_COMMAND package + "$CMAKE" "$CMAKE_GENERATOR" "$OMIM_PATH" \ + -DCMAKE_BUILD_TYPE="$CONF" \ + -DBUILD_DESIGNER:BOOL=ON \ + -DBUILD_STANDALONE:BOOL=$([ "$OPT_STANDALONE" == 1 ] && echo "ON" || echo "OFF") \ + ${CMAKE_CONFIG:-} + echo "" + "$CMAKE" --build "$DIRNAME" --target generator_tool + "$CMAKE" --build "$DIRNAME" --target skin_generator_tool + $MAKE_COMMAND $OPT_TARGET + if [ -n "$OPT_TARGET" ] && [ -n "$OPT_LAUNCH_BINARY" ]; then + for target in $OPT_TARGET; do + "$DIRNAME/$target" + done + fi fi if [ -n "$OPT_COMPILE_DATABASE" ]; then cp "$DIRNAME/compile_commands.json" "$OMIM_PATH" diff --git a/tools/unix/generate_symbols.sh b/tools/unix/generate_symbols.sh index c9494f679..5f580a812 100755 --- a/tools/unix/generate_symbols.sh +++ b/tools/unix/generate_symbols.sh @@ -86,8 +86,6 @@ done rm -rf "$DATA_PATH"/symbols/*/design/ -# The styles designer is not used at the moment. -# If enabled then remove design symbols from bundled android assets in android/sdk/src/main/assets/symbols -# for i in ${symbols_name[*]}; do -# cp -r "$DATA_PATH"/symbols/"${i}"/light/ "$DATA_PATH"/symbols/"${i}"/design/ -# done +for i in ${symbols_name[*]}; do + cp -r "$DATA_PATH"/symbols/"${i}"/light/ "$DATA_PATH"/symbols/"${i}"/design/ +done