From 5fc8c262cfb681fac1c8eaa41d8d4055f2d7746d Mon Sep 17 00:00:00 2001 From: Charliechen114514 <725610365@qq.com> Date: Tue, 8 Sep 2026 22:18:47 +0800 Subject: [PATCH] docs: normalize mistagged code fence closers across document tree Markdown closing fences must be bare; a tagged fence (e.g. ```text) after an opening ```cpp renders as literal text and swallows the following prose into the code block. Follow-up to #30, which fixed the same defect in document/development/ and document/status/current.md. - strip the info string from 1183 botched closers in 162 files - validate the repair against PR #30: byte-identical output on the six development guides it fixed by hand - re-verify with a CommonMark fence walker: 0 anomalies remain under document/ and repo-wide outside third_party/ --- document/DOXYGEN_REQUEST.md | 18 +++---- document/HandBook/base/expected.md | 18 +++---- document/HandBook/base/factory.md | 16 +++--- document/HandBook/base/hash.md | 16 +++--- document/HandBook/base/linux/proc_parser.md | 20 ++++---- .../HandBook/base/macro/plain_property.md | 8 +-- document/HandBook/base/macro/system_judge.md | 10 ++-- document/HandBook/base/macros.md | 8 +-- document/HandBook/base/mpsc_queue.md | 14 +++--- document/HandBook/base/once_init.md | 16 +++--- document/HandBook/base/overview.md | 14 +++--- document/HandBook/base/policy_chain.md | 20 ++++---- document/HandBook/base/scope_guard.md | 26 +++++----- document/HandBook/base/singleton.md | 14 +++--- document/HandBook/base/span.md | 16 +++--- document/HandBook/base/weak_ptr.md | 16 +++--- document/HandBook/base/weak_ptr_factory.md | 16 +++--- .../base/config_manager/01-quick-start.md | 40 +++++++-------- .../base/config_manager/04-architecture.md | 44 ++++++++-------- .../desktop/base/logger/advanced_usage.md | 36 ++++++------- .../desktop/base/logger/basic_logging.md | 38 +++++++------- .../desktop/base/logger/best_practices.md | 40 +++++++-------- .../desktop/base/logger/configuration.md | 38 +++++++------- .../desktop/base/logger/formatters.md | 34 ++++++------- .../HandBook/desktop/base/logger/overview.md | 10 ++-- .../desktop/base/logger/performance.md | 42 ++++++++-------- .../desktop/base/logger/quick_start.md | 24 ++++----- .../HandBook/desktop/base/logger/sinks.md | 28 +++++------ .../desktop/base/logger/troubleshooting.md | 50 +++++++++---------- .../HandBook/examples/cpu_info_example.md | 14 +++--- .../linux/cpu_implementation.md | 10 ++-- .../linux/memory/memory_implementation.md | 24 ++++----- .../windows/cpu_implementation.md | 14 +++--- .../windows/memory/memory_implementation.md | 18 +++---- .../HandBook/ui/application/application.md | 10 ++-- .../ui/application/material_application.md | 12 ++--- document/HandBook/ui/architecture/index.md | 2 +- .../01-why-we-need-own-math-layer.md | 2 +- .../02-color-system-hct.md | 14 +++--- .../03-geometry-and-device-pixel.md | 16 +++--- .../01-theme-system-design.md | 14 +++--- .../layer-2-theme-engine/02-token-system.md | 22 ++++---- .../layer-2-theme-engine/03-color-scheme.md | 18 +++---- .../04-typography-shape-motion.md | 16 +++--- .../01-animation-architecture.md | 12 ++--- .../02-timing-spring-animation.md | 14 +++--- .../03-factory-and-strategy.md | 18 +++---- .../01-state-machine.md | 22 ++++---- .../02-ripple-and-elevation.md | 24 ++++----- .../03-focus-indicator.md | 8 +-- .../01-adapter-pattern.md | 8 +-- .../02-button-deep-dive.md | 22 ++++---- .../03-painting-pipeline.md | 18 +++---- document/HandBook/ui/base/color.md | 12 ++--- document/HandBook/ui/base/color_helper.md | 8 +-- document/HandBook/ui/base/device_pixel.md | 8 +-- document/HandBook/ui/base/easing.md | 8 +-- document/HandBook/ui/base/geometry_helper.md | 8 +-- document/HandBook/ui/base/math_helper.md | 10 ++-- document/HandBook/ui/components/animation.md | 10 ++-- .../components/animation_factory_manager.md | 16 +++--- .../HandBook/ui/components/animation_group.md | 10 ++-- .../ui/components/spring_animation.md | 14 +++--- .../ui/components/timing_animation.md | 10 ++-- document/HandBook/ui/core/color_scheme.md | 10 ++-- document/HandBook/ui/core/font_type.md | 8 +-- document/HandBook/ui/core/motion_spec.md | 14 +++--- document/HandBook/ui/core/radius_scale.md | 14 +++--- document/HandBook/ui/core/theme.md | 6 +-- document/HandBook/ui/core/theme_factory.md | 14 +++--- document/HandBook/ui/core/theme_manager.md | 14 +++--- .../cfmaterial_token_literals.md | 12 ++--- .../cfmaterial_motion_token_literals.md | 16 +++--- .../cfmaterial_radius_scale_literals.md | 14 +++--- .../cfmaterial_typography_token_literals.md | 10 ++-- .../animation/cfmaterial_animation_factory.md | 12 ++--- .../cfmaterial_animation_strategy.md | 12 ++--- .../animation/cfmaterial_fade_animation.md | 16 +++--- .../animation/cfmaterial_scale_animation.md | 20 ++++---- .../animation/cfmaterial_slide_animation.md | 20 ++++---- .../ui/material/base/elevation_controller.md | 16 +++--- .../HandBook/ui/material/base/focus_ring.md | 12 ++--- .../ui/material/base/painter_layer.md | 18 +++---- .../ui/material/base/ripple_helper.md | 18 +++---- .../ui/material/cfmaterial_fonttype.md | 16 +++--- .../HandBook/ui/material/cfmaterial_motion.md | 20 ++++---- .../ui/material/cfmaterial_radius_scale.md | 14 +++--- .../HandBook/ui/material/cfmaterial_scheme.md | 16 +++--- .../HandBook/ui/material/cfmaterial_theme.md | 8 +-- .../ui/material/material_factory_class.md | 12 ++--- .../ui/material/material_factory_hpp.md | 18 +++---- .../HandBook/ui/material/widget/button.md | 22 ++++---- .../HandBook/ui/material/widget/checkbox.md | 6 +-- .../HandBook/ui/material/widget/combobox.md | 10 ++-- .../ui/material/widget/doublespinbox.md | 8 +-- .../HandBook/ui/material/widget/groupbox.md | 12 ++--- document/HandBook/ui/material/widget/label.md | 12 ++--- .../HandBook/ui/material/widget/listview.md | 12 ++--- .../ui/material/widget/progressbar.md | 10 ++-- .../ui/material/widget/radiobutton.md | 10 ++-- .../HandBook/ui/material/widget/scrollview.md | 16 +++--- .../HandBook/ui/material/widget/separator.md | 10 ++-- .../HandBook/ui/material/widget/slider.md | 10 ++-- .../HandBook/ui/material/widget/spinbox.md | 8 +-- .../ui/material/widget/state_machine.md | 14 +++--- .../HandBook/ui/material/widget/switch.md | 6 +-- .../HandBook/ui/material/widget/tableview.md | 14 +++--- .../HandBook/ui/material/widget/tabview.md | 12 ++--- .../HandBook/ui/material/widget/textarea.md | 12 ++--- .../HandBook/ui/material/widget/textfield.md | 14 +++--- .../HandBook/ui/material/widget/treeview.md | 14 +++--- document/ci/ci-build-entry.md | 32 ++++++------ document/ci/docker-environment.md | 26 +++++----- document/ci/toolchain-setup.md | 28 +++++------ document/optimize/pre-release-code-format.md | 2 +- document/release_rule/git_hooks_guide.md | 40 +++++++-------- .../build_helpers/ci_build_entry.sh.md | 10 ++-- .../scripts/build_helpers/config_files.md | 10 ++-- .../scripts/build_helpers/docker_start.md | 12 ++--- .../scripts/build_helpers/docker_start.sh.md | 24 ++++----- .../build_helpers/linux_configure.sh.md | 8 +-- .../build_helpers/linux_deploy_build.sh.md | 14 +++--- .../build_helpers/linux_develop_build.sh.md | 12 ++--- .../linux_fast_deploy_build.sh.md | 14 +++--- .../linux_fast_develop_build.sh.md | 14 +++--- .../build_helpers/linux_run_tests.sh.md | 12 ++--- .../build_helpers/windows_configure.md | 10 ++-- .../build_helpers/windows_deploy_build.md | 4 +- .../build_helpers/windows_develop_build.md | 4 +- .../windows_fast_deploy_build.md | 8 +-- .../windows_fast_develop_build.md | 8 +-- .../build_helpers/windows_run_tests.md | 6 +-- .../install_build_dependencies.sh.md | 8 +-- document/scripts/develop/format_cpp.ps1.md | 8 +-- document/scripts/develop/format_cpp.sh.md | 6 +-- .../develop/remove_trailing_space.ps1.md | 6 +-- .../develop/remove_trailing_space.sh.md | 6 +-- document/scripts/docker/Dockerfile.build.md | 4 +- document/scripts/docker/docker-compose.yml.md | 6 +-- document/scripts/document/index.md | 2 +- document/scripts/doxygen/lint.py.md | 12 ++--- document/scripts/lib/bash/lib_args.sh.md | 24 ++++----- document/scripts/lib/bash/lib_build.sh.md | 22 ++++---- document/scripts/lib/bash/lib_common.sh.md | 22 ++++---- document/scripts/lib/bash/lib_config.sh.md | 18 +++---- document/scripts/lib/bash/lib_git.sh.md | 8 +-- document/scripts/lib/bash/lib_paths.sh.md | 8 +-- .../scripts/lib/powershell/LibArgs.psm1.md | 16 +++--- .../scripts/lib/powershell/LibBuild.psm1.md | 24 ++++----- .../scripts/lib/powershell/LibCommon.psm1.md | 18 +++---- .../scripts/lib/powershell/LibConfig.psm1.md | 14 +++--- .../scripts/lib/powershell/LibGit.psm1.md | 8 +-- .../scripts/lib/powershell/LibPaths.psm1.md | 12 ++--- .../release/hooks/install_hooks.ps1.md | 8 +-- .../scripts/release/hooks/install_hooks.sh.md | 6 +-- .../release/hooks/pre-commit.sample.md | 2 +- .../scripts/release/hooks/pre-push.sample.md | 2 +- .../scripts/release/hooks/version_utils.sh.md | 10 ++-- document/todo/base/04_testing.md | 2 +- .../todo/base/99_ui_material_framework.md | 2 +- .../todo/desktop/milestone_00_overview.md | 2 +- document/todo/desktop/summary.md | 14 +++--- 162 files changed, 1183 insertions(+), 1183 deletions(-) diff --git a/document/DOXYGEN_REQUEST.md b/document/DOXYGEN_REQUEST.md index 44e283816..e29527e07 100644 --- a/document/DOXYGEN_REQUEST.md +++ b/document/DOXYGEN_REQUEST.md @@ -57,7 +57,7 @@ Every file must start with a file-level block like: * @since * @ingroup */ -```yaml +``` * Fill `@author`, `@date`, `@version` from git metadata if available; otherwise set to `"N/A"`. * Keep the file-level description concise (≤ 2–3 short sentences). @@ -87,7 +87,7 @@ Block style example: * @since Version or "N/A". * @ingroup Module name or "none". */ -```text +``` Line-style equivalent: @@ -96,7 +96,7 @@ Line-style equivalent: /// @details Optional extended description in third-person present tense. /// @param[in] name Description... /// @return Description... -```yaml +``` * **MUST** include `@tparam` for templates. * **MUST** include `@throws` (or `@throws None`). @@ -178,7 +178,7 @@ Class example: * @endcode */ class RingBuffer { ... }; -```yaml +``` --- @@ -202,7 +202,7 @@ enum class PowerState { Sleep, ///< Low-power sleep mode. On ///< Fully powered. }; -```yaml +``` --- @@ -217,7 +217,7 @@ Example: ```cpp /// @brief Pointer to underlying device context. Ownership: observer; may be nullptr. DeviceContext* ctx_; -```yaml +``` --- @@ -302,14 +302,14 @@ Provide exact failure messages for each check so the generator can iterate. * @ingroup util */ uint64_t parse_le_uint(const uint8_t* buf, size_t len); -```text +``` ### Bad (function) ```cpp /** Parses bytes into a number. This function will parse and return value. */ uint64_t parse_le_uint(const uint8_t* buf, size_t len); -```yaml +``` * Issues: first-person / future tense; no tags; no units; no param directions; too short; possibly misleading. @@ -378,7 +378,7 @@ Return an object with fields: ], "fixme_count": M } -```yaml +``` --- diff --git a/document/HandBook/base/expected.md b/document/HandBook/base/expected.md index 8a00c1191..4b929963c 100644 --- a/document/HandBook/base/expected.md +++ b/document/HandBook/base/expected.md @@ -42,7 +42,7 @@ cf::expected open_file(const std::string& path) { } return file; } -```text +``` `expected` 强制调用者处理错误——你想拿到值,就必须先检查有没有错误。而且类型系统会帮你看住:一个 `expected` 要么包含 `int`,要么包含 `ErrorCode`,不可能同时存在或都不存在。 @@ -66,7 +66,7 @@ cf::expected parse_number(std::string_view str) { return cf::unexpected(ParseError::Overflow); } } -```text +``` 调用方需要检查结果: @@ -85,7 +85,7 @@ if (result.has_value()) { break; } } -```text +``` ⚠️ 如果不检查直接调用 `value()`,会抛出 `bad_expected_access` 异常。但这个异常是你在"错误地使用 expected"时才抛出的,和业务逻辑异常是两码事。正常流程下,`expected` 的使用是不抛异常的。 @@ -107,7 +107,7 @@ int value = result.value_or(-1); // 如果是错误状态,返回 -1 // 方式三:直接访问(如果确实是错误状态,会抛异常) int value = result.value(); // 可能抛 bad_expected_access -```cpp +``` `operator*` 和 `operator->` 的行为类似指针,但不做边界检查——如果 `expected` 处于错误状态,调用它们的后果是未定义行为。这和原生指针的越界访问一样,性能优先,安全你自己负责。 @@ -130,7 +130,7 @@ auto result = save_config("config.txt"); if (!result) { std::cerr << "保存失败: " << result.error() << std::endl; } -```text +``` `expected` 的"值"是虚拟的,成功状态下没有实际数据存储,只有一个标志位。这意味着它的内存开销比 `expected` 小——只需要存一个 `bool` 和可能的 `E`。 @@ -170,7 +170,7 @@ cf::expected result = .transform_error([](ParseError err) { return "解析错误: " + std::to_string(static_cast(err)); }); -```text +``` 这些操作的组合可以实现复杂的错误处理逻辑,而且代码是线性的,不是嵌套的: @@ -194,7 +194,7 @@ return result3; return parse_number(input) .and_then(fetch_user) .and_then(calculate_score); -```text +``` ## 与异常的对比 @@ -220,7 +220,7 @@ union { E error; } storage_; bool has_value_; -```cpp +``` 大小是 `max(sizeof(T), sizeof(E)) + sizeof(bool)`,对齐后可能会有一点 padding。如果你在意内存占用,可以让错误类型尽量小——比如用 `enum` 代替 `std::string`。 @@ -257,7 +257,7 @@ namespace std { // 类型别名可以帮助过渡 template using expected = std::expected; -```text +``` 当然,我们还是建议直接用 `cf::expected`,这样可以保持代码的跨平台兼容性,而且我们可以根据自己的需求定制实现。 diff --git a/document/HandBook/base/factory.md b/document/HandBook/base/factory.md index df0b719ac..50902ec73 100644 --- a/document/HandBook/base/factory.md +++ b/document/HandBook/base/factory.md @@ -33,7 +33,7 @@ cf::PlainFactory factory; Widget* w = factory.make(10, 20); // 使用 w... delete w; // 调用方负责删除 -```text +``` `PlainFactory` 的设计目标之一是跨 ABI 边界。动态库导出的接口通常不适合直接返回 `std::unique_ptr`(不同编译器/标准库的 `unique_ptr` 布局可能不同),但裸指针永远兼容。 @@ -49,7 +49,7 @@ using WidgetFactory = cf::StaticPlainFactory; // 在任何地方 auto& factory = WidgetFactory::instance(); Widget* w = factory.make(10, 20); -```text +``` `StaticPlainFactory` 继承了 `PlainFactory` 和 `SimpleSingleton`,线程安全的单例由 Meyer's Singleton 保证。 @@ -71,7 +71,7 @@ auto unique_svc = factory.make_unique("Logger"); // std::unique_ptr auto shared_svc = factory.make_shared("Config"); // std::shared_ptr // 不需要手动 delete -```text +``` ### StaticSmartPtrPlainFactory - 单例版智能指针工厂 @@ -79,7 +79,7 @@ auto shared_svc = factory.make_shared("Config"); // std::shared_ptr using ServiceFactory = cf::StaticSmartPtrPlainFactory; auto svc = ServiceFactory::instance().make_unique("MyService"); -```text +``` ## RegisteredFactory - 注册式工厂 @@ -117,7 +117,7 @@ renderer_factory.register_creator([]() -> IRenderer* { // 创建 auto renderer = renderer_factory.make_unique(); renderer->draw(); -```text +``` ### 自定义删除器 @@ -128,7 +128,7 @@ renderer_factory.register_creator( []() -> IRenderer* { return new VulkanRenderer; }, [](IRenderer* p) { /* 自定义清理逻辑 */ delete p; } ); -```text +``` ### StaticRegisteredFactory - 单例版注册式工厂 @@ -142,7 +142,7 @@ RendererFactory::instance().register_creator([]() -> IRenderer* { // 使用 auto renderer = RendererFactory::instance().make_unique(); -```text +``` ### 检查注册状态 @@ -152,7 +152,7 @@ if (RendererFactory::instance().has_creator()) { } else { // 没有注册任何 creator,无法创建 } -```bash +``` ## 线程安全说明 diff --git a/document/HandBook/base/hash.md b/document/HandBook/base/hash.md index 08c2badbc..f29367a5e 100644 --- a/document/HandBook/base/hash.md +++ b/document/HandBook/base/hash.md @@ -22,7 +22,7 @@ switch (fnv1a64(token)) { case fnv1a64("START"): /* ... */ break; case fnv1a64("STOP"): /* ... */ break; } -```text +``` 编译器会在编译期算出 `fnv1a64("START")` 的值,switch 语句变成了一组整数比较,非常高效。 @@ -43,7 +43,7 @@ static_assert(h1 != h2, "Different strings must produce different hashes"); // 运行时使用 std::string_view std::string_view sv = "some_string"; uint64_t h3 = fnv1a64(sv); -```text +``` ### 32 位哈希 @@ -53,7 +53,7 @@ constexpr uint32_t h32 = fnv1a32("TokenA"); // 运行时 uint32_t h = fnv1a32(std::string_view("dynamic")); -```text +``` 32 位版本适用于内存受限的场景,但碰撞概率比 64 位高。如果哈希表不大(几千个条目以内),32 位通常够用。 @@ -64,7 +64,7 @@ uint32_t h = fnv1a32(std::string_view("dynamic")); constexpr uint64_t h1 = fnv1a64("data"); // 默认种子 constexpr uint64_t h2 = fnv1a64("data", 12345678ULL); // 自定义种子 // h1 != h2,相同输入不同种子产生不同哈希 -```text +``` ### 用户自定义字面量 @@ -74,7 +74,7 @@ using namespace cf::hash; // "_hash" 后缀,编译期计算 constexpr uint64_t h = "MyToken"_hash; static_assert(h == fnv1a64("MyToken"), "UDL must match function call"); -```text +``` `_hash` 字面量让代码更简洁,特别是在 switch-case 里: @@ -86,7 +86,7 @@ switch (fnv1a64(cmd)) { case "save"_hash: save_file(); break; default: unknown_cmd(); break; } -```text +``` ## FNV-1a 算法 @@ -98,7 +98,7 @@ for each byte in input: hash = hash XOR byte hash = hash * prime return hash -```bash +``` 参数值: @@ -138,7 +138,7 @@ if (it != hashmap.end()) { // 确认是真正的匹配 } } -```text +``` 3. **不要用于安全场景**:FNV-1a 不是加密哈希,不应该用于密码存储、完整性校验等安全场景。 diff --git a/document/HandBook/base/linux/proc_parser.md b/document/HandBook/base/linux/proc_parser.md index bdd63480f..b7d0af6dc 100644 --- a/document/HandBook/base/linux/proc_parser.md +++ b/document/HandBook/base/linux/proc_parser.md @@ -21,7 +21,7 @@ auto model = cf::parse_cpuinfo_field(line, "model name"); // 字段不存在时返回空视图 auto missing = cf::parse_cpuinfo_field(line, "vendor_id"); // missing.data() == nullptr -```text +``` 实际使用时通常是逐行读取: @@ -41,7 +41,7 @@ while (std::getline(cpuinfo, line)) { std::cout << "厂商: " << vendor << std::endl; } } -```text +``` ## 字符串去空格 @@ -52,7 +52,7 @@ std::string_view sv = " hello world "; auto trimmed = cf::trim_whitespace(sv); // "hello world" auto left_trimmed = cf::ltrim_whitespace(sv); // "hello world " auto right_trimmed = cf::rtrim_whitespace(sv);// " hello world" -```text +``` ## 解析缓存大小 @@ -63,7 +63,7 @@ auto size1 = cf::parse_cache_size("32K"); // 32 auto size2 = cf::parse_cache_size("1M"); // 1024 auto size3 = cf::parse_cache_size("2G"); // 2097152 auto invalid = cf::parse_cache_size("xyz"); // std::nullopt -```text +``` 这在读取 `/sys/devices/system/cpu/cpu0/cache/index*/size` 时特别有用: @@ -73,7 +73,7 @@ auto size_kb = cf::parse_cache_size(l1_size); if (size_kb) { std::cout << "L1 缓存: " << *size_kb << " KB" << std::endl; } -```text +``` ## 解析数字 @@ -84,7 +84,7 @@ auto value = cf::parse_uint32("4096"); // 4096 auto hex1 = cf::parse_hex_uint32("0x41"); // 65 auto hex2 = cf::parse_hex_uint32("FF"); // 255 auto invalid = cf::parse_uint32("abc"); // std::nullopt -```text +``` 十六进制解析在处理 ARM 的 `CPU implementer` 字段时很常见: @@ -94,7 +94,7 @@ if (auto impl_val = cf::parse_hex_uint32(impl)) { auto vendor = cf::arm_implementer_to_vendor(*impl_val); // impl_val = 0x41 -> vendor = "ARM" } -```text +``` ## 读取文件 @@ -107,7 +107,7 @@ auto max_freq = cf::read_uint32_file( if (max_freq.has_value()) { std::cout << "最大频率: " << *max_freq << " kHz" << std::endl; } -```text +``` ⚠️ 这个函数会执行文件 I/O,可能因为权限或文件不存在而失败。返回 `std::nullopt` 时可以判断是文件问题还是解析失败。 @@ -120,7 +120,7 @@ auto vendor = cf::arm_implementer_to_vendor(0x41); // "ARM" auto vendor2 = cf::arm_implementer_to_vendor(0x51); // "Qualcomm" auto vendor3 = cf::arm_implementer_to_vendor(0x69); // "Intel" auto unknown = cf::arm_implementer_to_vendor(0xFF); // "Unknown" -```text +``` 支持的厂商 ID 包括 ARM (0x41)、Broadcom (0x42)、Qualcomm (0x51)、NVIDIA (0x4E) 等。 @@ -137,7 +137,7 @@ std::string_view bad() { std::string_view good(const std::string& line) { return cf::parse_cpuinfo_field(line, "model name"); // OK,调用方保证 line 有效 } -```text +``` ## 相关文档 diff --git a/document/HandBook/base/macro/plain_property.md b/document/HandBook/base/macro/plain_property.md index 5ac5d4ed1..bacb0b557 100644 --- a/document/HandBook/base/macro/plain_property.md +++ b/document/HandBook/base/macro/plain_property.md @@ -28,7 +28,7 @@ class WidgetConfig { public: // 其他成员方法... }; -```text +``` ## 宏展开结果 @@ -48,7 +48,7 @@ public: private: val_type val_name{default_value}; -```text +``` 注意成员变量直接在 `private` 区域声明,而访问器在 `public` 区域。这是因为宏本身包含了访问修饰符,所以使用时不需要额外考虑这些。 @@ -83,7 +83,7 @@ int main() { return 0; } -```text +``` ## 方法命名 @@ -126,7 +126,7 @@ public: void method(); CF_PLAIN_PROPERTY(int, x, 0) // 会插入 public:/private:,破坏原有结构 }; -```text +``` ## 适用场景 diff --git a/document/HandBook/base/macro/system_judge.md b/document/HandBook/base/macro/system_judge.md index 00359ec94..8f0dfadd4 100644 --- a/document/HandBook/base/macro/system_judge.md +++ b/document/HandBook/base/macro/system_judge.md @@ -21,7 +21,7 @@ description: 定义了平台和架构检测的底层宏,是整个项目跨平 #if defined(__linux__) # define CFDESKTOP_OS_LINUX #endif -```text +``` Windows 的检测用了两个宏是因为 `_WIN32` 在 64 位 Windows 上也会被定义,而 `_WIN64` 只在 64 位上定义。用 `||` 连接可以覆盖所有情况。Linux 的 `__linux__` 则是所有类 Linux 系统通用的宏,如果你需要区分发行版,得在运行时检查 `/etc/os-release`。 @@ -44,7 +44,7 @@ CPU 架构检测主要服务于性能优化和 SIMD 指令使用: #if defined(__arm__) || defined(_M_ARM) # define CFDESKTOP_ARCH_ARM32 #endif -```text +``` 这里的 `__x86_64__` 和 `__aarch64__` 是 GCC/Clang 的约定,`_M_X64`、`_M_ARM64`、`_M_ARM` 是 MSVC 的约定。用多个宏检查是因为不同编译器的命名习惯不一样。 @@ -56,7 +56,7 @@ CPU 架构检测主要服务于性能优化和 SIMD 指令使用: #if defined() # define CFDESKTOP__ #endif -```text +``` 不带值的设计是有意为之的。我们只需要知道"是不是这个平台",不需要传递额外信息。如果需要细分版本(比如 Windows 10 vs Windows 11),应该在运行时检测,而不是用编译时宏。 @@ -80,7 +80,7 @@ CPU 架构检测主要服务于性能优化和 SIMD 指令使用: #elif defined(CFDESKTOP_ARCH_ARM64) // 使用 NEON 指令集 #endif -```bash +``` ⚠️ 记得在 `#else` 或 `#elif` 分支加上 `#error`,避免在不支持的平台上静默编译通过,结果运行时出错。 @@ -111,7 +111,7 @@ CPU 架构检测主要服务于性能优化和 SIMD 指令使用: #if defined(CFDESKTOP_OS_NEW_OS) // 新平台实现 #endif -```text +``` 记得同步更新文档和测试,确保 CI 环境能在新平台上正常运行。 diff --git a/document/HandBook/base/macros.md b/document/HandBook/base/macros.md index afbd88788..2e1c96f06 100644 --- a/document/HandBook/base/macros.md +++ b/document/HandBook/base/macros.md @@ -20,7 +20,7 @@ description: 是 CFDesktop 项目的宏定义入口文件,本身不直接定 #elif defined(CFDESKTOP_OS_LINUX) // Linux 特定代码 #endif -```text +``` 检测逻辑基于编译器预定义宏。Windows 平台检查 `_WIN32` 或 `_WIN64`,Linux 平台检查 `__linux__`。这些是主流编译器(MSVC、GCC、Clang)都遵守的约定。 @@ -36,7 +36,7 @@ description: 是 CFDesktop 项目的宏定义入口文件,本身不直接定 #elif defined(CFDESKTOP_ARCH_ARM32) // ARM32 特定代码 #endif -```text +``` x86-64 检查 `__x86_64__`、`_M_X64` 或 `__amd64__`,ARM64 检查 `__aarch64__` 或 `_M_ARM64`,ARM32 检查 `__arm__` 或 `_M_ARM`。这覆盖了我们项目目标平台的所有主流架构。 @@ -71,7 +71,7 @@ void set_thread_priority(int priority) { // Linux 实现省略... #endif } -```text +``` ## 命名约定 @@ -94,7 +94,7 @@ void set_thread_priority(int priority) { #if defined(__NEW_OS__) # define CFDESKTOP_OS_NEW_OS #endif -```text +``` 然后记得在对应的地方添加平台特定实现,并写测试确保在新平台上能正确编译和运行。 diff --git a/document/HandBook/base/mpsc_queue.md b/document/HandBook/base/mpsc_queue.md index 912c38e51..9f9d43f2f 100644 --- a/document/HandBook/base/mpsc_queue.md +++ b/document/HandBook/base/mpsc_queue.md @@ -31,7 +31,7 @@ int value; if (queue.tryPop(value)) { std::cout << "Got: " << value << std::endl; // Got: 42 } -```text +``` ### 批量操作 @@ -49,7 +49,7 @@ size_t popped = queue.tryPopBatch(buffer, 32); for (size_t i = 0; i < popped; ++i) { process(buffer[i]); } -```text +``` ### 容量查询 @@ -62,7 +62,7 @@ size_t approx_size = queue.size(); // 是否为空(近似值) bool empty = queue.empty(); -```text +``` ## 容量要求 @@ -72,7 +72,7 @@ bool empty = queue.empty(); cf::lockfree::MpscQueue q1; // OK: 1024 = 2^10 cf::lockfree::MpscQueue q2; // OK: 2048 = 2^11 cf::lockfree::MpscQueue q3; // 编译错误:static_assert 失败 -```text +``` 选择容量时应该考虑最坏情况下的生产速率和消费速率之差。如果生产者短暂 burst 产生了很多数据,队列需要有足够的缓冲空间。 @@ -118,7 +118,7 @@ while (seq != pos) { #endif seq = cell->sequence.load(std::memory_order_acquire); } -```text +``` 这意味着如果消费者跟不上,生产者线程会被阻塞在自旋中。如果你的场景可能出现持续的生产过剩,需要在上层做背压控制。 @@ -129,7 +129,7 @@ struct Cell { std::atomic sequence; // 序列号 alignas(alignof(T)) unsigned char storage[sizeof(T)]; // 原始存储 }; -```text +``` 每个槽位的存储是 `alignas(T)` 的原始字节数组,通过 placement new 构造对象。这样避免了不必要的默认构造,也支持没有默认构造函数的类型。 @@ -137,7 +137,7 @@ struct Cell { ```cpp char padding_[64 - sizeof(readPos_) - sizeof(writePos_) - sizeof(buffer_) % 64]; -```bash +``` ## 线程安全 diff --git a/document/HandBook/base/once_init.md b/document/HandBook/base/once_init.md index a9a41a78c..a4895d638 100644 --- a/document/HandBook/base/once_init.md +++ b/document/HandBook/base/once_init.md @@ -29,7 +29,7 @@ protected: return init_resources(); } }; -```text +``` 首次调用 `get_resources()` 时,`init_resources()` 会被自动调用。后续调用直接返回缓存的资源,不会再执行初始化。 @@ -51,7 +51,7 @@ void print_cpu_info() { auto& info2 = g_cpu_cache.get_resources(); assert(&info == &info2); // 同一个对象 } -```text +``` ## 重新初始化 @@ -60,7 +60,7 @@ void print_cpu_info() { ```cpp // 强制重新初始化 g_cpu_cache.force_reinit(); -```text +``` 带参数的版本可以在重新初始化时传入新参数: @@ -84,7 +84,7 @@ private: // 使用 config.force_reinit("/etc/app/new_config.json"); -```text +``` ⚠️ `force_reinit()` 不是线程安全的。如果可能和 `get_resources()` 并发调用,需要自己加锁保护。 @@ -105,7 +105,7 @@ protected: SystemInfoCache sys_info; auto& info = sys_info.get_resources(); std::cout << "CPU 核心数: " << info.cpu_count << std::endl; -```text +``` ### 延迟加载配置 @@ -128,7 +128,7 @@ ConfigCache config; if (config.get_resources().debug_mode) { // ... } -```text +``` ## 线程安全保证 @@ -142,7 +142,7 @@ std::thread t2([&]() { auto& info = cache.get_resources(); }); t1.join(); t2.join(); // init_resources() 只被执行一次 -```text +``` 但 `force_reinit()` 不是线程安全的。如果需要在线程间重新初始化,必须加锁: @@ -150,7 +150,7 @@ t2.join(); std::mutex cache_mutex; std::lock_guard lock(cache_mutex); cache.force_reinit(); // 现在安全了 -```text +``` ## 注意事项 diff --git a/document/HandBook/base/overview.md b/document/HandBook/base/overview.md index 4a0466b81..dc66fa64e 100644 --- a/document/HandBook/base/overview.md +++ b/document/HandBook/base/overview.md @@ -32,7 +32,7 @@ if (result.has_value()) { } else { // 根据 error() 的值决定怎么恢复 } -```text +``` ## 容器视图 @@ -48,7 +48,7 @@ int arr[] = {1, 2, 3}; process(vec); // OK process(arr); // 也 OK -```text +``` ## 资源管理 @@ -63,7 +63,7 @@ process(arr); // 也 OK // 使用文件,无论中间发生什么,离开作用域都会自动关闭 } -```text +``` ## 懒加载初始化 @@ -83,7 +83,7 @@ protected: // 首次调用 get_resources() 时才执行初始化 auto& info = cache.get_resources(); -```text +``` ⚠️ `force_reinit()` 不是线程安全的,如果需要在运行时重新初始化,记得自己加锁。 @@ -99,7 +99,7 @@ std::string_view model = cf::parse_cpuinfo_field(line, "model name"); // 直接读单个数字值的文件 auto freq = cf::read_uint32_file("/sys/devices/system/cpu/cpu0/cpufreq/max_freq"); -```text +``` ## 弱引用 @@ -123,7 +123,7 @@ auto weak = manager.GetWeakPtr(); if (weak) { weak->ApplyTheme(); // 安全访问 } -```text +``` ## 平台检测 @@ -137,7 +137,7 @@ if (weak) { #elif defined(CFDESKTOP_OS_LINUX) // Linux 特定代码 #endif -```text +``` ## 相关文档 diff --git a/document/HandBook/base/policy_chain.md b/document/HandBook/base/policy_chain.md index a82e31f70..6fdba1bb9 100644 --- a/document/HandBook/base/policy_chain.md +++ b/document/HandBook/base/policy_chain.md @@ -24,7 +24,7 @@ std::optional get_font(const std::string& name) { if (auto f = try_builtin(name)) return f; return std::nullopt; } -```text +``` 这看起来还好,但当策略数量增加、策略需要动态注册时,硬编码的 if-else 就不够灵活了。PolicyChain 把这些策略变成可组合、可动态管理的链。 @@ -52,7 +52,7 @@ auto result = chain.execute("42"); if (result) { std::cout << "Result: " << *result << std::endl; // Result: 42 } -```text +``` ### 使用工厂函数 @@ -73,7 +73,7 @@ auto chain = cf::make_policy_chain( auto r1 = chain.execute("hello"); // 返回 std::optional(5) — stoi 失败但 length 可用 auto r2 = chain.execute("123"); // 返回 std::optional(123) — stoi 成功 auto r3 = chain.execute(""); // 返回 std::optional(0) — 兜底策略 -```text +``` `make_policy_chain` 按参数顺序添加策略,第一个参数优先级最高。 @@ -97,7 +97,7 @@ auto chain = cf::policy_chain_builder() auto r1 = chain.execute(5); // 10 — 第一个策略处理 auto r2 = chain.execute(-3); // 3 — 第二个策略处理 auto r3 = chain.execute(0); // 0 — 兜底策略 -```text +``` Builder 的 `then` 按调用顺序添加策略,先添加的优先级高。 @@ -124,7 +124,7 @@ template class PolicyChain { [[nodiscard]] bool empty() const; [[nodiscard]] SizeType size() const; }; -```text +``` ### 工厂函数和 Builder @@ -136,7 +136,7 @@ auto make_policy_chain(Policies&&... policies); // Builder 创建器 template auto policy_chain_builder(); -```text +``` ## 执行语义 @@ -153,7 +153,7 @@ if (result) { } else { // 所有策略都未能处理 } -```text +``` ## 典型使用场景 @@ -175,7 +175,7 @@ auto renderer_chain = cf::make_policy_chain( ); auto renderer = renderer_chain.execute(); -```text +``` ### 配置值解析 @@ -191,7 +191,7 @@ auto config_chain = cf::policy_chain_builder() return get_default(key); // 最后默认值 }) .build(); -```text +``` ### 资源加载 @@ -207,7 +207,7 @@ auto loader_chain = cf::make_policy_chain( return try_load_from_network(path); } ); -```text +``` ## 线程安全 diff --git a/document/HandBook/base/scope_guard.md b/document/HandBook/base/scope_guard.md index d9c07baa9..e53cfc13e 100644 --- a/document/HandBook/base/scope_guard.md +++ b/document/HandBook/base/scope_guard.md @@ -34,7 +34,7 @@ void process_file(const std::string& path) { free(buffer); // 三个出口,三个地方写清理代码 fclose(f); } -```text +``` 每个可能的返回路径都要记得清理所有资源,漏一个就泄漏。用 `ScopeGuard` 就简单多了: @@ -53,7 +53,7 @@ void process_file(const std::string& path) { // 更多代码... } -```text +``` 无论从哪个路径退出,`ScopeGuard` 都会执行对应的清理代码。你不需要在每个返回点都写一遍,也不容易漏。 @@ -74,7 +74,7 @@ void process_file(const std::string& path) { // 做一些事情... // 离开作用域时 counter 变成 42 } -```text +``` lambda 按引用捕获 `counter`,所以在守卫内部可以修改它。你也可以按值捕获,看具体需求。 @@ -100,7 +100,7 @@ void save_config(const std::string& path) { cleanup.dismiss(); // 成功,不需要删除临时文件 } } -```text +``` `dismiss()` 是不可逆的,一旦调用就不能再恢复。多次调用 `dismiss()` 是安全的,不会有额外效果。 @@ -117,7 +117,7 @@ void save_config(const std::string& path) { cf::ScopeGuard guard3([&order]() { order.push_back(3); }); } // order = {3, 2, 1} -```text +``` 这和 C++ 局部变量的析构顺序一致——后创建的先析构。这个顺序很重要,如果多个守卫之间有依赖,你需要知道哪个先执行。比如先分配的资源应该后释放(LIFO),正好符合这个顺序。 @@ -136,7 +136,7 @@ try { } catch (...) { // 异常被捕获,但清理已经执行 } -```text +``` ⚠️ 如果清理代码本身抛出异常,这个异常会传播出去。如果在栈展开过程中(已经有一个异常在处理)清理代码又抛出异常,程序会调用 `std::terminate`。所以确保清理代码不会抛异常,或者把可能抛异常的代码用 `try-catch` 包起来。 @@ -153,7 +153,7 @@ void read_config(const std::string& path) { // 使用文件... // 离开作用域自动关闭 } -```text +``` ### 状态回滚 @@ -169,7 +169,7 @@ void update_state(State& s) { rollback.dismiss(); // 成功,不需要回滚 } -```text +``` ### 锁的释放 @@ -180,7 +180,7 @@ void critical_section() { // 临界区代码... } -```text +``` 当然更推荐直接用 `std::lock_guard` 或 `std::unique_lock`,但 `ScopeGuard` 可以处理更复杂的场景。 @@ -200,7 +200,7 @@ void process_item(Item& item) { // 离开作用域自动恢复 } -```text +``` ## 限制和注意事项 @@ -211,7 +211,7 @@ cf::ScopeGuard guard1([]() {}); cf::ScopeGuard guard2 = guard1; // 编译错误 cf::ScopeGuard guard3 = std::move(guard1); // 编译错误 -```text +``` 这个设计是为了确保清理代码只执行一次。如果允许复制,同一个守卫可能被复制到多个地方,不清楚应该由谁负责清理。如果允许移动,移动后原守卫的清理代码就不应该再执行,但这会让语义变得复杂。 @@ -225,7 +225,7 @@ cf::ScopeGuard guard([ptr]() {}); // 编译错误 // 可以这样 auto ptr = std::make_unique(42); cf::ScopeGuard guard([&ptr]() {}); // 按引用捕获 -```text +``` ## 性能考虑 @@ -258,7 +258,7 @@ for (int i = 0; i < 10; ++i) { end:; } // 守卫仍然执行 -```text +``` 无论控制流怎么跳转,只要离开了守卫所在的作用域,清理代码就会执行。这得益于 C++ 的 RAII 机制——析构函数总会在作用域结束时被调用。 diff --git a/document/HandBook/base/singleton.md b/document/HandBook/base/singleton.md index fe89380dd..31ee1d711 100644 --- a/document/HandBook/base/singleton.md +++ b/document/HandBook/base/singleton.md @@ -35,7 +35,7 @@ using LoggerSingleton = cf::SimpleSingleton; // 在任何地方 LoggerSingleton::instance().log("Hello"); -```text +``` 不需要手动初始化,第一次调用 `instance()` 时自动构造。后续调用返回同一个实例。 @@ -55,7 +55,7 @@ private: // 使用 auto& mgr = WindowManager::instance(); mgr.create_window("Main"); -```text +``` 注意把构造函数设为 `private` 或 `protected`,并声明 `SimpleSingleton` 为友元。 @@ -93,7 +93,7 @@ DBSingleton::init("host=localhost port=5432", 10); // 使用 DBSingleton::instance().query("SELECT * FROM users"); -```text +``` ### 初始化时机控制 @@ -115,7 +115,7 @@ int main() { // 阶段 3:运行 run_app(); } -```text +``` 如果调用 `instance()` 之前没有调用 `init()`,会抛出 `std::logic_error`: @@ -126,7 +126,7 @@ try { } catch (const std::logic_error& e) { // "Singleton not initialized. Call init() first." } -```text +``` ### 重置单例 @@ -142,7 +142,7 @@ void test_something() { // 下次使用前需要重新 init cf::Singleton::init("production_config.json"); } -```text +``` `reset()` 之后必须重新调用 `init()` 才能使用 `instance()`。 @@ -153,7 +153,7 @@ void test_something() { ```cpp cf::Singleton::init("config1.json"); // 生效 cf::Singleton::init("config2.json"); // 被忽略,仍使用 config1 -```bash +``` ## 两种单例对比 diff --git a/document/HandBook/base/span.md b/document/HandBook/base/span.md index 67504e060..22a726504 100644 --- a/document/HandBook/base/span.md +++ b/document/HandBook/base/span.md @@ -17,7 +17,7 @@ void process(const std::vector& data); // 只能接受 C 数组(但会退化成指针,丢失长度) void process(int* data, size_t size); -```text +``` 第一种限制了调用方必须用 `vector`,第二种需要手动传长度而且容易出错。用 `span` 就没有这些问题: @@ -32,7 +32,7 @@ int c_arr[] = {1, 2, 3}; process(vec); // OK process(arr); // OK process(c_arr); // OK -```text +``` ## 构造方式 @@ -55,7 +55,7 @@ cf::span s3 = arr2; // 手动指定指针和长度 cf::span s4(vec.data(), vec.size()); -```text +``` ## 元素访问 @@ -68,7 +68,7 @@ int first = s[0]; // 下标访问 int first2 = s.front(); // 首元素 int last = s.back(); // 末元素 int* ptr = s.data(); // 底层指针 -```text +``` ⚠️ `operator[]` 不做边界检查,越界访问是未定义行为。如果需要安全检查,标准库提供了 `at()` 方法,但我们的实现里为了性能省略了。 @@ -90,7 +90,7 @@ auto middle = s.subspan(2, 4); // {3, 4, 5, 6} // 从位置 2 到末尾 auto tail = s.subspan(2); // {3, 4, 5, 6, 7, 8, 9, 10} -```text +``` 切片返回的新 `span` 仍指向原始数据,只是起始位置和长度不同。这意味着切片操作是 O(1) 的,没有任何拷贝开销。 @@ -116,7 +116,7 @@ process_packet(buffer); uint8_t stack_buf[256]; size_t received = recv(sock, stack_buf, 256, 0); process_packet(cf::span(stack_buf, received)); -```text +``` ## const 正确性 @@ -130,7 +130,7 @@ const std::vector vec = {1, 2, 3}; read_only(vec); // OK read_write(vec); // 编译错误 -```text +``` ## 生命周期陷阱 @@ -146,7 +146,7 @@ cf::span get_bad_span() { cf::span get_good_span(const std::vector& vec) { return vec; // OK,调用方保证 vec 有效 } -```text +``` 这个坑在异步代码里特别容易出现——如果在一个线程里创建 `span`,另一个线程里使用,必须确保原始数据的生命周期足够长。 diff --git a/document/HandBook/base/weak_ptr.md b/document/HandBook/base/weak_ptr.md index 74d85ba25..30d459b07 100644 --- a/document/HandBook/base/weak_ptr.md +++ b/document/HandBook/base/weak_ptr.md @@ -41,7 +41,7 @@ auto weak_ref = manager.GetWeakPtr(); if (weak_ref) { // 检查对象是否存活 weak_ref->ApplyTheme(); } -```text +``` ⚠️ `WeakPtrFactory` 必须声明为类的最后一个成员。C++ 按声明顺序的逆序销毁成员,这样可以确保工厂先失效,其他成员的析构函数中如果持有弱引用也能正确检测到失效。 @@ -65,7 +65,7 @@ if (MyClass* ptr = weak.Get()) { // 方式三:直接解引用(会断言,仅确定对象存在时使用) *weak; // 如果无效会触发 assert weak->Method(); // 同上 -```cpp +``` 直接解引用会触发断言,这是有意为之的设计。如果你用了 `operator->` 或 `operator*`,说明你已经确定对象存在,不会再检查。如果你不敢确定,应该用 `Get()` 或 `IsValid()` 先检查。 @@ -85,7 +85,7 @@ weak->Method(); // 同上 assert(!weak.IsValid()); assert(weak.Get() == nullptr); -```text +``` 这个设计避免了 `shared_ptr` 的隐式生命周期延长问题。持有 `WeakPtr` 不会阻止对象被销毁,这也是它和 `std::weak_ptr` 的核心区别之一。同时需要注意的是,`WeakPtr` 内部持有的是指向工厂内嵌标志的裸指针(而非 `shared_ptr`),因此 `WeakPtr` 的生命周期**绝不能**超过工厂——工厂析构后再访问 `WeakPtr` 是未定义行为。 @@ -120,7 +120,7 @@ cf::WeakPtr derived_again = if (derived_again) { derived_again->DerivedMethod(); } -```text +``` `DynamicCast` 会在运行时检查类型,如果转换失败返回无效的 `WeakPtr`。这个操作不是免费的,但比直接 `dynamic_cast` 原始指针要安全,因为转换失败得到的是空指针而不是未定义行为。 @@ -138,7 +138,7 @@ if (weak.IsValid()) { // 检查通过 // 正确做法:在单线程序列中使用 // 或者用其他同步机制保护整个检查+访问过程 -```text +``` 这个限制和 `std::weak_ptr::lock()` 不一样。标准库的 `lock()` 是原子的,可以返回一个 `shared_ptr` 保证对象在使用期间存活。我们选择不提供这个功能,是因为我们的设计中对象有唯一拥有者,不存在共享所有权。此外,标志直接嵌入在工厂中(无堆分配),`WeakPtr` 只持有裸指针,无法像 `shared_ptr` 那样延长对象生命周期。 @@ -175,7 +175,7 @@ assert(!weak2.IsValid()); // 失效 // 注意:失效后不能再调用 GetWeakPtr(),会触发断言失败 // auto weak3 = obj.GetWeakPtr(); // 断言失败! -```text +``` 这个功能在某些场景下很有用,比如你想显式通知所有观察者对象不再可用,但又不想真的销毁对象。与旧版实现不同,失效后**不能再创建新的弱引用**——`GetWeakPtr()` 会触发断言失败。这是因为存活标志直接嵌入在工厂中(使用 `std::atomic`),失效只是将标志设为 `false`,不会分配新的标志。如果你需要"重启"后继续创建弱引用,应该使用一个全新的工厂实例。 @@ -208,7 +208,7 @@ private: }; // resource_ 的析构函数中,如果持有 Bad 的 WeakPtr,会看到失效 -```text +``` 第二个陷阱是忘记检查有效性直接访问。这在异步代码里特别容易出现,因为回调执行时对象可能已经被销毁: @@ -226,7 +226,7 @@ post_task([weak]() { weak->Method(); } }); -```text +``` ## 相关文档 diff --git a/document/HandBook/base/weak_ptr_factory.md b/document/HandBook/base/weak_ptr_factory.md index 9b1bcc988..6225380d3 100644 --- a/document/HandBook/base/weak_ptr_factory.md +++ b/document/HandBook/base/weak_ptr_factory.md @@ -29,7 +29,7 @@ private: // 必须是最后一个成员变量 cf::WeakPtrFactory weak_factory_{this}; }; -```text +``` 构造时传入 `this` 指针,工厂会记住对象的位置。每次调用 `GetWeakPtr()` 就会创建一个新的弱引用,指向同一个对象。 @@ -49,7 +49,7 @@ private: // 工厂最后销毁 cf::WeakPtrFactory weak_factory_{this}; }; -```text +``` 如果把工厂放在中间,某些成员析构时可能仍然检测到弱引用"有效",然后尝试访问已经被析构的部分对象,后果是未定义行为。 @@ -68,7 +68,7 @@ auto weak3 = obj.GetWeakPtr(); // 所有弱引用都指向同一个对象 assert(weak1.Get() == weak2.Get()); assert(weak2.Get() == weak3.Get()); -```text +``` 每次调用都创建一个新的 `WeakPtr` 对象,但它们共享同一个内部的"存活标志"。对象销毁或调用 `InvalidateWeakPtrs()` 后,所有弱引用同时失效。存活标志(`WeakReferenceFlag`)直接嵌入在 `WeakPtrFactory` 内部,不涉及任何堆分配。所有 `WeakPtr` 实例通过裸指针引用这个标志,因此 `WeakPtr` 的创建开销极小。 @@ -93,7 +93,7 @@ public: private: cf::WeakPtrFactory weak_factory_{this}; }; -```text +``` `InvalidateWeakPtrs()` 会把内部的存活标志设为失效。失效前创建的所有弱引用都会变成无效。注意,与旧版实现不同,失效后**不能再创建新的弱引用**——后续调用 `GetWeakPtr()` 会触发断言失败。这是因为标志直接嵌入在工厂中,失效操作只是将 `std::atomic` 设为 `false`,不会分配新的标志。 @@ -112,7 +112,7 @@ private: MyClass a; MyClass b = a; // 编译错误:WeakPtrFactory 不可复制 MyClass c = std::move(a); // 编译错误:WeakPtrFactory 不可移动 -```text +``` 这个设计是有意为之的。工厂和对象的生命周期绑定在一起,复制或移动会破坏这个关系。如果你确实需要移动对象,得先清理所有弱引用,但这个场景在我们的使用中极少出现,干脆直接禁了。 @@ -140,7 +140,7 @@ private: std::vector callbacks_; cf::WeakPtrFactory weak_factory_{this}; }; -```text +``` ### 观察者模式 @@ -169,7 +169,7 @@ private: std::vector> observers_; cf::WeakPtrFactory weak_factory_{this}; }; -```text +``` ### 单次失效模式 @@ -194,7 +194,7 @@ public: private: cf::WeakPtrFactory weak_factory_{this}; }; -```text +``` ## 注意事项 diff --git a/document/HandBook/desktop/base/config_manager/01-quick-start.md b/document/HandBook/desktop/base/config_manager/01-quick-start.md index e14e45e90..2bb08d4ad 100644 --- a/document/HandBook/desktop/base/config_manager/01-quick-start.md +++ b/document/HandBook/desktop/base/config_manager/01-quick-start.md @@ -17,7 +17,7 @@ description: 本文档将引导您从零开始使用 ConfigStore 配置管理中 #include // Qt 字符串类型 using namespace cf::config; -```text +``` ### 获取单例实例 @@ -26,7 +26,7 @@ ConfigStore 使用单例模式,首次访问时自动初始化: ```cpp // 获取单例实例 auto& config = ConfigStore::instance(); -```text +``` ### 自定义路径配置 @@ -64,7 +64,7 @@ public: // 在首次使用前初始化 auto provider = std::make_shared(); ConfigStore::instance().initialize(provider); -```text +``` ## 基础操作 @@ -98,7 +98,7 @@ bool auto_save = ConfigStore::instance().query( KeyView{.group = "app", .key = "auto_save"}, true ); -```text +``` #### Optional 查询 @@ -121,7 +121,7 @@ if (auto value = ConfigStore::instance().query( KeyView{.group = "network", .key = "port"})) { std::cout << "端口: " << *value << std::endl; } -```text +``` #### 指定层查询 @@ -137,7 +137,7 @@ auto user_theme = ConfigStore::instance().query( if (user_theme.has_value()) { std::cout << "用户设置的主题: " << user_theme.value() << std::endl; } -```text +``` ### 写入配置 @@ -169,7 +169,7 @@ ConfigStore::instance().set( KeyView{.group = "app", .key = "auto_save"}, false ); -```text +``` #### 选择目标层 @@ -189,7 +189,7 @@ ConfigStore::instance().set( std::string("abc123"), Layer::Temp // 写入 Temp 层,重启后丢失 ); -```text +``` #### 通知策略 @@ -219,7 +219,7 @@ ConfigStore::instance().set( ); // 批量操作完成后,一次性触发所有 Watcher ConfigStore::instance().notify(); -```text +``` ### 键管理 @@ -247,7 +247,7 @@ if (result == RegisterResult::KeyRegisteredSuccess) { } else { std::cout << "键已存在" << std::endl; } -```text +``` #### 注销键 @@ -268,7 +268,7 @@ auto result = ConfigStore::instance().unregister_key( if (result == UnRegisterResult::KeyUnRegisteredSuccess) { std::cout << "键注销成功" << std::endl; } -```text +``` #### 检查键存在 @@ -283,7 +283,7 @@ bool in_user = ConfigStore::instance().has_key( KeyView{.group = "app", .key = "theme"}, Layer::User ); -```text +``` ## 监听配置变更 @@ -303,7 +303,7 @@ auto handle = ConfigStore::instance().watch( } } ); -```text +``` ### 通配符监听 @@ -325,7 +325,7 @@ auto theme_watcher = ConfigStore::instance().watch( std::cout << "主题配置变更: " << k.full_key << std::endl; } ); -```text +``` ### 取消监听 @@ -335,7 +335,7 @@ WatcherHandle handle = ConfigStore::instance().watch("app.*", callback); // 取消监听 ConfigStore::instance().unwatch(handle); -```text +``` ### 手动通知模式 @@ -355,7 +355,7 @@ ConfigStore::instance().set(KeyView{.group = "batch", .key = "b"}, 2, // 手动触发通知 ConfigStore::instance().notify(); -```text +``` ## 持久化操作 @@ -367,7 +367,7 @@ ConfigStore::instance().sync(SyncMethod::Async); // 同步同步(阻塞直到写入完成) ConfigStore::instance().sync(SyncMethod::Sync); -```text +``` ### 重新加载配置 @@ -375,7 +375,7 @@ ConfigStore::instance().sync(SyncMethod::Sync); // 从磁盘重新加载所有配置 // 注意:这会清空 Temp 层的所有临时配置 ConfigStore::instance().reload(); -```text +``` ### 查看待写入变更 @@ -383,7 +383,7 @@ ConfigStore::instance().reload(); // 获取待同步的变更数量 size_t pending = ConfigStore::instance().pending_changes(); std::cout << "待同步变更数: " << pending << std::endl; -```text +``` ## 完整示例:应用程序配置管理 @@ -517,7 +517,7 @@ int main() { return 0; } -```text +``` ## 下一步 diff --git a/document/HandBook/desktop/base/config_manager/04-architecture.md b/document/HandBook/desktop/base/config_manager/04-architecture.md index f2856608a..6c64a9035 100644 --- a/document/HandBook/desktop/base/config_manager/04-architecture.md +++ b/document/HandBook/desktop/base/config_manager/04-architecture.md @@ -31,7 +31,7 @@ ConfigStore 采用四层优先级架构,实现了配置的层次化管理和 | - 系统级配置,{app_dir}/system.ini (CFDesktop 自管理目录) | | - 全局默认配置,只读或需要特权写入 | +-----------------------------------------------+ -```cpp +``` **查询顺序(优先级从高到低)**:Temp -> App -> User -> System @@ -55,7 +55,7 @@ ConfigStore 使用 Pimpl(Pointer to Implementation)模式实现接口与实 | - 单例继承 | | - Watcher 机制 | | | | - 线程同步 | +------------------+ +------------------------+ -```text +``` **优势**: 1. **ABI 稳定性**:实现变更不影响公共头文件,无需重新编译依赖代码 @@ -76,7 +76,7 @@ class SimpleSingleton { return target; } }; -```text +``` **特点**: - **线程安全**:C++11 标准保证静态局部变量初始化的线程安全性 @@ -113,7 +113,7 @@ template [[nodiscard]] RegisterResult register_key(const Key& key, const Value& init_value, Layer layer = Layer::App, NotifyPolicy notify_policy = NotifyPolicy::Immediate); -```text +``` **类型转换机制**(`detail::any_cast`): - 直接类型匹配:`std::any` 直接包含目标类型 @@ -155,7 +155,7 @@ private: std::vector pending_changes_; std::vector deferred_events_; }; -```text +``` **内部方法架构**: @@ -170,7 +170,7 @@ private: | clear() | | clear_impl() | | clear_layer() | | clear_layer_impl() | +------------------+ +------------------------+ -```text +``` 这种设计避免了在已持锁场景下的重复加锁,提高了效率。 @@ -190,7 +190,7 @@ public: virtual QString app_filename() const = 0; virtual bool is_layer_enabled(int layer_index) const = 0; }; -```bash +``` **默认实现**:`DesktopConfigStorePathProvider` @@ -217,7 +217,7 @@ struct Key { std::string full_key; // 完整键,如 "app.theme.name" std::string full_description; // 完整描述 }; -```text +``` **转换逻辑**: @@ -227,7 +227,7 @@ struct Key { // Key -> KeyView "app.theme.name" => group="app.theme", key="name" -```text +``` **验证规则**(`default_policy`): - 只允许字母、数字、下划线和点号 @@ -275,7 +275,7 @@ struct Key { | v 返回给用户 -```text +``` ### 3.2 写入流程 @@ -321,7 +321,7 @@ struct Key { | v 返回结果 -```text +``` ### 3.3 Watcher 触发机制 @@ -353,7 +353,7 @@ struct Key { | v 完成 -```text +``` **延迟回调机制的关键**: 1. 在主锁内收集事件(避免回调中死锁) @@ -381,7 +381,7 @@ struct Key { | | | | v v v v 返回值 -------> 下层 ---------> 下层 --------> 默认值 -```text +``` ## 4. 线程安全 @@ -397,7 +397,7 @@ std::shared_lock lock(mutex_); // query(), has_key() // 写操作:独占锁,独占访问 std::unique_lock lock(mutex_); // set(), register_key(), etc. -```bash +``` **并发场景分析**: @@ -444,7 +444,7 @@ void execute_deferred_watchers() { event.callback(...); // 安全执行,无主锁 } } -```text +``` **锁分离设计**: - `mutex_`:保护配置数据和 watcher 列表 @@ -456,7 +456,7 @@ void execute_deferred_watchers() { ```cpp std::atomic next_handle_{1}; // WatcherHandle 分配无需加锁 -```text +``` **内存序保证**: - 默认使用 `memory_order_seq_cst` @@ -497,7 +497,7 @@ class ConfigStore { public: void set_key_helper(std::unique_ptr helper); }; -```text +``` ### 5.2 自定义路径提供者 @@ -543,13 +543,13 @@ public: private: QString base_dir_; }; -```text +``` **使用方式**: ```cpp auto test_provider = std::make_shared("/tmp/test_config"); cf::config::ConfigStore::instance().initialize(test_provider); -```text +``` ### 5.3 扩展存储后端 @@ -588,7 +588,7 @@ class ConfigStoreImpl { void unwatch(WatcherHandle handle); NotifyResult notify(); }; -```text +``` **注意事项**: 1. 保持与现有 ConfigStoreImpl 相同的接口签名 @@ -693,7 +693,7 @@ bool exists = ConfigStore::instance().has_key(key_view); // 检查特定层 bool exists_in_app = ConfigStore::instance().has_key(key_view, Layer::App); -```text +``` ### 8.2 日志建议 @@ -747,7 +747,7 @@ private: // 使用方式 auto mock_provider = std::make_shared("/tmp/test_config"); cf::config::ConfigStore::instance().initialize(mock_provider); -```yaml +``` **单元测试**:参考 `test/config_manager/config_store_test.cpp` diff --git a/document/HandBook/desktop/base/logger/advanced_usage.md b/document/HandBook/desktop/base/logger/advanced_usage.md index cc86e894e..8a9da2471 100644 --- a/document/HandBook/desktop/base/logger/advanced_usage.md +++ b/document/HandBook/desktop/base/logger/advanced_usage.md @@ -28,7 +28,7 @@ description: 本文档介绍 CFLogger 的高级功能,包括自定义 Sink、F #include "cflog/cflog_format_factory.h" #include "cflog/formatter/console_formatter.h" #include "cflog/sinks/console_sink.h" -```text +``` ### 获取 Logger 实例 @@ -36,7 +36,7 @@ description: 本文档介绍 CFLogger 的高级功能,包括自定义 Sink、F using namespace cf::log; auto& logger = Logger::instance(); -```text +``` ### 基本用法 @@ -50,7 +50,7 @@ logger.setMininumLevel(level::INFO); // 刷新 logger.flush(); // 异步 logger.flush_sync(); // 同步 -```text +``` ## 自定义 Sink @@ -89,7 +89,7 @@ private: std::string prefix_; std::ostream& output_stream_ = std::cout; }; -```text +``` ### 使用自定义 Sink @@ -102,7 +102,7 @@ my_sink->setFormat(std::make_shared()); // 添加到 Logger Logger::instance().add_sink(my_sink); -```text +``` ### 网络 Sink 示例 @@ -142,7 +142,7 @@ private: asio::ip::tcp::socket socket_; std::vector messages_; }; -```text +``` ## 自定义 Formatter @@ -171,7 +171,7 @@ public: bool configurable() const override { return false; } }; -```text +``` ### 可配置的 Formatter @@ -219,7 +219,7 @@ public: private: std::shared_ptr config_; }; -```text +``` ## FormatterFactory 使用 @@ -247,7 +247,7 @@ auto cached = factory.get_or_create("console"); // 清除缓存 factory.clear_cache(); -```text +``` ### 运行时切换格式 @@ -267,7 +267,7 @@ sink->setFormat(factory.create("simple")); // 运行时切换到详细格式 sink->setFormat(factory.create("verbose")); -```text +``` ## 动态 Sink 管理 @@ -279,7 +279,7 @@ void enable_file_logging(const std::string& path) { file_sink->setFormat(std::make_shared()); Logger::instance().add_sink(file_sink); } -```text +``` ### 运行时移除 Sink @@ -287,13 +287,13 @@ void enable_file_logging(const std::string& path) { void disable_file_logging(FileSink* sink) { Logger::instance().remove_sink(sink); } -```text +``` ### 清除所有 Sink ```cpp Logger::instance().clear_sinks(); -```text +``` ## 运行时配置修改 @@ -305,7 +305,7 @@ Logger::instance().setMininumLevel(level::INFO); // 晚上调试时使用 DEBUG 级别 Logger::instance().setMininumLevel(level::DEBUG); -```text +``` ### 动态修改 Formatter 配置 @@ -320,7 +320,7 @@ config->disable(FormatterFlag::COLOR); config->set_timestamp_format("%Y-%m-%d %H:%M:%S"); formatter->set_config(config); -```text +``` ## 多环境配置 @@ -345,7 +345,7 @@ void setup_dev_logging() { Logger::instance().add_sink(console); Logger::instance().setMininumLevel(level::TRACE); } -```text +``` ### 生产环境配置 @@ -380,7 +380,7 @@ void setup_prod_logging() { Logger::instance().add_sink(error_file); Logger::instance().setMininumLevel(level::INFO); } -```text +``` ## 完整示例 @@ -450,7 +450,7 @@ int main(int argc, char** argv) { Logger::instance().flush_sync(); return 0; } -```text +``` ## 下一步 diff --git a/document/HandBook/desktop/base/logger/basic_logging.md b/document/HandBook/desktop/base/logger/basic_logging.md index b8c6d2451..39e97598f 100644 --- a/document/HandBook/desktop/base/logger/basic_logging.md +++ b/document/HandBook/desktop/base/logger/basic_logging.md @@ -13,7 +13,7 @@ description: 本文档详细介绍 CFLogger 的简单 API 使用方法。 ```cpp #include "cflog/cflog.h" -```text +``` ## 日志函数详解 @@ -23,7 +23,7 @@ description: 本文档详细介绍 CFLogger 的简单 API 使用方法。 void trace(std::string_view msg, std::string_view tag = "CFLog", std::source_location loc = std::source_location::current()); -```text +``` **用途**:记录最详细的输出信息,通常用于追踪函数调用流程。 @@ -35,7 +35,7 @@ void process_request(const std::string& request) { // 处理请求... trace("请求处理完成", "HTTP"); } -```text +``` **建议**: - 生产环境通常设置为禁用 @@ -48,7 +48,7 @@ void process_request(const std::string& request) { void debug(std::string_view msg, std::string_view tag = "CFLog", std::source_location loc = std::source_location::current()); -```text +``` **用途**:记录调试信息,帮助开发者理解程序运行状态。 @@ -60,7 +60,7 @@ void connect_database(const std::string& url) { // 连接逻辑... debug("数据库连接成功", "Database"); } -```text +``` **建议**: - 开发环境默认级别 @@ -73,7 +73,7 @@ void connect_database(const std::string& url) { void info(std::string_view msg, std::string_view tag = "CFLog", std::source_location loc = std::source_location::current()); -```text +``` **用途**:记录程序正常运行的重要信息。 @@ -83,7 +83,7 @@ void info(std::string_view msg, info("应用程序启动", "App"); info("配置文件加载完成", "Config"); info("服务器启动,监听端口 8080", "Server"); -```text +``` **建议**: - 生产环境推荐的最低级别 @@ -96,7 +96,7 @@ info("服务器启动,监听端口 8080", "Server"); void warning(std::string_view msg, std::string_view tag = "CFLog", std::source_location loc = std::source_location::current()); -```text +``` **用途**:记录潜在的问题,不会影响程序继续运行。 @@ -109,7 +109,7 @@ void load_config(const std::string& path) { // 使用默认配置... } } -```text +``` **建议**: - 记录可恢复的异常情况 @@ -121,7 +121,7 @@ void load_config(const std::string& path) { ```cpp void error(std::string_view msg, std::source_location loc = std::source_location::current()); -```text +``` **用途**:记录错误和异常情况。 @@ -133,7 +133,7 @@ void save_file(const std::string& path) { error("文件保存失败: " + path, "FileIO"); } } -```text +``` **重要特性**: - **ERROR 日志永不丢失**:即使队列满,ERROR 日志也会被保留 @@ -146,7 +146,7 @@ void save_file(const std::string& path) { ```cpp void set_level(level lvl); -```text +``` 设置全局最低日志级别,低于此级别的日志将被过滤。 @@ -170,7 +170,7 @@ int main() { flush(); return 0; } -```bash +``` ### 不同环境的推荐级别 @@ -192,7 +192,7 @@ info("用户登录成功", "Auth"); info("查询数据库", "Database"); warning("缓存未命中", "Cache"); error("连接超时", "Network"); -```bash +``` ### 推荐的标签命名 @@ -219,7 +219,7 @@ error("连接超时", "Network"); ```cpp void flush(); -```text +``` 异步刷新,请求工作线程处理队列,立即返回。 @@ -227,7 +227,7 @@ void flush(); info("重要操作开始"); do_something(); flush(); // 确保日志写入 -```text +``` **适用场景**: - 需要确保日志及时写入 @@ -237,7 +237,7 @@ flush(); // 确保日志写入 ```cpp void flush_sync(); // 高级 API -```text +``` 同步刷新,等待所有日志写入完成。 @@ -245,7 +245,7 @@ void flush_sync(); // 高级 API #include "cflog/cflog.hpp" Logger::instance().flush_sync(); -```text +``` **适用场景**: - 程序退出前 @@ -297,7 +297,7 @@ int main() { return 0; } -```bash +``` ## 与高级 API 的对比 diff --git a/document/HandBook/desktop/base/logger/best_practices.md b/document/HandBook/desktop/base/logger/best_practices.md index 81d2f6009..5ef439bcd 100644 --- a/document/HandBook/desktop/base/logger/best_practices.md +++ b/document/HandBook/desktop/base/logger/best_practices.md @@ -39,7 +39,7 @@ void process_user_login(const std::string& username) { trace("参数 username = " + username, "Auth"); // ... 过度日志 } -```text +``` ## 标签使用 @@ -55,7 +55,7 @@ info("缓存更新", "cache"); // 小写也可以,保持一致 info("连接成功", "db"); // 过于简短 info("请求处理", "HttpRequestHandler"); // 过于详细 info("缓存更新", "c"); // 意义不明 -```text +``` ### 按模块划分标签 @@ -80,7 +80,7 @@ public: debug("发送数据: " + data, "Network"); } }; -```bash +``` ### 常用标签建议 @@ -109,7 +109,7 @@ warning("查询耗时 " + std::to_string(duration_ms) + "ms 超过阈值", "Data error("文件打开失败", "FileIO"); info("用户登录", "Auth"); warning("查询慢", "Database"); -```text +``` ### 结构化消息 @@ -120,7 +120,7 @@ info("请求处理 | method=POST | path=/api/users | duration=50ms | status=200" // ✅ 使用键值对 error("数据库错误 | code=" + std::to_string(err.code) + " | msg=" + err.message + " | query=" + query, "Database"); -```text +``` ### 避免敏感信息 @@ -131,7 +131,7 @@ info("用户登录: user=admin&password=123456", "Auth"); // ✅ 脱敏处理 info("用户登录: user=admin&password=****", "Auth"); info("信用卡支付: ****-****-****-" + last4, "Payment"); -```text +``` ## 性能考虑 @@ -153,7 +153,7 @@ void process_data(const std::vector& items) { } info("完成处理 " + std::to_string(items.size()) + " 个项目"); } -```text +``` ### 延迟计算 @@ -165,7 +165,7 @@ trace("调试信息: " + expensive_computation()); // 即使 TRACE 被过滤也 if (should_log(level::TRACE)) { trace("调试信息: " + expensive_computation()); } -```text +``` ### 字符串拼接 @@ -179,7 +179,7 @@ info(msg); // ✅ 单次拼接 info("用户: " + username + ", 操作: " + action); -```text +``` ## 线程安全 @@ -202,7 +202,7 @@ int main() { t.join(); } } -```text +``` ### 共享资源的日志 @@ -220,7 +220,7 @@ private: std::mutex mutex_; int counter_ = 0; }; -```text +``` ## 错误处理 @@ -242,7 +242,7 @@ try { error("数据库连接失败 | url=" + url + " | error=" + std::string(e.what()), "Database"); } -```text +``` ### 关键操作前后 @@ -259,7 +259,7 @@ void save_to_file(const std::string& path, const Data& data) { throw; } } -```text +``` ## 启动和关闭 @@ -280,7 +280,7 @@ int main(int argc, char** argv) { return 0; } -```text +``` ### 应用关闭 @@ -296,7 +296,7 @@ int main(int argc, char** argv) { return 0; } -```text +``` ## 配置管理 @@ -324,7 +324,7 @@ void setup_logging(Environment env) { break; } } -```text +``` ### 动态调整 @@ -344,7 +344,7 @@ public: Logger::instance().setMininumLevel(static_cast(new_level)); } }; -```text +``` ## 日志轮转 @@ -361,7 +361,7 @@ class DailyFileSink : public ISink { // 每天创建新文件 // app_20260316.log, app_20260317.log, ... }; -```text +``` ## 单元测试 @@ -378,7 +378,7 @@ TEST(MyTest, TestSomething) { // 测试结束恢复 Logger::instance().setMininumLevel(level::WARNING); } -```text +``` ### Mock Sink @@ -409,7 +409,7 @@ TEST(LoggingTest, ErrorLogged) { ASSERT_FALSE(mock->messages.empty()); ASSERT_TRUE(mock->messages[0].find("Test error") != std::string::npos); } -```text +``` ## 检查清单 diff --git a/document/HandBook/desktop/base/logger/configuration.md b/document/HandBook/desktop/base/logger/configuration.md index 88ec43efb..889239406 100644 --- a/document/HandBook/desktop/base/logger/configuration.md +++ b/document/HandBook/desktop/base/logger/configuration.md @@ -19,7 +19,7 @@ enum class level { WARNING, // 警告信息 ERROR // 错误信息 }; -```text +``` ### 设置最低级别 @@ -29,13 +29,13 @@ set_level(level::INFO); // 高级 API Logger::instance().setMininumLevel(level::INFO); -```text +``` ### 级别关系 ```text TRACE (0) < DEBUG (1) < INFO (2) < WARNING (3) < ERROR (4) -```bash +``` 设置某级别后,只有该级别及以上的日志会被记录: @@ -66,7 +66,7 @@ void setup_logging_by_env() { Logger::instance().setMininumLevel(lvl); } -```text +``` ## FormatterFlag 配置 @@ -83,7 +83,7 @@ enum FormatterFlag : uint32_t { MESSAGE = 1 << 5, // 消息内容 COLOR = 1 << 6, // ANSI 颜色 }; -```text +``` ### 预设组合 @@ -91,7 +91,7 @@ enum FormatterFlag : uint32_t { MINIMAL = LEVEL | MESSAGE; DEFAULT = TIMESTAMP | LEVEL | TAG | SOURCE_LOCATION | MESSAGE; VERBOSE = TIMESTAMP | LEVEL | TAG | THREAD_ID | SOURCE_LOCATION | MESSAGE; -```bash +``` ### 输出组件示例 @@ -123,7 +123,7 @@ if (config->is_enabled(FormatterFlag::COLOR)) { config->set_flags(FormatterFlag::MINIMAL); formatter->set_config(config); -```text +``` ### 位运算组合 @@ -136,7 +136,7 @@ auto flags = FormatterFlag::DEFAULT & ~FormatterFlag::SOURCE_LOCATION; // 添加某个标志 auto flags = FormatterFlag::MINIMAL | FormatterFlag::TIMESTAMP; -```text +``` ## 时间戳格式配置 @@ -145,7 +145,7 @@ auto flags = FormatterFlag::MINIMAL | FormatterFlag::TIMESTAMP; ```cpp auto config = std::make_shared(); config->set_timestamp_format("%Y-%m-%d %H:%M:%S"); -```bash +``` ### 常用格式 @@ -200,7 +200,7 @@ formatter->set_config(config); // 方法3:使用 FileFormatter(自动忽略颜色) auto formatter = std::make_shared(); -```text +``` ## 队列配置 @@ -209,7 +209,7 @@ auto formatter = std::make_shared(); ```cpp // AsyncPostQueue 中的常量 static constexpr size_t kMaxNormalQueueSize = 65536; // 2^16 -```bash +``` 这是编译时常量,运行时不可修改。 @@ -238,7 +238,7 @@ void monitor_queue() { last_count = current_count; } } -```text +``` ## 文件 Sink 配置 @@ -249,7 +249,7 @@ enum class OpenMode { Append, // 追加到文件末尾 Truncate // 覆盖现有文件 }; -```text +``` ### 使用示例 @@ -259,7 +259,7 @@ auto sink = std::make_shared("app.log"); // 覆盖模式(测试环境) auto sink = std::make_shared("app.log", OpenMode::Truncate); -```text +``` ## 多环境配置 @@ -288,7 +288,7 @@ void setup_dev_environment() { Logger::instance().add_sink(console); Logger::instance().setMininumLevel(level::TRACE); } -```text +``` ### 测试环境 @@ -310,7 +310,7 @@ void setup_test_environment() { Logger::instance().add_sink(console); Logger::instance().setMininumLevel(level::DEBUG); } -```text +``` ### 生产环境 @@ -349,7 +349,7 @@ void setup_production_environment() { Logger::instance().add_sink(error_log); Logger::instance().setMininumLevel(level::INFO); } -```text +``` ## 配置文件示例 @@ -382,7 +382,7 @@ void setup_production_environment() { ] } } -```text +``` ### 命令行参数 @@ -431,7 +431,7 @@ void parse_command_line_args(int argc, char** argv) { Logger::instance().add_sink(file_sink); } } -```text +``` ## 配置最佳实践 diff --git a/document/HandBook/desktop/base/logger/formatters.md b/document/HandBook/desktop/base/logger/formatters.md index da22b8c64..c158624e5 100644 --- a/document/HandBook/desktop/base/logger/formatters.md +++ b/document/HandBook/desktop/base/logger/formatters.md @@ -17,7 +17,7 @@ LogRecord (结构化数据) Formatter (格式化器) ↓ std::string (可读文本) -```bash +``` ### 内置 Formatter @@ -43,7 +43,7 @@ enum FormatterFlag : uint32_t { MESSAGE = 1 << 5, // 消息内容 COLOR = 1 << 6, // ANSI 颜色 }; -```text +``` ### 预设组合 @@ -56,7 +56,7 @@ DEFAULT = TIMESTAMP | LEVEL | TAG | SOURCE_LOCATION | MESSAGE // 详细:包含所有组件 VERBOSE = TIMESTAMP | LEVEL | TAG | THREAD_ID | SOURCE_LOCATION | MESSAGE -```bash +``` ### 输出示例 @@ -77,7 +77,7 @@ using namespace cf::log; // 使用默认配置 auto formatter = std::make_shared(); -```text +``` ### 自定义配置 @@ -91,7 +91,7 @@ auto minimal = std::make_shared( auto verbose = std::make_shared( FormatterFlag::VERBOSE | FormatterFlag::COLOR ); -```text +``` ### 运行时修改配置 @@ -109,7 +109,7 @@ config->enable(FormatterFlag::THREAD_ID); config->set_timestamp_format("%Y-%m-%d %H:%M:%S"); formatter->set_config(config); -```bash +``` ### ANSI 颜色映射 @@ -132,7 +132,7 @@ formatter->set_config(config); [14:23:47] [ERROR] [Network] 连接失败 ^^^^^ 红色 -```text +``` ## FileFormatter @@ -144,7 +144,7 @@ formatter->set_config(config); using namespace cf::log; auto formatter = std::make_shared(); -```text +``` ### 特点 @@ -158,7 +158,7 @@ FileFormatter 与 AsciiColorFormatter 基本相同,但: auto formatter = std::make_shared( FormatterFlag::DEFAULT | FormatterFlag::COLOR // COLOR 被忽略 ); -```text +``` ## DefaultFormatter @@ -170,7 +170,7 @@ auto formatter = std::make_shared( #include "cflog/formatter/default_formatter.h" auto formatter = std::make_shared(); -```text +``` **输出**:只包含 `LogRecord.msg` @@ -191,7 +191,7 @@ auto config = std::make_shared( FormatterFlag::MINIMAL, "%H:%M:%S" // 时间格式 ); -```text +``` ### 线程安全操作 @@ -209,7 +209,7 @@ if (config->is_enabled(FormatterFlag::COLOR)) { // 设置所有标志 config->set_flags(FormatterFlag::VERBOSE); -```bash +``` ### 时间戳格式 @@ -238,7 +238,7 @@ public: return false; // 不支持配置 } }; -```text +``` ### 可配置自定义 @@ -288,7 +288,7 @@ private: return ""; } }; -```text +``` ## 常见格式示例 @@ -316,7 +316,7 @@ private: return ""; } }; -```text +``` ### Syslog 格式 @@ -347,7 +347,7 @@ private: return ""; } }; -```text +``` ## 使用 FormatterFactory @@ -366,7 +366,7 @@ auto formatter = factory.create("custom"); // 获取或创建(带缓存) auto cached = factory.get_or_create("custom"); -```bash +``` ## 选择建议 diff --git a/document/HandBook/desktop/base/logger/overview.md b/document/HandBook/desktop/base/logger/overview.md index 5846c192d..3bf166412 100644 --- a/document/HandBook/desktop/base/logger/overview.md +++ b/document/HandBook/desktop/base/logger/overview.md @@ -58,7 +58,7 @@ int main() { info("Hello, CFLogger!"); return 0; } -```text +``` ### 典型配置 @@ -79,7 +79,7 @@ void init_logger() { Logger::instance().setMininumLevel(level::INFO); } -```text +``` ## 日志输出示例 @@ -89,7 +89,7 @@ void init_logger() { [14:23:45] [INFO] [CFLog] Application started [14:23:46] [WARNING] [Config] Config file not found [14:23:47] [ERROR] [Network] Connection failed -```text +``` ### 彩色控制台输出 @@ -102,7 +102,7 @@ void init_logger() { [14:23:47] [ERROR] [Network] Connection failed ^^^^^ 红色 -```bash +``` ## 手册结构 @@ -131,7 +131,7 @@ overview → advanced_usage → formatters → sinks → configuration 深入路径: overview → architecture → performance → troubleshooting -```text +``` ## 相关资源 diff --git a/document/HandBook/desktop/base/logger/performance.md b/document/HandBook/desktop/base/logger/performance.md index 575d47764..704f97f84 100644 --- a/document/HandBook/desktop/base/logger/performance.md +++ b/document/HandBook/desktop/base/logger/performance.md @@ -33,7 +33,7 @@ description: 本文档介绍 CFLogger 的性能特性和优化建议。 - 线程数: 16 - 每线程日志数: 10000 - 队列洪泛数: 70000 -```text +``` ## 架构性能优势 @@ -48,7 +48,7 @@ description: 本文档介绍 CFLogger 的性能特性和优化建议。 [调用线程] → [入队] → [立即返回] ↓ [工作线程] → [格式化] → [写入磁盘] -```text +``` **优势**: - 调用线程不会被 I/O 阻塞 @@ -61,7 +61,7 @@ CFLogger 使用无锁 MPSC(多生产者单消费者)队列: ```cpp cf::lockfree::MpscQueue normalQueue_; -```text +``` **优势**: - 多线程写入无需互斥锁 @@ -72,7 +72,7 @@ cf::lockfree::MpscQueue normalQueue_; ```cpp void submit(LogRecord record); // 按值传递 -```text +``` LogRecord 使用移动语义,避免字符串拷贝: @@ -80,7 +80,7 @@ LogRecord 使用移动语义,避免字符串拷贝: LogRecord record; record.msg = "很长的日志消息..."; async_queue_.submit(std::move(record)); // 移动,不拷贝 -```text +``` ## 性能影响因素 @@ -96,7 +96,7 @@ trace("这条消息不会被记录"); // 只做一次原子比较 // ❌ 高开销 set_level(level::TRACE); trace("这条消息会被记录"); // 入队、格式化、写入 -```bash +``` ### 消息大小 @@ -116,7 +116,7 @@ trace("完整响应: " + huge_json_response); // ✅ 好 trace("响应大小: " + std::to_string(response.size()) + " bytes"); debug("响应内容: " + response.substr(0, 100) + "..."); -```text +``` ### 格式化器复杂度 @@ -133,7 +133,7 @@ auto formatter = std::make_shared( auto formatter = std::make_shared( FormatterFlag::VERBOSE ); -```bash +``` ### Sink 类型 @@ -154,7 +154,7 @@ auto formatter = std::make_shared( #else Logger::instance().setMininumLevel(level::INFO); #endif -```text +``` ### 2. 避免热路径过度日志 @@ -170,7 +170,7 @@ for (int i = 0; i < 1000000; ++i) { // 处理... } debug("完成处理 1000000 个项目"); -```text +``` ### 3. 延迟计算 @@ -182,7 +182,7 @@ trace("详细信息: " + expensive_function()); if (Logger::instance().getMininumLevel() <= level::TRACE) { trace("详细信息: " + expensive_function()); } -```text +``` ### 4. 简化格式 @@ -197,7 +197,7 @@ if (Logger::instance().getMininumLevel() <= level::TRACE) { FormatterFlag::VERBOSE | FormatterFlag::COLOR ); #endif -```text +``` ### 5. 批量刷新 @@ -213,7 +213,7 @@ for (int i = 0; i < 1000; ++i) { info("项目 " + std::to_string(i)); } Logger::instance().flush(); // 最后统一刷新 -```text +``` ### 6. 监控队列溢出 @@ -231,7 +231,7 @@ void monitor_queue_overflow() { last_check = now; } } -```text +``` ## 内存使用 @@ -249,7 +249,7 @@ void monitor_queue_overflow() { 队列容量:65,536 条 队列内存:约 14 MB -```text +``` ### 内存优化 @@ -261,7 +261,7 @@ void log_message(std::string_view msg) { // ✅ 避免大量临时对象 info("数据: " + data.to_string()); // data.to_string() 返回临时字符串 -```text +``` ## 线程扩展性 @@ -275,7 +275,7 @@ CFLogger 在多线程环境下表现良好: 4 线程: 40,000 条/秒 8 线程: 80,000 条/秒 16 线程: 120,000 条/秒 (开始饱和) -```text +``` ### 瓶颈分析 @@ -317,7 +317,7 @@ private: std::chrono::steady_clock::time_point start_time_; size_t log_count_ = 0; }; -```text +``` ### 使用示例 @@ -332,7 +332,7 @@ for (int i = 0; i < 10000; ++i) { Logger::instance().flush_sync(); monitor.report(); -```text +``` ## 性能检查清单 @@ -371,7 +371,7 @@ void setup_high_performance_logging() { // 较高的日志级别 Logger::instance().setMininumLevel(level::WARNING); } -```text +``` ### 调试配置 @@ -395,7 +395,7 @@ void setup_verbose_logging() { // 最低级别 Logger::instance().setMininumLevel(level::TRACE); } -```text +``` ## 下一步 diff --git a/document/HandBook/desktop/base/logger/quick_start.md b/document/HandBook/desktop/base/logger/quick_start.md index bc4d4a491..94c4fb8e0 100644 --- a/document/HandBook/desktop/base/logger/quick_start.md +++ b/document/HandBook/desktop/base/logger/quick_start.md @@ -15,13 +15,13 @@ CFLogger 提供两套 API:简单 API 和高级 API。 ```cpp #include "cflog/cflog.h" -```text +``` ### 高级 API(需要更多控制) ```cpp #include "cflog/cflog.hpp" -```text +``` ## 第二步:Hello World @@ -41,13 +41,13 @@ int main() { return 0; } -```text +``` 编译运行后,你将看到: ```text [INFO] Hello, CFLogger! -```text +``` ## 第三步:使用不同日志级别 @@ -68,7 +68,7 @@ int main() { flush(); return 0; } -```bash +``` ### 日志级别对照表 @@ -102,7 +102,7 @@ int main() { flush(); return 0; } -```text +``` ### 级别过滤规则 @@ -112,7 +112,7 @@ int main() { 设置为 INFO: 显示 INFO, WARNING, ERROR 设置为 WARNING:显示 WARNING, ERROR 设置为 ERROR:只显示 ERROR -```text +``` ## 第五步:使用标签组织日志 @@ -133,7 +133,7 @@ int main() { flush(); return 0; } -```bash +``` ### 常用标签建议 @@ -196,7 +196,7 @@ int main() { Logger::instance().flush_sync(); // 等待写入完成 return 0; } -```text +``` ## 完整示例 @@ -239,7 +239,7 @@ int main() { return 0; } -```text +``` ## CMake 配置 @@ -254,7 +254,7 @@ add_executable(my_app main.cpp) # 链接 CFLogger target_link_libraries(my_app PRIVATE CFDesktop::logger) -```text +``` ## 编译运行 @@ -267,7 +267,7 @@ cmake --build build # 运行 ./build/my_app -```yaml +``` ## 常见问题 diff --git a/document/HandBook/desktop/base/logger/sinks.md b/document/HandBook/desktop/base/logger/sinks.md index e99cb776c..d6b5f31c8 100644 --- a/document/HandBook/desktop/base/logger/sinks.md +++ b/document/HandBook/desktop/base/logger/sinks.md @@ -21,7 +21,7 @@ LogRecord (日志记录) Sink (输出目标) ↓ 实际存储位置 -```text +``` ### Sink 职责 @@ -44,7 +44,7 @@ auto console_sink = std::make_shared(); console_sink->setFormat(std::make_shared()); Logger::instance().add_sink(console_sink); -```text +``` **特点**: - 线程安全 @@ -72,7 +72,7 @@ auto file_sink = std::make_shared( file_sink->setFormat(std::make_shared()); Logger::instance().add_sink(file_sink); -```bash +``` **特点**: - 线程安全 @@ -113,7 +113,7 @@ void setup_logging() { file_sink->setFormat(factory.create("file")); Logger::instance().add_sink(file_sink); } -```text +``` ### 分级输出(普通日志 vs 错误日志) @@ -137,7 +137,7 @@ void setup_split_logging() { ); // 实际使用需要自定义 Sink 来过滤 ERROR 级别 } -```text +``` ## 自定义 Sink @@ -175,7 +175,7 @@ protected: std::shared_ptr formatter_; }; -```text +``` ### 旋转文件 Sink @@ -261,7 +261,7 @@ private: size_t file_index_; std::ofstream file_; }; -```text +``` ### 过滤 Sink @@ -294,7 +294,7 @@ private: std::shared_ptr wrapped_sink_; level min_level_; }; -```text +``` ### 统计 Sink @@ -333,7 +333,7 @@ private: mutable std::mutex mutex_; std::map counts_; }; -```text +``` ### 网络 Sink @@ -449,7 +449,7 @@ private: std::string sending_; std::mutex queue_mutex_; }; -```text +``` ## Sink 最佳实践 @@ -469,7 +469,7 @@ public: private: std::mutex mutex_; }; -```text +``` ### 2. 错误处理 @@ -485,7 +485,7 @@ bool write(const LogRecord& record) override { return false; } } -```text +``` ### 3. 资源清理 @@ -500,7 +500,7 @@ bool write(const LogRecord& record) override { socket_.close(); } } -```text +``` ## 使用示例 @@ -549,7 +549,7 @@ void setup_comprehensive_logging() { logger.setMininumLevel(level::INFO); } -```text +``` ## 下一步 diff --git a/document/HandBook/desktop/base/logger/troubleshooting.md b/document/HandBook/desktop/base/logger/troubleshooting.md index bb413cacc..0061d82e8 100644 --- a/document/HandBook/desktop/base/logger/troubleshooting.md +++ b/document/HandBook/desktop/base/logger/troubleshooting.md @@ -28,7 +28,7 @@ int main() { flush(); // 或 Logger::instance().flush_sync() return 0; } -```text +``` **原因 2:日志级别设置过高** @@ -40,7 +40,7 @@ info("这条不会显示"); // INFO < WARNING // ✅ 调整级别 set_level(level::INFO); info("这条会显示"); -```text +``` **原因 3:没有添加 Sink** @@ -52,7 +52,7 @@ Logger::instance().log(level::INFO, "Hello", "Tag", {}); auto sink = std::make_shared(); Logger::instance().add_sink(sink); Logger::instance().log(level::INFO, "Hello", "Tag", {}); -```text +``` ### Q2: 程序崩溃后日志丢失 @@ -77,7 +77,7 @@ int main() { Logger::instance().flush_sync(); // 确保所有日志写入 return 0; } -```text +``` ### Q3: 队列溢出导致日志丢失 @@ -88,7 +88,7 @@ size_t overflow = Logger::instance().get_normal_queue_overflow(); if (overflow > 0) { warning("队列溢出,丢失 " + std::to_string(overflow) + " 条日志"); } -```text +``` #### 解决方法 @@ -100,7 +100,7 @@ Logger::instance().setMininumLevel(level::WARNING); // 减少详细日志 // trace() → debug() → info() -```text +``` **方法 2:批量日志** @@ -116,7 +116,7 @@ for (int i = 0; i < 10000; ++i) { // 处理... } trace("完成处理 10000 个项目"); -```text +``` **方法 3:异步处理加速** @@ -125,7 +125,7 @@ trace("完成处理 10000 个项目"); ```cpp // 使用更快的 Sink // 考虑使用内存缓冲 + 批量写入 -```text +``` ### Q4: 颜色显示异常 @@ -135,7 +135,7 @@ trace("完成处理 10000 个项目"); ```text [14:23:45] [INFO] [CFLog] ^[[92m消息^[[0m -```text +``` #### 原因 @@ -158,7 +158,7 @@ auto file_formatter = std::make_shared( auto config = std::make_shared(FormatterFlag::DEFAULT); config->disable(FormatterFlag::COLOR); formatter->set_config(config); -```text +``` ### Q5: 编译错误 @@ -166,7 +166,7 @@ formatter->set_config(config); ```text fatal error: cflog/cflog.h: No such file or directory -```text +``` #### 解决方法 @@ -181,13 +181,13 @@ target_link_libraries(my_app PRIVATE CFDesktop::logger) target_include_directories(my_app PRIVATE ${CMAKE_SOURCE_DIR}/desktop/base/logger/include ) -```text +``` #### 问题:链接错误 ```text undefined reference to `cf::log::info(std::string_view, ...)` -```text +``` #### 解决方法 @@ -197,7 +197,7 @@ target_link_libraries(my_app PRIVATE CFDesktop::logger) # 检查 logger 库是否被构建 # 确保 CMake 选项 CFDESKTOP_BUILD_LOGGER 为 ON -```text +``` ### Q6: 多线程日志混乱 @@ -224,7 +224,7 @@ public: private: std::mutex mutex_; }; -```text +``` ### Q7: 性能问题 @@ -255,7 +255,7 @@ public: private: std::chrono::steady_clock::time_point start_; }; -```text +``` #### 解决方法 @@ -282,7 +282,7 @@ size_t overflow = Logger::instance().get_normal_queue_overflow(); if (overflow > 0) { // 日志产生速度 > 消费速度 } -```text +``` **原因 2:自定义 Sink 泄漏** @@ -298,7 +298,7 @@ public: private: std::vector messages_; // 从不清理 }; -```text +``` **解决方法** @@ -320,7 +320,7 @@ private: size_t max_size_ = 1000; std::mutex mutex_; }; -```text +``` ### Q9: 文件打开失败 @@ -352,7 +352,7 @@ void ensure_log_directory(const std::string& log_path) { // 使用 ensure_log_directory("/var/log/myapp/app.log"); auto sink = std::make_shared("/var/log/myapp/app.log"); -```text +``` ### Q10: 时间戳不正确 @@ -371,7 +371,7 @@ auto sink = std::make_shared("/var/log/myapp/app.log"); auto config = std::make_shared(); config->set_timestamp_format("%Y-%m-%d %H:%M:%S %z"); // 添加时区 formatter->set_config(config); -```text +``` ## 调试技巧 @@ -385,7 +385,7 @@ Logger::instance().setMininumLevel(level::TRACE); auto formatter = std::make_shared( FormatterFlag::VERBOSE | FormatterFlag::COLOR ); -```text +``` ### 监控队列状态 @@ -419,7 +419,7 @@ private: std::thread thread_; size_t last_overflow_ = 0; }; -```text +``` ### 捕获异常 @@ -438,7 +438,7 @@ public: } } }; -```text +``` ## 获取帮助 @@ -468,7 +468,7 @@ grep "\[Network\]" app.log # 实时过滤 tail -f app.log | grep ERROR -```text +``` ## 下一步 diff --git a/document/HandBook/examples/cpu_info_example.md b/document/HandBook/examples/cpu_info_example.md index a634cfdf7..f6dc43f18 100644 --- a/document/HandBook/examples/cpu_info_example.md +++ b/document/HandBook/examples/cpu_info_example.md @@ -87,7 +87,7 @@ int main() { return 0; } -```text +``` ## 编译和运行 @@ -96,13 +96,13 @@ int main() { ```bash cd /home/charliechen/project/QtProjects/CFDesktop ./scripts/build_helpers/linux_fast_develop_build.sh -```text +``` ### 运行 ```bash ./out/build_develop/example/base/system/example_cpu_info -```text +``` ## 示例输出 @@ -127,7 +127,7 @@ cd /home/charliechen/project/QtProjects/CFDesktop 缓存: 32KB 256KB 12288KB 大小核架构: 否 温度: 不可用 -```text +``` 在 ARM Linux 系统上的典型输出: @@ -150,7 +150,7 @@ cd /home/charliechen/project/QtProjects/CFDesktop 缓存: 64KB 512KB 4096KB 大小核架构: 否 温度: 45°C -```text +``` ## 错误处理 @@ -173,7 +173,7 @@ if (!result.has_value()) { // 使用结果 auto info = result.value(); -```cpp +``` ## 最佳实践 @@ -189,7 +189,7 @@ auto info1 = cf::getCPUInfo(); // 稍后刷新查询 auto info2 = cf::getCPUInfo(true); -```text +``` ## 相关文档 diff --git a/document/HandBook/implementation/linux/cpu_implementation.md b/document/HandBook/implementation/linux/cpu_implementation.md index 66a0e47bb..0c15ef36a 100644 --- a/document/HandBook/implementation/linux/cpu_implementation.md +++ b/document/HandBook/implementation/linux/cpu_implementation.md @@ -40,7 +40,7 @@ cf::expected query_cpu_basic_info(cf::CPUInfoHost& h return {}; } -```text +``` 架构信息用 `uname()` 而不是读文件,是因为某些嵌入式板子的 `/proc/cpuinfo` 可能不包含完整的架构字段。 @@ -71,7 +71,7 @@ float calculate_cpu_usage() { if (total_delta == 0) return 0.0f; return 100.0f * (1.0f - static_cast(idle_delta) / total_delta); } -```text +``` ⚠️ 首次调用会返回不准确的数据,因为需要上次采样的值作为基准。 @@ -89,7 +89,7 @@ CPU 特性标志在 `flags`(x86)或 `Features`(ARM)字段里,是个空 // 读取缓存大小 auto l1_size = cf::read_uint32_file("/sys/devices/system/cpu/cpu0/cache/index0/size"); // 输出通常是 "32K",parse_cache_size() 会处理单位转换 -```text +``` 温度信息从 `/sys/class/thermal/thermal_zone*/temp` 读取,但不是所有设备都有温度传感器。返回值通常是 millidegree,需要除以 1000 转成摄氏度。 @@ -124,7 +124,7 @@ bool detect_big_little(cf::CPUBonusInfoHost& host) { return false; } -```text +``` 这个方法不是百分之百可靠——有些同频 CPU 也可能被误判为大小核——但在我们支持的设备上效果还可以。 @@ -145,7 +145,7 @@ std::string_view arm_implementer_to_vendor(uint32_t impl_val) { default: return "Unknown"; } } -```text +``` ## 相关文档 diff --git a/document/HandBook/implementation/linux/memory/memory_implementation.md b/document/HandBook/implementation/linux/memory/memory_implementation.md index 58ca4c186..e6b928816 100644 --- a/document/HandBook/implementation/linux/memory/memory_implementation.md +++ b/document/HandBook/implementation/linux/memory/memory_implementation.md @@ -20,7 +20,7 @@ Buffers: 128000 kB Cached: 5120000 kB SwapTotal: 8388608 kB SwapFree: 8388608 kB -```text +``` 解析逻辑很简单:跳过字段名后的冒号和空格,用 `strtoul` 提取数字,然后乘以 1024 转换成字节。 @@ -51,7 +51,7 @@ bool parseMemInfoLine(const char* line, const char* fieldName, uint64_t& outKb) outKb = static_cast(value); return true; } -```text +``` 这个解析函数在所有基于 `/proc/meminfo` 的查询里都是共用的,避免了重复代码。只要找到需要的字段就返回,还能提前终止循环节省时间。 @@ -96,7 +96,7 @@ void queryPhysicalMemory(PhysicalMemory& physical) { physical.available_bytes = memAvailable * 1024; physical.free_bytes = memFree * 1024; } -```text +``` 文件打开失败时所有字段归零,而不是抛异常——这在内存查询这种场景下是合理的,因为调用方通常更希望拿到"空数据"而不是崩溃。 @@ -135,7 +135,7 @@ void queryCachedMemory(CachedMemory& cached) { cached.shared_bytes = shmem * 1024; cached.slab_bytes = slab * 1024; } -```text +``` 这些缓存内存都是可以被回收的,所以 `MemAvailable` 已经考虑了它们。如果你的程序只关心"还能分配多少内存",看 `MemAvailable` 就够了;如果需要分析内存占用细节(比如排查内存去哪了),这些字段会很有用。 @@ -169,7 +169,7 @@ void querySwapMemory(SwapMemory& swap) { swap.total_bytes = swapTotal * 1024; swap.free_bytes = swapFree * 1024; } -```text +``` 如果系统没有配置交换空间,`SwapTotal` 会是 0,这种情况在服务器上挺常见的。 @@ -205,7 +205,7 @@ void queryProcessMemory(ProcessMemory& process) { process.vm_size_bytes = vmSize * 1024; process.vm_peak_bytes = vmPeak * 1024; } -```text +``` `/proc/self` 是指向当前进程 `/proc/` 的符号链接,这样就不需要先获取自己的 PID 了。 @@ -225,7 +225,7 @@ void queryDimmInfo(std::vector& dimms) { queryDimmViaSysFs(dimms); } } -```text +``` ### dmidecode 解析 @@ -281,7 +281,7 @@ bool parseDmidecode(const char* output, std::vector& dimms) { return !dimms.empty(); } -```text +``` 内存类型字符串的匹配是大小写不敏感的,支持 DDR2/3/4/5、LPDDR3/4/4X/5 以及 SDRAM。如果遇到未知类型会返回 `UNKNOWN`。 @@ -307,7 +307,7 @@ uint64_t parseMemorySize(const char* sizeStr) { return 0; } -```text +``` 插槽编号从 `Locator` 字段提取,但这个字段格式不统一——可能是 "DIMM0"、"ChannelA-DIMM0"、"Slot 1" 之类的。我们用一个简单的启发式规则:找到最后一个数字,把它当作插槽号。这不是 100% 准确,但大多数情况下能工作。 @@ -337,7 +337,7 @@ bool queryDimmViaSysFs(std::vector& dimms) { dimms.push_back(dimm); return true; } -```text +``` 这个回退方案至少能保证不会返回空列表,但 `capacity_bytes` 会是 0,调用方需要处理这种情况。 @@ -391,7 +391,7 @@ HugePages_Surp: 0 DirectMap4k: 256000 kB DirectMap2M: 5120000 kB DirectMap1G: 10485760 kB -```text +``` 而 `/proc/self/status` 是这样: @@ -408,7 +408,7 @@ VmSize: 123456 kB VmRSS: 67890 kB VmPeak: 234567 kB ... -```text +``` ## 注意事项 diff --git a/document/HandBook/implementation/windows/cpu_implementation.md b/document/HandBook/implementation/windows/cpu_implementation.md index 3bcfde769..1b69edc01 100644 --- a/document/HandBook/implementation/windows/cpu_implementation.md +++ b/document/HandBook/implementation/windows/cpu_implementation.md @@ -18,7 +18,7 @@ cf::expected query_cpu_basic_info(cf::CPUInfoHost& hostI // WMI 查询代码 }); } -```text +``` 这个封装确保 COM 正确初始化,线程退出时自动调用 `CoUninitialize()`,而且异常安全。 @@ -57,7 +57,7 @@ cf::expected query_cpu_basic_info(cf::CPUInfoHost& hostI return {}; } -```text +``` ⚠️ `CoSetProxyBlanket()` 调用是必须的,否则查询会返回 `E_ACCESSDENIED`。这个坑踩过一次。 @@ -79,7 +79,7 @@ std::string architectureToString(UINT16 archValue) { default: return "Unknown"; } } -```text +``` ## 性能信息 @@ -104,7 +104,7 @@ float get_cpu_usage() { PdhCloseQuery(query); return static_cast(value.doubleValue); } -```text +``` ⚠️ 两次 `PdhCollectQueryData()` 之间必须有延迟,否则返回的数据是 0 或无效值。这是因为性能计数器是基于时间差计算的。 @@ -141,7 +141,7 @@ bool detect_feature(const char* feature_name) { } return false; } -```text +``` EAX=1 返回的是基本特性,EAX=7 返回的是扩展特性。不同特性位分布在不同的寄存器里,需要查 Intel 的手册确认。 @@ -158,7 +158,7 @@ std::optional get_cpu_temperature() { } return std::nullopt; // 大多数情况不可用 } -```text +``` 所以我们的实现里,温度信息在 Windows 上基本总是 `std::nullopt`。这不是 bug,是 Windows 硬件生态的限制。 @@ -178,7 +178,7 @@ cf::ScopeGuard objGuard([&pclsObj]() { if (pclsObj) pclsObj->Release(); }); cf::ScopeGuard varGuard([&vtProp]() { VariantClear(&vtProp); }); // ... 复杂的查询逻辑,无论哪里返回,资源都会被释放 -```text +``` ## 相关文档 diff --git a/document/HandBook/implementation/windows/memory/memory_implementation.md b/document/HandBook/implementation/windows/memory/memory_implementation.md index c668aadf4..5806b304a 100644 --- a/document/HandBook/implementation/windows/memory/memory_implementation.md +++ b/document/HandBook/implementation/windows/memory/memory_implementation.md @@ -23,7 +23,7 @@ void queryPhysicalMemory(PhysicalMemory& physical) { physical.available_bytes = status.ullAvailPhys; physical.free_bytes = status.ullAvailPhys; // Windows: AvailPhys ~= Free } -```text +``` 交换空间的获取方式类似: @@ -36,7 +36,7 @@ void querySwapMemory(SwapMemory& swap) { swap.total_bytes = status.ullTotalPageFile; swap.free_bytes = status.ullAvailPageFile; } -```text +``` ⚠️ `dwLength` 字段必须设置为 `sizeof(MEMORYSTATUSEX)`,否则 `GlobalMemoryStatusEx` 会返回失败。这个设计很古老,但一直保留到现在。 @@ -69,7 +69,7 @@ void queryProcessMemory(ProcessMemory& process) { process.vm_peak_bytes = 0; } } -```bash +``` 字段映射关系: | Windows 字段 | 我们的字段 | 含义 | @@ -106,7 +106,7 @@ if (result != bufferSize) { const RawSMBIOSData* smbios = reinterpret_cast(buffer.data()); parseSmbiosMemoryDevices(smbios->SMBIOSTableData, smbios->Length, dimms); -```text +``` ### SMBIOS 结构解析 @@ -143,7 +143,7 @@ struct MemoryDevice { }; #pragma pack(pop) -```text +``` SMBIOS 结构后面跟着一个字符串表,字符串索引从 1 开始。0 表示没有字符串。我们需要跳过格式化段(`Length` 字节),然后读取字符串直到双 null 字节: @@ -170,7 +170,7 @@ const char* getSmbiosString(const uint8_t* structStart, uint8_t stringIndex, return reinterpret_cast(strStart); } -```text +``` ⚠️ SMBIOS 字符串索引是 1-based 的,这一点很容易忘。索引 0 表示没有字符串,不是第一个字符串。 @@ -208,7 +208,7 @@ MemoryType smbiosToMemoryType(uint8_t smbType) { default: return MemoryType::UNKNOWN; } } -```text +``` ### 容量解析 @@ -229,7 +229,7 @@ if (sizeValue == 0 || (sizeValue & 0x8000) != 0) { // 正常大小,单位是 MB dimm.capacity_bytes = static_cast(sizeValue) * 1024 * 1024; } -```text +``` ⚠️ 32GB 以上的内存条必须用扩展大小字段,因为 16 位的 `Size` 字段只能表示到 32767 MB(约 32GB)。 @@ -273,7 +273,7 @@ void parseSmbiosMemoryDevices(const uint8_t* data, uint32_t length, p = next; } } -```text +``` ## 平台限制 diff --git a/document/HandBook/ui/application/application.md b/document/HandBook/ui/application/application.md index 0db2bd812..10d6591e6 100644 --- a/document/HandBook/ui/application/application.md +++ b/document/HandBook/ui/application/application.md @@ -27,7 +27,7 @@ int main(int argc, char* argv[]) { return app.exec(); } -```text +``` 注意:如果你直接用 `Application` 而不是它的派生类,需要手动注册主题并调用 `init()`,否则 `currentTheme()` 会抛异常。实际上更推荐用 `MaterialApplication`,下面会说。 @@ -43,7 +43,7 @@ app.setTheme("theme.material.dark"); connect(&app, &Application::themeChanged, [](const core::ICFTheme& newTheme) { // 响应主题切换,更新 UI }); -```text +``` 这里有个细节:动画工厂的重建会保留之前的启用状态。比如你禁用了动画,切换主题后动画仍然是禁用状态,不会突然恢复。 @@ -61,7 +61,7 @@ if (fadeIn) { // 全局禁用动画(比如在批量操作时) Application::setAnimationsEnabled(false); -```text +``` 返回的是 `WeakPtr`,因为动画工厂归 `Application` 所有,外部只持有弱引用。如果 `Application` 被销毁或者主题切换导致工厂重建,原来的 `WeakPtr` 就会失效——所以拿到指针后要尽快用,不要长期持有。 @@ -80,7 +80,7 @@ Application::registerAnimationFactoryType("theme.fluent", // 现在切换到 "theme.fluent.*" 主题时会用你的工厂 app.setTheme("theme.fluent.dark"); -```text +``` 匹配逻辑是按前缀最长匹配:`theme.fluent.dark` 会匹配 `theme.fluent`,而不是 `theme`。这样你可以同时注册多个工厂,每个负责一个主题家族。 @@ -103,7 +103,7 @@ protected: Application::init(); } }; -```text +``` 如果顺序搞反了,基类的 `init()` 会尝试创建动画工厂,但此时主题还没注册,工厂创建会失败。 diff --git a/document/HandBook/ui/application/material_application.md b/document/HandBook/ui/application/material_application.md index 380888a6c..3f8b30cdd 100644 --- a/document/HandBook/ui/application/material_application.md +++ b/document/HandBook/ui/application/material_application.md @@ -25,7 +25,7 @@ int main(int argc, char* argv[]) { return app.exec(); } -```text +``` 构造函数里会自动调用 `init()`,`init()` 按下面的顺序执行: @@ -45,7 +45,7 @@ app.setTheme("theme.material.dark"); // 或者用 token 字面量(推荐) using namespace cf::ui::core::token::literals; app.setTheme(MATERIAL_THEME_DARK); -```text +``` 主题切换会触发 `themeChanged` 信号,动画工厂会同步重建。 @@ -59,7 +59,7 @@ Application::registerAnimationFactoryType("theme.material", [](const core::ICFTheme& theme, QObject* parent) { return std::make_unique(theme, nullptr, parent); }); -```text +``` 所以你不需要自己管工厂创建,只要确保主题 token 以 `theme.material.` 开头就行。 @@ -72,7 +72,7 @@ MaterialApplication app(argc, argv); // 如果想要暗色主题作为默认 app.setTheme(MATERIAL_THEME_DARK); -```text +``` 或者自己派生一个类,在 `init()` 里改默认主题: @@ -84,7 +84,7 @@ protected: setTheme(MATERIAL_THEME_DARK); // 然后改默认 } }; -```text +``` ⚠️ 不要在 `MaterialApplication::init()` 里改默认主题再调用基类 `init()`——因为基类的 `init()` 会创建动画工厂,此时默认主题还是亮色的。要么在构造后改,要么在派生类的 `init()` 里先调基类 `init()` 再改。 @@ -101,7 +101,7 @@ themeManager()->insert_one("theme.material.light", ...); themeManager()->setThemeTo("theme.material.light", false); app.init(); // 别忘了手动调用 init() -```text +``` 而 `MaterialApplication` 把这些都包圆了,构造完就能用。如果你用 Material Design 3,没理由不用它。 diff --git a/document/HandBook/ui/architecture/index.md b/document/HandBook/ui/architecture/index.md index 96a38990a..9b02e45b7 100644 --- a/document/HandBook/ui/architecture/index.md +++ b/document/HandBook/ui/architecture/index.md @@ -19,7 +19,7 @@ CFDesktop UI 框架采用五层架构设计,遵循依赖倒置原则:上层 ├─────────────────────────────────────────┤ │ Layer 1 · 数学工具层 │ HCT 色彩, 几何, 像素适配 └─────────────────────────────────────────┘ -```text +``` ## 各层详情 diff --git a/document/HandBook/ui/architecture/layer-1-math-utility/01-why-we-need-own-math-layer.md b/document/HandBook/ui/architecture/layer-1-math-utility/01-why-we-need-own-math-layer.md index d5ab3c999..de84dcd5f 100644 --- a/document/HandBook/ui/architecture/layer-1-math-utility/01-why-we-need-own-math-layer.md +++ b/document/HandBook/ui/architecture/layer-1-math-utility/01-why-we-need-own-math-layer.md @@ -76,7 +76,7 @@ QList palette = tonalPalette(brandColor); // palette[0] 是最接近品牌色的 tone 值 // palette[1] 到 palette[13] 是从 Tone 0 到 Tone 100 的 13 个等级 -```yaml +``` 如果你打印出这些颜色的 RGB 值,你会发现它们的色相和色度基本保持一致,只有亮度在变化——这正是 HCT 空间的优势。 diff --git a/document/HandBook/ui/architecture/layer-1-math-utility/02-color-system-hct.md b/document/HandBook/ui/architecture/layer-1-math-utility/02-color-system-hct.md index 28b6cc98f..da20b4f6c 100644 --- a/document/HandBook/ui/architecture/layer-1-math-utility/02-color-system-hct.md +++ b/document/HandBook/ui/architecture/layer-1-math-utility/02-color-system-hct.md @@ -60,13 +60,13 @@ HSL hsl = rgbToHsl(r, g, b); outH = hsl.h; // 色相直接沿用 outT = perceivedLightness * 100.0f; // 感知亮度 outC = (hsl.s / toneFactor) * 100.0f; // 色度从饱和度推导 -```text +``` 这里有几个细节需要注意。首先是"感知亮度",它不是简单的 (R+G+B)/3,而是加权平均: ```cpp float perceivedLightness = 0.299f * r + 0.587f * g + 0.114f * b; -```text +``` 这个权重来源于人眼对不同颜色的敏感度——绿色看起来最亮,蓝色最暗。 @@ -76,7 +76,7 @@ float perceivedLightness = 0.299f * r + 0.587f * g + 0.114f * b; float toneFactor = 1.0f - std::abs(hsl.l - 0.5f) * 1.5f; toneFactor = math::clamp(toneFactor, 0.2f, 1.0f); outC = (hsl.s / toneFactor) * 100.0f; -```text +``` ## 从 HCT 到 RGB 的回转 @@ -93,7 +93,7 @@ toneSaturationFactor = toneSaturationFactor * toneSaturationFactor; outS = chromaFactor * (0.3f + 0.7f * toneSaturationFactor); outL = t - chromaFactor * 0.05f; // 高色度会让颜色看起来更暗 -```text +``` 然后再用标准的 HSL → RGB 算法转回 RGB。 @@ -119,7 +119,7 @@ QList tonalPalette(CFColor keyColor) { } return palette; } -```text +``` 就这么简单。你固定 Hue 和 Chroma,只变 Tone,就能得到 13 个颜色,它们的"颜色感"是一致的,只有亮度不同。 @@ -146,7 +146,7 @@ float relativeLuminance() const { return 0.2126f * r + 0.7152f * g + 0.0722f * b; } -```text +``` 为什么需要 gamma 校正?因为 sRGB 是非线性编码的,直接用 RGB 值计算亮度会得到错误的结果。gamma 校正后,RGB 值才与实际的物理光强成正比。 @@ -162,7 +162,7 @@ float contrastRatio(CFColor& a, CFColor& b) { return (lighter + 0.05f) / (darker + 0.05f); } -```yaml +``` 加 0.05 是 WCAG 标准的规定,用于避免极端情况的数值问题。 diff --git a/document/HandBook/ui/architecture/layer-1-math-utility/03-geometry-and-device-pixel.md b/document/HandBook/ui/architecture/layer-1-math-utility/03-geometry-and-device-pixel.md index 3b9857ae2..6c44cc403 100644 --- a/document/HandBook/ui/architecture/layer-1-math-utility/03-geometry-and-device-pixel.md +++ b/document/HandBook/ui/architecture/layer-1-math-utility/03-geometry-and-device-pixel.md @@ -36,7 +36,7 @@ struct CanvasUnitHelper { private: qreal devicePixelRatio; }; -```text +``` 转换逻辑很简单: @@ -51,7 +51,7 @@ qreal spToPx(qreal sp) const { qreal fontScale = font.pointSizeF() / 10.0; // 假设默认 10pt return sp * devicePixelRatio * fontScale; } -```text +``` 这里有个坑:Windows 上获取 devicePixelRatio 的方式经历了多次变迁。早期版本用 `QScreen::devicePixelRatio()`,但这个值在 Windows 10 1709 之后的"缩放与布局"设置下可能不准确。现在推荐用 `QScreen::logicalDotsPerInch()` 除以 96 来计算。 @@ -65,7 +65,7 @@ enum class BreakPoint { Medium, // 600dp - 839dp Expanded // >= 840dp }; -```text +``` 这个设计很有意思:Material 不是针对具体设备(手机/平板/桌面)分类,而是针对"可用宽度"分类。一个桌面窗口如果缩得很窄,也应该用 Compact 布局。 @@ -79,7 +79,7 @@ BreakPoint breakPoint(qreal widthDp) { return BreakPoint::Expanded; } } -```text +``` ## 圆角矩形工具 @@ -97,7 +97,7 @@ QPainterPath roundedRect(const QRectF& rect, float radius); // 每个角单独指定 QPainterPath roundedRect(const QRectF& rect, float topLeft, float topRight, float bottomLeft, float bottomRight); -```text +``` ShapeScale 枚举对应 Material 的标准圆角尺寸: @@ -111,7 +111,7 @@ enum class ShapeScale { ShapeExtraLarge, // 28dp ShapeFull // 50% of size }; -```text +``` 这里有个需要注意的地方:ShapeFull 是"完全圆角",也就是变成一个胶囊或圆形。这种情况下圆角半径是矩形短边的一半。我们在实现时需要特殊处理: @@ -119,7 +119,7 @@ enum class ShapeScale { if (scale == ShapeScale::ShapeFull) { radius = std::min(rect.width(), rect.height()) / 2.0f; } -```text +``` ## 为什么不直接用 QSS? @@ -150,7 +150,7 @@ void Button::paintEvent(QPaintEvent* event) { // 圆角半径 qreal radius = helper.dpToPx(cornerRadius()); } -```yaml +``` 这样无论在什么 DPI 的屏幕上,按钮的视觉大小都是一致的。 diff --git a/document/HandBook/ui/architecture/layer-2-theme-engine/01-theme-system-design.md b/document/HandBook/ui/architecture/layer-2-theme-engine/01-theme-system-design.md index 1d6dbc0e6..a68342fdc 100644 --- a/document/HandBook/ui/architecture/layer-2-theme-engine/01-theme-system-design.md +++ b/document/HandBook/ui/architecture/layer-2-theme-engine/01-theme-system-design.md @@ -44,7 +44,7 @@ protected: std::unique_ptr radius_scale_; std::unique_ptr font_type_; }; -```text +``` 设计成接口的原因是:我们可能有多种不同的主题实现(Material、Cupertino、Fluent),但它们都应该遵循同一个接口。控件只需要依赖 ICFTheme 接口,不需要知道具体是哪种主题。 @@ -61,7 +61,7 @@ public: virtual std::unique_ptr fromJson(const QByteArray& json) = 0; virtual QByteArray toJson(ICFTheme* raw_theme) = 0; }; -```text +``` `fromName()` 用于创建预定义的主题(比如 "light"、"dark"),`fromJson()` 用于从 Material Theme Builder 导出的 JSON 创建主题,`toJson()` 用于序列化。 @@ -93,7 +93,7 @@ public: signals: void themeChanged(const ICFTheme& new_theme); }; -```text +``` 使用方式很直观: @@ -108,7 +108,7 @@ ThemeManager::instance().install_widget(myButton); // 切换主题 ThemeManager::instance().setThemeTo("material.light"); -```text +``` ## 为什么用接口 + 工厂? @@ -129,7 +129,7 @@ static ThemeManager& instance() { static ThemeManager manager; return manager; } -```text +``` C++11 保证局部静态变量的初始化是线程安全的,所以这个实现不需要额外的锁。 @@ -143,7 +143,7 @@ C++11 保证局部静态变量的初始化是线程安全的,所以这个实 // 在控件的构造函数中 connect(&ThemeManager::instance(), &ThemeManager::themeChanged, this, [this](const ICFTheme&) { update(); }); -```text +``` 这里有个设计细节:为什么用 `install_widget` 而不是让控件直接连接信号? @@ -163,7 +163,7 @@ ThemeManager (owner) ├── unique_ptr motion_spec_ ├── unique_ptr radius_scale_ └── unique_ptr font_type_ -```yaml +``` 控件只持有引用(通过 `themeChanged` 信号的参数),不拥有主题的所有权。这样当主题被销毁时,不会有 dangling pointer 的问题。 diff --git a/document/HandBook/ui/architecture/layer-2-theme-engine/02-token-system.md b/document/HandBook/ui/architecture/layer-2-theme-engine/02-token-system.md index f0116bff1..2a7228f5c 100644 --- a/document/HandBook/ui/architecture/layer-2-theme-engine/02-token-system.md +++ b/document/HandBook/ui/architecture/layer-2-theme-engine/02-token-system.md @@ -31,7 +31,7 @@ auto result = PrimaryToken::get(); if (result) { QColor color = *result; } -```text +``` 这样有几个好处: @@ -56,7 +56,7 @@ public: // 静态方法获取值 static cf::expected get(); }; -```text +``` 注意这里的设计技巧:StaticToken 的所有构造函数都被删除了。这意味着你不能创建 StaticToken 的实例——它只是一个"类型",用来承载编译时信息。 @@ -80,14 +80,14 @@ constexpr uint64_t fnv1a64(std::string_view str) { constexpr uint64_t operator""_hash(const char* str, size_t len) { return fnv1a64(std::string_view(str, len)); } -```text +``` 这样你就可以在编译时计算字符串的哈希: ```cpp constexpr uint64_t PRIMARY_HASH = fnv1a64("md.primary"); // 编译时常量 using PrimaryToken = StaticToken; -```text +``` ## TokenRegistry:运行时存储 @@ -117,7 +117,7 @@ private: mutable std::shared_mutex registry_mutex_; std::unordered_map slot_map_; }; -```text +``` TokenRegistry 是一个单例,内部用 `unordered_map` 存储数据,键是哈希值。 @@ -131,7 +131,7 @@ struct TokenSlot { const std::type_info* type_info; // 用于类型检查 std::string name; // 调试用 }; -```text +``` 注册时,我们创建一个 `std::any` 存储 `T` 类型的值,同时保存 `typeid(T)` 用于后续的类型检查。 @@ -156,7 +156,7 @@ auto TokenRegistry::get() -> Result { return std::any_cast(slot->data.get()); } -```text +``` ## 线程安全设计 @@ -168,7 +168,7 @@ std::shared_lock lock(registry_mutex_); // 写操作(register_token):独占锁 std::unique_lock lock(registry_mutex_); -```text +``` 这个设计适合"读多写少"的场景——token 注册通常发生在初始化阶段,之后大部分时候都是读取。 @@ -193,7 +193,7 @@ inline constexpr const char* const ALL_TOKENS[] = { inline constexpr size_t TOKEN_COUNT = 26; } // namespace cf::ui::core::token::literals -```text +``` 这些 `constexpr` 字面量可以在编译时使用,而且避免了字符串字面量的重复。 @@ -209,7 +209,7 @@ struct ICFColorScheme { return const_cast(this)->queryExpectedColor(name); } }; -```text +``` MaterialColorScheme 的实现内部会用 TokenRegistry 来查找颜色。虽然这里还是用了字符串参数,但内部实现可以复用 Token 系统,保持一致性。 @@ -226,7 +226,7 @@ auto result = TokenRegistry::get().get_dynamic("custom.color"); if (result) { QColor color = *result; } -```yaml +``` DynamicToken 用的是运行时字符串查找,没有编译时检查,但更加灵活。适合插件系统、用户自定义主题等场景。 diff --git a/document/HandBook/ui/architecture/layer-2-theme-engine/03-color-scheme.md b/document/HandBook/ui/architecture/layer-2-theme-engine/03-color-scheme.md index 2dc8c19e2..aac18a95f 100644 --- a/document/HandBook/ui/architecture/layer-2-theme-engine/03-color-scheme.md +++ b/document/HandBook/ui/architecture/layer-2-theme-engine/03-color-scheme.md @@ -32,7 +32,7 @@ private: EmbeddedTokenRegistry registry_; mutable std::unordered_map color_cache_; }; -```text +``` 注意这里有两个存储:`registry_` 存储原始的 CFColor(带 HCT 信息),`color_cache_` 存储转换为 QColor 的结果(用于查询缓存)。 @@ -56,7 +56,7 @@ QList tonalPalette(CFColor keyColor) { } return palette; } -```text +``` 这个算法我们在 Layer 1 里讲过,核心思想是固定 Hue 和 Chroma,只变 Tone。这样生成的 13 个颜色在视觉上是"同一个颜色的不同亮度版本"。 @@ -73,7 +73,7 @@ primary = primaryPalette[Tone 40]; // md.primary onPrimary = primaryPalette[Tone 100]; // md.onPrimary(白色) primaryContainer = primaryPalette[Tone 90]; // md.primaryContainer onPrimaryContainer = primaryPalette[Tone 10]; // md.onPrimaryContainer -```text +``` 这里有个设计选择:为什么 Primary 选 Tone 40,而 PrimaryContainer 选 Tone 90? @@ -91,7 +91,7 @@ QList secondaryPalette = tonalPalette(secondarySeed); // Tertiary:从主种子颜色衍生(不同的衍生规则) CFColor tertiarySeed = deriveTertiary(seedColor); QList tertiaryPalette = tonalPalette(tertiarySeed); -```text +``` 衍生算法会调整 HCT 值,让 Secondary 和 Tertiary 与 Primary 形成视觉和谐。比如 Secondary 可能旋转色相 30 度,Tertiary 可能旋转 60 度。 @@ -107,7 +107,7 @@ onBackground = CFColor(hue, chroma, Tone 10); // 深色文本 // Dark 主题 background = CFColor(hue, chroma, Tone 10); // 接近黑色 onBackground = CFColor(hue, chroma, Tone 90); // 浅色文本 -```text +``` 注意这里虽然用了相同的 `hue` 和 `chroma`,但 Tone 值差异很大。实际上,Surface 颜色通常会使用很低的 chroma(接近中性灰),以避免与内容颜色冲突。 @@ -118,7 +118,7 @@ Error 组使用固定的种子颜色(通常是红色),不随主题变化 ```cpp CFColor errorSeed("#B00020"); // Material 标准错误红 QList errorPalette = tonalPalette(errorSeed); -```text +``` ## onX 颜色的对比度要求 @@ -127,7 +127,7 @@ Material Design 3 要求 `onX` 颜色与 X 颜色之间满足 WCAG AA 对比度 ```cpp float ratio = contrastRatio(primary, onPrimary); // ratio >= 4.5 必须成立 -```text +``` 如果 tonalPalette 生成的颜色不满足对比度要求,需要调整。通常的做法是: @@ -162,7 +162,7 @@ QColor& MaterialColorScheme::queryExpectedColor(const char* name) { color_cache_[name] = color; return color_cache_[name]; } -```text +``` 注意这里返回的是引用,意味着调用者不应该修改返回的颜色(否则会影响缓存)。如果需要修改,应该用 `queryColor` 返回副本。 @@ -190,7 +190,7 @@ QColor onPrimary = lightScheme.queryColor("md.onPrimary"); // 验证对比度 float ratio = contrastRatio(primary, onPrimary); // ratio 应该 >= 4.5 -```yaml +``` ## 总结 diff --git a/document/HandBook/ui/architecture/layer-2-theme-engine/04-typography-shape-motion.md b/document/HandBook/ui/architecture/layer-2-theme-engine/04-typography-shape-motion.md index 58e1db3c3..ed2a6175f 100644 --- a/document/HandBook/ui/architecture/layer-2-theme-engine/04-typography-shape-motion.md +++ b/document/HandBook/ui/architecture/layer-2-theme-engine/04-typography-shape-motion.md @@ -26,7 +26,7 @@ protected: std::unique_ptr radius_scale_; std::unique_ptr font_type_; }; -```bash +``` 这四个组件相互独立,但通过主题聚合在一起。控件可以根据需要选择性地使用这些组件。 @@ -62,7 +62,7 @@ IFontType 是一个简单的接口,只有一个方法: struct IFontType { virtual QFont queryTargetFont(const char* name) = 0; }; -```text +``` 使用方式: @@ -72,7 +72,7 @@ QFont bodyLarge = theme->font_type().queryTargetFont("bodyLarge"); // 设置到控件 label->setFont(bodyLarge); -```bash +``` MaterialTypography 的实现内部维护了一个 QFont 的映射表,用 Token 名称作为键。为了保证性能,QFont 对象被缓存起来,避免重复创建。 @@ -98,7 +98,7 @@ IRadiusScale 接口也很简单: struct IRadiusScale { virtual float queryRadiusScale(const char* name) = 0; }; -```text +``` 返回值是 dp 单位,控件需要根据 devicePixelRatio 转换成像素: @@ -106,7 +106,7 @@ struct IRadiusScale { float radiusDp = theme->radius_scale().queryRadiusScale("cornerMedium"); CanvasUnitHelper helper(qApp->devicePixelRatio()); float radiusPx = helper.dpToPx(radiusDp); -```text +``` 注意这里有个设计细节:IRadiusScale 返回的是 dp,不是 px。原因是圆角应该在所有 DPI 下保持一致的"视觉大小",所以应该由控件根据当前的 devicePixelRatio 进行转换。 @@ -126,7 +126,7 @@ struct IMotionSpec { virtual int queryEasing(const char* name) = 0; virtual int queryDelay(const char* name) = 0; }; -```text +``` 使用方式: @@ -135,7 +135,7 @@ struct IMotionSpec { int duration = theme->motion_spec().queryDuration("md.motion.shortEnter"); int easing = theme->motion_spec().queryEasing("md.motion.shortEnter"); int delay = theme->motion_spec().queryDelay("md.motion.shortEnter"); -```bash +``` Material Design 3 定义了几种标准动画: @@ -171,7 +171,7 @@ void Button::paintEvent(QPaintEvent* event) { // ... 使用这些值绘制 } -```yaml +``` ## 扩展性 diff --git a/document/HandBook/ui/architecture/layer-3-animation-engine/01-animation-architecture.md b/document/HandBook/ui/architecture/layer-3-animation-engine/01-animation-architecture.md index 2026e1ef2..692ee8231 100644 --- a/document/HandBook/ui/architecture/layer-3-animation-engine/01-animation-architecture.md +++ b/document/HandBook/ui/architecture/layer-3-animation-engine/01-animation-architecture.md @@ -48,7 +48,7 @@ protected: float m_progress = 0.0f; State m_state = State::Idle; }; -```text +``` 这里有个关键设计:`GetWeakPtr()` 返回的是弱引用。原因我们后面会讲,但核心思想是:动画由工厂拥有所有权,用户只持有弱引用。 @@ -69,7 +69,7 @@ Running → Paused (pause) Paused → Running (start) Running/Paused → Idle (stop) Running → Finished (自然结束) -```text +``` ## Direction 方向控制 @@ -101,7 +101,7 @@ CFMaterialAnimationFactory (owner) Controls └── WeakPtr -```text +``` 工厂拥有动画的所有权(`unique_ptr`),控件只持有弱引用(`WeakPtr`)。这样设计的好处是: @@ -120,7 +120,7 @@ if (anim) { // 检查 WeakPtr 是否有效 }); anim->start(); } -```text +``` ## progressChanged 信号 @@ -139,7 +139,7 @@ ICFAnimationManagerFactory 提供了全局开关: ```cpp factory->setEnabledAll(false); // 禁用所有动画 factory->setEnabledAll(true); // 启用所有动画 -```text +``` 禁用时,`getAnimation()` 会返回无效的 WeakPtr,这样就不会创建新动画。已有的正在运行的动画不受影响,会自然完成。 @@ -155,7 +155,7 @@ factory->setEnabledAll(true); // 启用所有动画 ```cpp factory->setTargetFps(60.0f); // 60 FPS -```yaml +``` 这会影响动画的定时器间隔。更高的 FPS 意味着更平滑的动画,但也意味着更多的 CPU 开销。 diff --git a/document/HandBook/ui/architecture/layer-3-animation-engine/02-timing-spring-animation.md b/document/HandBook/ui/architecture/layer-3-animation-engine/02-timing-spring-animation.md index c8c7379f5..97c7249f3 100644 --- a/document/HandBook/ui/architecture/layer-3-animation-engine/02-timing-spring-animation.md +++ b/document/HandBook/ui/architecture/layer-3-animation-engine/02-timing-spring-animation.md @@ -29,7 +29,7 @@ protected: float m_to = 1.0f; int m_elapsed = 0; // 已过时间(毫秒) }; -```text +``` 注意 `motion_spec_` 是一个原始指针。这是一个设计决策:IMotionSpec 的生命周期必须比动画长。在 CFMaterialAnimationFactory 中,这个条件是满足的,因为工厂持有对主题的引用,而主题拥有 MotionSpec。 @@ -61,7 +61,7 @@ bool ICFTimingAnimation::tick(int dt) { return m_elapsed < duration; // 返回 false 表示动画结束 } -```text +``` 这里的关键是 `valueForProgress()`,它根据缓动曲线将 [0, 1] 的线性进度映射到非线性进度。 @@ -97,7 +97,7 @@ protected: float m_velocity = 0.0f; float m_target = 1.0f; }; -```text +``` ## springStep 物理模拟 @@ -123,7 +123,7 @@ std::pair springStep(float position, float velocity, float target, return {newPosition, newVelocity}; } -```text +``` 这个算法在 Layer 1 讲过,关键点是: @@ -163,7 +163,7 @@ bool ICFSpringAnimation::tick(int dt) { return !isConverged; } -```text +``` 收敛的条件是速度足够小且距离目标足够近。阈值 0.01 是经验值,可以根据需要调整。 @@ -182,7 +182,7 @@ namespace Easing { SpringPreset springBouncy(); // 明显的弹性 SpringPreset springStiff(); // 僵硬的弹性 } -```text +``` 不同的预设适用于不同的场景:按钮点击用 gentle,对话框进入用 bouncy,列表滚动用 stiff。 @@ -216,7 +216,7 @@ if (fadeAnim) { // SpringAnimation:弹性缩放 // 需要先注册自定义弹簧动画 // 或者使用预设的弹簧动画 token -```yaml +``` ## 总结 diff --git a/document/HandBook/ui/architecture/layer-3-animation-engine/03-factory-and-strategy.md b/document/HandBook/ui/architecture/layer-3-animation-engine/03-factory-and-strategy.md index feaed5b1d..cd504349d 100644 --- a/document/HandBook/ui/architecture/layer-3-animation-engine/03-factory-and-strategy.md +++ b/document/HandBook/ui/architecture/layer-3-animation-engine/03-factory-and-strategy.md @@ -37,7 +37,7 @@ private: bool globalEnabled_ = true; std::unordered_map> animations_; }; -```bash +``` ## Token 到 AnimationDescriptor 的映射 @@ -62,7 +62,7 @@ struct AnimationDescriptor { float fromValue; float toValue; }; -```text +``` ## getAnimation() 的完整流程 @@ -109,7 +109,7 @@ cf::WeakPtr CFMaterialAnimationFactory::getAnimation(const // 7. 返回 WeakPtr return animations_[token]->GetWeakPtr(); } -```text +``` ## 动画实例的创建 @@ -135,7 +135,7 @@ std::unique_ptr CFMaterialAnimationFactory::createSlideAni std::unique_ptr CFMaterialAnimationFactory::createScaleAnimation(...) { // 创建 CFMaterialScaleAnimation } -```text +``` ## AnimationStrategy 策略模式 @@ -157,7 +157,7 @@ public: return true; // 默认启用动画 } }; -```text +``` 控件类型可以实现自己的策略: @@ -182,7 +182,7 @@ public: return adjusted; } }; -```text +``` 使用策略: @@ -193,7 +193,7 @@ auto factory = std::make_unique(theme, std::move(but // 控件获取动画时会自动应用策略 auto anim = factory->getAnimation("md.animation.fadeIn"); -```text +``` ## 策略的应用时机 @@ -207,7 +207,7 @@ auto anim = factory->getAnimation("md.animation.fadeIn"); ```cpp factory->setEnabledAll(false); // 禁用所有动画 -```text +``` 禁用时,`getAnimation()` 会返回无效的 WeakPtr。已有的正在运行的动画不受影响,会自然完成。 @@ -224,7 +224,7 @@ factory->setEnabledAll(false); // 禁用所有动画 ```cpp factory->setTargetEnabled("md.animation.fadeIn", false); -```yaml +``` 这样只有特定的动画被禁用,其他动画正常运行。 diff --git a/document/HandBook/ui/architecture/layer-4-material-behavior/01-state-machine.md b/document/HandBook/ui/architecture/layer-4-material-behavior/01-state-machine.md index 40dd49b6f..770ca6e55 100644 --- a/document/HandBook/ui/architecture/layer-4-material-behavior/01-state-machine.md +++ b/document/HandBook/ui/architecture/layer-4-material-behavior/01-state-machine.md @@ -34,7 +34,7 @@ enum class State { StateChecked = 0x10, // 选中状态 StateDragged = 0x20 // 拖拽状态 }; -```text +``` 注意这里使用了位掩码设计,每个状态是一个 2 的幂次方。这样多个状态可以通过按位或运算组合。 @@ -44,7 +44,7 @@ Material Design 3 定义了状态的优先级顺序: ```text Disabled > Pressed > Dragged > Focused > Hovered > Normal -```bash +``` 这意味着如果一个控件同时是 Disabled 和 Hovered, Disabled 状态会"赢",透明度由 Disabled 决定(通常是 0.00)。 @@ -78,7 +78,7 @@ void onFocusOut(); void onEnable(); void onDisable(); void onCheckedChanged(bool checked); -```text +``` 这些方法对应 Qt 的各种事件,控件在事件处理函数中调用它们: @@ -92,7 +92,7 @@ void Button::mousePressEvent(QMouseEvent* event) { QPushButton::mousePressEvent(event); m_stateMachine->onPress(event->pos()); } -```text +``` 注意这里的一个关键点:必须先调用父类方法。否则 Qt 的信号机制会被破坏,比如 `clicked()` 信号可能不会发出。 @@ -113,14 +113,14 @@ void StateMachine::animateOpacityTo(float from, float to) { anim->start(); } } -```text +``` 如果动画系统被禁用,会直接设置目标透明度: ```cpp m_opacity = to; emit stateLayerOpacityChanged(m_opacity); -```text +``` ## 状态优先级的实现 @@ -137,7 +137,7 @@ float StateMachine::targetOpacityForState(States s) const { if (s & StateChecked) return 0.08f; return 0.00f; // Normal } -```text +``` 使用按位与运算(`&`)来检查状态是否被设置。这意味着多个状态可以同时存在,但只有一个决定透明度。 @@ -150,7 +150,7 @@ void CheckBox::setChecked(bool checked) { QCheckBox::setChecked(checked); m_stateMachine->onCheckedChanged(checked); } -```text +``` ## Disabled 状态的特殊处理 @@ -163,7 +163,7 @@ void StateMachine::onPress(const QPoint& pos) { } // ... 正常处理 } -```text +``` ## 信号通知 @@ -172,14 +172,14 @@ StateMachine 发出两个信号: ```cpp void stateChanged(States newState, States oldState); void stateLayerOpacityChanged(float opacity); -```text +``` 控件可以连接这些信号来触发重绘: ```cpp connect(m_stateMachine, &StateMachine::stateLayerOpacityChanged, this, [this](float) { update(); }); -```yaml +``` ## 总结 diff --git a/document/HandBook/ui/architecture/layer-4-material-behavior/02-ripple-and-elevation.md b/document/HandBook/ui/architecture/layer-4-material-behavior/02-ripple-and-elevation.md index e6efc4876..8bd500147 100644 --- a/document/HandBook/ui/architecture/layer-4-material-behavior/02-ripple-and-elevation.md +++ b/document/HandBook/ui/architecture/layer-4-material-behavior/02-ripple-and-elevation.md @@ -34,7 +34,7 @@ struct MdRipple { bool releasing; // 是否处于释放阶段 float maxRadius; // 最大半径 }; -```text +``` ### 最大半径的计算 @@ -56,7 +56,7 @@ float RippleHelper::maxRadius(const QRectF& rect, const QPointF& center) const { float d4 = std::hypot(center.x() - rect.right(), center.y() - rect.bottom()); return std::max({d1, d2, d3, d4}); } -```text +``` ### 多涟漪并存 @@ -64,7 +64,7 @@ float RippleHelper::maxRadius(const QRectF& rect, const QPointF& center) const { ```cpp QList m_ripples; -```text +``` 每次 `paint()` 时,所有涟漪都被绘制。当涟漪的透明度降到 0 以下时,它被从列表中移除。 @@ -102,7 +102,7 @@ void RippleHelper::onPress(const QPoint& pos, const QRectF& widgetRect) { anim->start(); } } -```text +``` ### 涟漪颜色 @@ -112,14 +112,14 @@ void RippleHelper::onPress(const QPoint& pos, const QRectF& widgetRect) { void RippleHelper::setColor(const CFColor& color) { m_color = color; } -```text +``` 绘制时,使用这个颜色加上当前的透明度值: ```cpp QColor rippleColor = m_color.native_color; rippleColor.setAlphaF(m_ripples[i].opacity); -```text +``` ### 涟漪绘制 @@ -139,7 +139,7 @@ void RippleHelper::paint(QPainter* painter, const QPainterPath& clipPath) { painter->restore(); } -```bash +``` ## MdElevationController:海拔阴影控制器 @@ -165,7 +165,7 @@ MdElevationController::ShadowParams MdElevationController::paramsForLevel(float // 根据级别返回对应的 blur、offset、opacity // ... } -```text +``` ### 阴影绘制 @@ -182,7 +182,7 @@ void MdElevationController::paintShadow(QPainter* painter, const QPainterPath& s // 第二层阴影(可选,更明显的海拔效果) } -```text +``` 实际实现中,Qt 的 QGraphicsDropShadowEffect 可以用来生成模糊阴影,但我们可能需要自定义绘制以获得更精确的控制。 @@ -199,7 +199,7 @@ void MdElevationController::setPressed(bool pressed) { // 恢复原始海拔 } } -```text +``` 这个效果通过调整阴影偏移和透明度来实现。 @@ -213,7 +213,7 @@ float MdElevationController::pressOffset() const { // 海拔越高,按压时的偏移量越大 return m_currentLevel * 2.0f; // 简化的公式 } -```text +``` ### Dark Theme 的叠色表示 @@ -224,7 +224,7 @@ CFColor MdElevationController::tonalOverlay(CFColor surface, CFColor primary) co // 使用 elevationOverlay 函数计算叠色 return elevationOverlay(surface, primary, static_cast(m_currentLevel)); } -```yaml +``` `elevationOverlay` 函数我们在 Layer 1 讲过,它根据海拔级别选择不同的叠加透明度。 diff --git a/document/HandBook/ui/architecture/layer-4-material-behavior/03-focus-indicator.md b/document/HandBook/ui/architecture/layer-4-material-behavior/03-focus-indicator.md index 43353520a..f92663ca8 100644 --- a/document/HandBook/ui/architecture/layer-4-material-behavior/03-focus-indicator.md +++ b/document/HandBook/ui/architecture/layer-4-material-behavior/03-focus-indicator.md @@ -40,7 +40,7 @@ private: float m_progress = 0.0f; // 0 = 隐藏,1 = 完全显示 cf::WeakPtr m_animator; }; -```text +``` ## 焦点进入/离开 @@ -73,7 +73,7 @@ void MdFocusIndicator::onFocusOut() { m_progress = 0.0f; } } -```text +``` 注意淡出动画使用的是 `1.0f - progress`,因为我们希望透明度从 1 变到 0。 @@ -112,7 +112,7 @@ void MdFocusIndicator::paint(QPainter* painter, const QRectF& widgetRect, painter->drawPath(ringPath); painter->restore(); } -```text +``` ## 与控件的集成 @@ -135,7 +135,7 @@ void Button::paintEvent(QPaintEvent* event) { // 最后绘制焦点环 m_focusIndicator->paint(&painter, rect(), cornerRadius()); } -```yaml +``` ## 无障碍考虑 diff --git a/document/HandBook/ui/architecture/layer-5-widget-adapter/01-adapter-pattern.md b/document/HandBook/ui/architecture/layer-5-widget-adapter/01-adapter-pattern.md index d20b49a7b..7f6347a77 100644 --- a/document/HandBook/ui/architecture/layer-5-widget-adapter/01-adapter-pattern.md +++ b/document/HandBook/ui/architecture/layer-5-widget-adapter/01-adapter-pattern.md @@ -45,7 +45,7 @@ void Button::mousePressEvent(QMouseEvent* event) { m_stateMachine->onPress(event->pos()); m_ripple->onPress(event->pos(), rect()); } -```text +``` 第一步调用父类方法是必须的,原因如下: @@ -80,7 +80,7 @@ Button::Button(ButtonVariant variant, QWidget* parent) connect(&ThemeManager::instance(), &ThemeManager::themeChanged, this, [this](const ICFTheme&) { update(); }); } -```text +``` 注意所有行为组件的 parent 都是 `this`,这意味着它们会随控件一起销毁,无需手动管理生命周期。 @@ -99,7 +99,7 @@ QColor Button::containerColor() const { // ... } } -```text +``` 这里使用 `currentTheme()` 获取当前活动的主题。如果主题切换,`themeChanged` 信号会触发 `update()`,控件会重新读取主题数据并重绘。 @@ -122,7 +122,7 @@ QColor Button::containerColor() const { // 错误!不要这样做 auto& theme = ThemeManager::instance().currentTheme(); theme.color_scheme().setColor("md.primary", QColor("#FF0000")); -```bash +``` 修改主题应该通过 ThemeManager 的 `setThemeTo()` 方法,或者在主题创建时指定颜色。这保证了主题的全局一致性。 diff --git a/document/HandBook/ui/architecture/layer-5-widget-adapter/02-button-deep-dive.md b/document/HandBook/ui/architecture/layer-5-widget-adapter/02-button-deep-dive.md index 08d6f7174..4daa10183 100644 --- a/document/HandBook/ui/architecture/layer-5-widget-adapter/02-button-deep-dive.md +++ b/document/HandBook/ui/architecture/layer-5-widget-adapter/02-button-deep-dive.md @@ -31,7 +31,7 @@ enum class ButtonVariant { Text, Elevated }; -```text +``` ## 构造函数中的组件初始化 @@ -65,7 +65,7 @@ Button::Button(ButtonVariant variant, QWidget* parent) connect(&ThemeManager::instance(), &ThemeManager::themeChanged, this, [this](const ICFTheme&) { update(); }); } -```text +``` ## 事件处理的标准实现 @@ -105,7 +105,7 @@ void Button::focusOutEvent(QFocusEvent* event) { m_stateMachine->onFocusOut(); m_focusIndicator->onFocusOut(); } -```text +``` ## 七步绘制流程 @@ -137,7 +137,7 @@ void Button::paintEvent(QPaintEvent* event) { drawContent(painter, contentRect); // 6. 内容(图标+文本) drawFocusIndicator(painter, shape); // 7. 焦点环 } -```text +``` 让我们逐步拆解这个流程。 @@ -152,7 +152,7 @@ void Button::drawShadow(QPainter& p, const QRectF& contentRect, const QPainterPa } // 其他变体没有阴影(或只有轻微阴影) } -```text +``` ### 第二步:绘制背景 @@ -179,7 +179,7 @@ QColor Button::containerColor() const { } return Qt::black; // fallback } -```text +``` ### 第三步:绘制状态层 @@ -210,7 +210,7 @@ QColor Button::stateLayerColor() const { } return Qt::white; } -```text +``` ### 第四步:绘制涟漪 @@ -220,7 +220,7 @@ QColor Button::stateLayerColor() const { void Button::drawRipple(QPainter& p, const QPainterPath& shape) { m_ripple->paint(&p, shape); } -```text +``` ### 第五步:绘制边框 @@ -242,7 +242,7 @@ void Button::drawOutline(QPainter& p, const QPainterPath& shape) { p.setBrush(Qt::NoBrush); p.drawPath(shape); } -```text +``` ### 第六步:绘制内容 @@ -266,7 +266,7 @@ void Button::drawContent(QPainter& p, const QRectF& contentRect) { // 绘制文本 p.drawText(textRect, Qt::AlignCenter, text()); } -```text +``` ### 第七步:绘制焦点环 @@ -276,7 +276,7 @@ void Button::drawContent(QPainter& p, const QRectF& contentRect) { void Button::drawFocusIndicator(QPainter& p, const QPainterPath& shape) { m_focusIndicator->paint(&p, rect(), cornerRadius()); } -```yaml +``` ## 总结 diff --git a/document/HandBook/ui/architecture/layer-5-widget-adapter/03-painting-pipeline.md b/document/HandBook/ui/architecture/layer-5-widget-adapter/03-painting-pipeline.md index a42dba8f1..a1c0e8f88 100644 --- a/document/HandBook/ui/architecture/layer-5-widget-adapter/03-painting-pipeline.md +++ b/document/HandBook/ui/architecture/layer-5-widget-adapter/03-painting-pipeline.md @@ -21,7 +21,7 @@ void Button::paintEvent(QPaintEvent* event) { // ... 绘制代码 } -```text +``` 这两个设置会影响绘制质量: @@ -48,7 +48,7 @@ void setChecked(bool checked) { update(); // 只在状态改变时重绘 } } -```text +``` 状态机已经做了这个优化——只有当透明度值真正改变时,才会发出 `stateLayerOpacityChanged` 信号。 @@ -71,7 +71,7 @@ private: QColor cachedLabelColor_; float cachedCornerRadius_; }; -```text +``` 在 `themeChanged` 信号处理中刷新缓存: @@ -81,7 +81,7 @@ connect(&ThemeManager::instance(), &ThemeManager::themeChanged, refreshThemeCache(); update(); }); -```text +``` 这样每次绘制时就不需要查询主题了。 @@ -94,13 +94,13 @@ void Button::drawRipple(QPainter& p, const QPainterPath& shape) { QPainterPath clipPath = geometry::roundedRect(rect(), cornerRadius()); m_ripple->paint(&p, clipPath); } -```text +``` Qt 的 `setClipPath` 操作比较昂贵,因为需要计算复杂的几何。如果控件形状简单(比如矩形),可以考虑用 `setClipRect` 替代: ```cpp painter.setClipRect(rect(), Qt::IntersectClip); // 更高效的矩形裁剪 -```text +``` ## 静态内容的预渲染 @@ -123,7 +123,7 @@ void Button::paintEvent(QPaintEvent* event) { painter.drawPixmap(0, 0, cachedBackground_); // 直接绘制缓存的图像 // ... 绘制动态内容 } -```text +``` 但对于大多数控件来说,这个优化可能不值得——因为背景色会随主题切换而改变。 @@ -144,7 +144,7 @@ QTimer::singleShot(0, this, []() { widget->update(); } }); -```text +``` 这样可以将多次重绘合并为一次。 @@ -180,7 +180,7 @@ void Button::paintEvent(QPaintEvent* event) { // Desktop 配置:绘制所有效果 // ... 完整的七步流程 } -```yaml +``` ## 总结 diff --git a/document/HandBook/ui/base/color.md b/document/HandBook/ui/base/color.md index 334073d69..55e27b640 100644 --- a/document/HandBook/ui/base/color.md +++ b/document/HandBook/ui/base/color.md @@ -24,7 +24,7 @@ QColor brighterBlue = rgbBlue.lighter(120); // 结果可能偏紫了 CFColor hctBlue(240.0f, 80.0f, 50.0f); // 蓝色,中高饱和度,中等亮度 CFColor brighterHCT = CFColor(hctBlue.hue(), hctBlue.chroma(), 70.0f); // 色相 240° 不变,鲜艳度不变,只是更亮了 -```text +``` 这就是 Material Design 3 能从单个"种子颜色"生成整套主题的原因——在 HCT 空间里,你可以独立控制颜色的各个维度。 @@ -46,7 +46,7 @@ CFColor fromHexWithAlpha("#80FF0000"); // 50% 透明的红色 // 从 HCT 值 CFColor fromHCT(240.0f, 80.0f, 50.0f); // 蓝色,饱和度 80,明度 50 -```text +``` HCT 值的范围: - `hue`:0° 到 360°,绕色相环一圈 @@ -69,7 +69,7 @@ float t = color.tone(); // 约 42(中等偏暗) // 基于这些值生成变体 CFColor lighterVariant(color.hue(), color.chroma() * 0.8f, color.tone() + 20); CFColor darkerVariant(color.hue(), color.chroma() * 1.2f, color.tone() - 20); -```text +``` HCT 值在构造时计算并缓存,所以访问是 O(1) 的。别担心性能问题。 @@ -87,7 +87,7 @@ float lumBg = bg.relativeLuminance(); // ~1.0(白色) // 对比度 = (Lmax + 0.05) / (Lmin + 0.05) float ratio = (std::max(lumText, lumBg) + 0.05) / (std::min(lumText, lumBg) + 0.05); -```text +``` 这个函数在 `color_helper.h` 的 `contrastRatio()` 里被使用,一般不需要直接调用。但如果你想自己实现一些可访问性相关的逻辑,可以用它。 @@ -105,7 +105,7 @@ QColor qColor = myColor.native_color(); QPainter painter(this); painter.setBrush(myColor.native_color()); painter.drawEllipse(center, 50, 50); -```text +``` `CFColor` 不是 `QColor` 的子类,而是组合关系。这样设计是为了避免 `QColor` 的隐式转换带来的意外——`QColor(const char*)` 会把字符串当成颜色名,而我们希望 hex 字符串有明确的解析行为。 @@ -129,7 +129,7 @@ QList palette = tonalPalette(seed); CFColor primary = palette[6]; // 主要色 CFColor onPrimary = palette[10]; // 主要色上的文字 CFColor surface = palette[4]; // 表面色 -```text +``` 这样生成的主题保证色彩和谐,避免了手工调整 RGB 时容易出现的"脏色"问题。 diff --git a/document/HandBook/ui/base/color_helper.md b/document/HandBook/ui/base/color_helper.md index 78a27e4e3..efb2328e0 100644 --- a/document/HandBook/ui/base/color_helper.md +++ b/document/HandBook/ui/base/color_helper.md @@ -24,7 +24,7 @@ CFColor result = blend(base, overlay, 0.5f); // 半透明遮罩效果 CFColor dimmed = blend(originalColor, CFColor(0, 0, 0), 0.3f); -```text +``` 混合是在 RGB 空间线性进行的,不是 alpha 混合。ratio = 0 返回 base,ratio = 1 返回 overlay,中间值按比例线性组合。 @@ -44,7 +44,7 @@ CFColor card = elevationOverlay(surface, primary, 4); // 高程 8dp 的对话框 CFColor dialog = elevationOverlay(surface, primary, 8); -```text +``` 高程越大,surface 颜色会越向 primary 靠近,模拟阴影和光照效果。这是 Material Design 3 的标准做法,比单纯用 alpha 叠加黑色要精致一些。 @@ -68,7 +68,7 @@ if (contrastRatio(foreground, background) < 4.5f) { // 自动选择深色/浅色文字 CFColor textColor = contrastRatio(darkText, bgColor) > contrastRatio(lightText, bgColor) ? darkText : lightText; -```text +``` 对比度是基于相对亮度计算的,返回值范围是 1.0 到 21.0。1.0 表示两个颜色亮度相同,21.0 是黑白之间的最大对比度。 @@ -90,7 +90,7 @@ QList palette = tonalPalette(keyColor); CFColor surface = palette[4]; // 浅色表面 CFColor surfaceVariant = palette[6]; // 变体表面 CFColor onSurface = palette[10]; // 表面上的文字 -```text +``` 色调板是 Material Design 3 主题系统的核心——整个颜色系统都是从单个"种子颜色"衍生出来的。生成的色调遵循 Material 规范,确保视觉和谐度。 diff --git a/document/HandBook/ui/base/device_pixel.md b/document/HandBook/ui/base/device_pixel.md index 44e7e986b..99025adb5 100644 --- a/document/HandBook/ui/base/device_pixel.md +++ b/document/HandBook/ui/base/device_pixel.md @@ -27,7 +27,7 @@ qreal fontSize = helper.spToPx(14.0); // 14sp -> 28px // 反向转换:物理像素转回设备无关像素 qreal dpValue = helper.pxToDp(352.0); // 352px -> 176dp -```text +``` 设备像素比通常从 Qt 的 `QWindow` 或 `QScreen` 获取: @@ -35,7 +35,7 @@ qreal dpValue = helper.pxToDp(352.0); // 352px -> 176dp // 在 Qt 窗口中获取当前 DPI qreal dpi = window()->devicePixelRatio(); CanvasUnitHelper helper(dpi); -```text +``` ## 单位类型 @@ -63,7 +63,7 @@ if (bp == CanvasUnitHelper::BreakPoint::Compact) { // 桌面布局:>= 840dp // 使用多列布局,显示完整导航 } -```text +``` 断点判断使用的是 dp 值,而不是物理像素。这样不管屏幕 DPI 如何,布局行为保持一致。 @@ -83,7 +83,7 @@ int rounded = qRound(helper.dpToPx(16.0)); // 错误的做法:直接截断 int truncated = static_cast(helper.dpToPx(16.0)); -```text +``` ## 性能考虑 diff --git a/document/HandBook/ui/base/easing.md b/document/HandBook/ui/base/easing.md index 7e2feeaea..1b7d47f26 100644 --- a/document/HandBook/ui/base/easing.md +++ b/document/HandBook/ui/base/easing.md @@ -23,7 +23,7 @@ QEasingCurve curve = fromEasingType(Type::Standard); QPropertyAnimation* anim = new QPropertyAnimation(this, "geometry"); anim->setEasingCurve(fromEasingType(Type::Emphasized)); anim->setDuration(300); -```text +``` 各种类型的区别: - `Linear`:匀速,没有加速感 @@ -47,7 +47,7 @@ QEasingCurve overshoot = custom(0.34f, 1.56f, 0.64f, 1.0f); // 弹性效果 QEasingCurve bounce = custom(0.68f, -0.6f, 0.32f, 1.6f); -```text +``` 控制点参数的含义: - `x1, y1` 是第一个控制点的坐标 @@ -77,7 +77,7 @@ SpringPreset stiff = springStiff(); // 配合 math_helper 的 springStep() 使用 auto [pos, vel] = math::springStep(currentPos, velocity, targetPos, bouncy.stiffness, bouncy.damping, dt); -```text +``` 弹簧的调优确实有点玄学——stiffness 太小会拖沓,太大容易震荡;damping 太小会一直抖,太大又没有弹性。我们提供的预设是在实际项目中调出来的经验值,基本够用了。 @@ -100,7 +100,7 @@ anim->setEasingCurve(fromEasingType(Type::Linear)); // 按钮点击反馈:用弹簧,增加触感 SpringPreset preset = springBouncy(); // 在 update 循环中调用 springStep() -```text +``` 总的原则:**用户主动触发的操作用强缓动/弹簧,系统自动的状态变化用弱缓动**。这样既能传达操作的"手感",又不会让界面显得太花哨。 diff --git a/document/HandBook/ui/base/geometry_helper.md b/document/HandBook/ui/base/geometry_helper.md index f13cf1268..cddffc812 100644 --- a/document/HandBook/ui/base/geometry_helper.md +++ b/document/HandBook/ui/base/geometry_helper.md @@ -27,7 +27,7 @@ QPainterPath path3 = roundedRect(rect, ShapeScale::ShapeLarge); // 完全圆角 (50%) - 变成椭圆/胶囊形 QPainterPath path4 = roundedRect(rect, ShapeScale::ShapeFull); -```text +``` 这些预设值来自 Material Design 3 的形状规范,确保整个应用的视觉语言一致。 @@ -43,7 +43,7 @@ QPainterPath buttonPath = roundedRect(buttonRect, 8.0f); // 大圆角,营造现代感 QPainterPath cardPath = roundedRect(cardRect, 16.0f); -```text +``` ⚠️ 半径单位是像素,不是 dp。在高 DPI 屏幕上需要乘以设备像素比,否则看起来会太小。 @@ -65,7 +65,7 @@ QPainterPath bubblePath = roundedRect(bubbleRect, 16.0f, // topRight 16.0f, // bottomLeft 4.0f); // bottomRight -```text +``` 这种用法在做聊天气泡、底部抽屉之类的组件时特别常见。Qt 原生 API 需要手动构建 path,我们的封装简化了这个过程。 @@ -90,7 +90,7 @@ void paintEvent(QPaintEvent* event) override { painter.setPen(QPen(QColor("#E0E0E0"), 1.0)); painter.drawPath(path); } -```text +``` 记得启用 `Antialiasing`,否则圆角会有明显的锯齿。 diff --git a/document/HandBook/ui/base/math_helper.md b/document/HandBook/ui/base/math_helper.md index 5e71348cf..b1f22e2f6 100644 --- a/document/HandBook/ui/base/math_helper.md +++ b/document/HandBook/ui/base/math_helper.md @@ -24,7 +24,7 @@ float opacity = lerp(0.0f, 1.0f, progress); // progress 从 0 到 1 // 颜色通道插值 int red = static_cast(lerp(startColor.red(), endColor.red(), t)); -```text +``` 插值参数 t 不限制在 [0, 1] 范围内——超出时会产生"外推"效果,这在某些动画场景下是有用的,但要小心使用。 @@ -41,7 +41,7 @@ float normalized = remap(input, 0.0f, 100.0f, -1.0f, 1.0f); // 进度条的实际像素位置 float pixel = remap(progress, 0.0f, 1.0f, 0.0f, trackWidth); -```text +``` `remap()` 在处理滑块、进度条这类 UI 控件时特别方便——不用自己算比例和偏移了。 @@ -56,7 +56,7 @@ float eased = cubicBezier(0.2f, 0.0f, 0.0f, 1.0f, t); // 自定义曲线 float custom = cubicBezier(0.25f, 0.1f, 0.25f, 1.0f, progress); -```text +``` 这个函数的参数控制点 x 坐标建议限制在 [0, 1] 范围内,否则曲线会出现"回退"效果(一个 t 对应多个 x)。y 坐标可以超出这个范围,产生弹性过冲效果。 @@ -78,7 +78,7 @@ auto [newPos, newVel] = springStep(pos, vel, target, 1.0f/60.0f); pos = newPos; vel = newVel; -```text +``` stiffness 控制弹簧的硬度——值越大回弹越快。damping 控制阻尼——值越小震荡越明显。这两个参数的调优有点玄学,我们提供了一些预设值(参见 `easing.h`)。 @@ -94,7 +94,7 @@ float angle = lerpAngle(350.0f, 10.0f, 0.5f); // 返回 0° // 旋转动画 float currentAngle = lerpAngle(startAngle, targetAngle, progress); -```text +``` 这个函数对旋转动画特别有用——不然你的 UI 元素可能会莫名其妙地绕一大圈。 diff --git a/document/HandBook/ui/components/animation.md b/document/HandBook/ui/components/animation.md index 0cdc28db5..54818a400 100644 --- a/document/HandBook/ui/components/animation.md +++ b/document/HandBook/ui/components/animation.md @@ -13,7 +13,7 @@ description: 是所有动画组件的抽象基类,定义了动画的生命周 ```cpp enum class State { Idle, Running, Paused, Finished }; -```text +``` - `Idle`:动画尚未启动或已停止,处于初始状态 - `Running`:动画正在运行中 @@ -45,7 +45,7 @@ connect(anim, &ICFAbstractAnimation::progressChanged, [](float progress) { // 启动动画 anim->start(ICFAbstractAnimation::Direction::Forward); -```text +``` ## 方向控制 @@ -60,7 +60,7 @@ anim->start(ICFAbstractAnimation::Direction::Backward); // 翻转当前方向 anim->reverse(); // 停止并以相反方向重新开始 -```text +``` ⚠️ `reverse()` 会停止当前动画并重新启动,而不是简单地改变播放方向。如果需要无缝反向,需要自己管理逻辑。 @@ -72,7 +72,7 @@ anim->pause(); // 停止并重置到初始状态 anim->stop(); -```text +``` `stop()` 和 `pause()` 的区别在于:`pause()` 可以恢复,而 `stop()` 会将动画重置回初始状态。 @@ -82,7 +82,7 @@ anim->stop(); ```cpp cf::WeakPtr weak = anim->GetWeakPtr(); -```text +``` ⚠️ 动画类本身不是线程安全的。如果需要在多线程环境下使用,外部需要自行加锁。 diff --git a/document/HandBook/ui/components/animation_factory_manager.md b/document/HandBook/ui/components/animation_factory_manager.md index d9f5a6f01..d95f6c8aa 100644 --- a/document/HandBook/ui/components/animation_factory_manager.md +++ b/document/HandBook/ui/components/animation_factory_manager.md @@ -24,7 +24,7 @@ auto anim = factory->getAnimation("md.animation.fadeIn"); if (anim) { anim->start(); } -```text +``` ## 动画注册 @@ -41,7 +41,7 @@ if (result == ICFAnimationManagerFactory::RegisteredResult::OK) { } else if (result == ICFAnimationManagerFactory::RegisteredResult::DUP_NAME) { // 名称重复 } -```text +``` 如果需要更精细的控制,可以用 `registerAnimationCreator` 传入一个 lambda: @@ -51,7 +51,7 @@ factory->registerAnimationCreator("customFade", [](QObject* parent) { anim->setDuration(500); // 自定义时长 return anim; }); -```text +``` ⚠️ 传入的 lambda 会在每次 `getAnimation()` 被调用时执行,所以不要在这里捕获可能会失效的局部变量。 @@ -65,7 +65,7 @@ if (anim) { // 动画存在且可用 anim->start(); } -```text +``` 返回 `WeakPtr` 而不是原始指针,是因为工厂拥有动画的所有权。如果工厂被销毁,所有返回的 `WeakPtr` 会自动失效,不会变成悬空指针: @@ -79,7 +79,7 @@ if (anim) { if (anim) { // 这里不会执行,因为 anim 已经失效 } -```text +``` ## 全局开关 @@ -99,7 +99,7 @@ factory->setTargetEnabled("md.animation.fadeIn", false); if (!factory->targetEnabled("md.animation.fadeIn")) { // 这个动画被禁用 } -```text +``` ⚠️ `setEnabledAll(false)` 只影响**新创建**的动画,已经正在运行的动画会继续直到完成。如果需要立即停止所有动画,需要自己维护一个引用列表并逐个调用 `stop()`。 @@ -110,7 +110,7 @@ if (!factory->targetEnabled("md.animation.fadeIn")) { ```cpp factory->setTargetFps(60.0f); // 60 FPS factory->setTargetFps(30.0f); // 30 FPS(省电) -```text +``` 这个值会影响动画 tick 之间的时间间隔,但不会改变动画的总时长——时长是通过 `MotionSpec` 控制的。 @@ -124,7 +124,7 @@ enum class RegisteredResult { DUP_NAME, // 名称已存在 UNSUPPORT_TYPE // 不支持的类型 }; -```text +``` 遇到 `DUP_NAME` 时,如果要替换已存在的动画,需要先移除再重新注册。目前接口没有直接提供移除方法,这是设计上的取舍——我们假设动画注册是在初始化阶段一次性完成的。 diff --git a/document/HandBook/ui/components/animation_group.md b/document/HandBook/ui/components/animation_group.md index c6fc778f2..607f256e4 100644 --- a/document/HandBook/ui/components/animation_group.md +++ b/document/HandBook/ui/components/animation_group.md @@ -13,7 +13,7 @@ description: 是动画组合容器,用于将多个动画作为一个整体来 ```cpp enum class Mode { Parallel, Sequential }; -```text +``` `Parallel` 模式下,所有动画同时启动,适合处理多个属性的同步变化。`Sequential` 模式下,动画按添加顺序依次执行,前一个完成后才开始下一个。 @@ -37,7 +37,7 @@ group->addAnimation(scaleAnim->GetWeakPtr()); // 启动整个组 group->start(ICFAbstractAnimation::Direction::Forward); -```text +``` ## 顺序执行 @@ -54,7 +54,7 @@ sequentialGroup->addAnimation(anim3->GetWeakPtr()); // anim1 完成后执行 anim2,然后 anim3 sequentialGroup->start(); -```text +``` ## 弱引用管理 @@ -71,7 +71,7 @@ delete anim; // 移除操作也是安全的 group->removeAnimation(invalidWeakPtr); // 无操作 -```text +``` ## 生命周期 @@ -81,7 +81,7 @@ group->removeAnimation(invalidWeakPtr); // 无操作 group->pause(); // 暂停组内所有动画 group->stop(); // 停止并重置所有动画 group->reverse(); // 翻转执行方向 -```text +``` `stop()` 会停止组内所有动画并将它们重置到初始状态,而 `pause()` 只是暂停,可以继续恢复。 diff --git a/document/HandBook/ui/components/spring_animation.md b/document/HandBook/ui/components/spring_animation.md index 0d424d3c3..c3f892ea5 100644 --- a/document/HandBook/ui/components/spring_animation.md +++ b/document/HandBook/ui/components/spring_animation.md @@ -15,7 +15,7 @@ description: 是基于物理弹簧模型的动画基类,用真实的弹簧动 加速度 = (目标值 - 当前值) * 刚度 - 速度 * 阻尼 速度 += 加速度 * dt 位置 += 速度 * dt -```text +``` 系统会一直迭代,直到位置足够接近目标值且速度足够小。这意味着动画的实际时长取决于初始距离和物理参数,而不是预先设定的时间。 @@ -34,7 +34,7 @@ auto* anim = new CFSpringAnimation(gentle, this); // 或者用高弹性预设 auto bouncy = cf::ui::base::Easing::springBouncy(); auto* anim2 = new CFSpringAnimation(bouncy, this); -```text +``` 框架提供了三种预设: - `springGentle()`:柔和的弹簧,回弹较少,适合常规过渡 @@ -51,7 +51,7 @@ anim->setTarget(1.0f); // 运行过程中改变目标,弹簧会立即转向 anim->setTarget(0.5f); // 会立即向新目标加速 -```text +``` 这种"追逐"特性使得弹簧动画非常适合交互式场景——比如拖拽释放后,元素会"弹"回原位,或者在拖拽过程中跟随手指。 @@ -66,7 +66,7 @@ anim->setInitialVelocity(100.0f); // 配合负目标值,可以做出"甩出去"的效果 anim->setTarget(-1.0f); anim->setInitialVelocity(500.0f); -```text +``` 如果没有设置初始速度,默认从静止开始。 @@ -76,7 +76,7 @@ anim->setInitialVelocity(500.0f); ```cpp float position = anim->currentValue(); -```text +``` 这个值会随着弹簧振荡而变化,直到收敛到目标值。 @@ -101,7 +101,7 @@ tick(int dt) { position += velocity * dt; return !isSettled(); // 是否已稳定 } -```text +``` 如果你在做一个按钮的点击反馈: - 时间动画:按下时缩小,松开时放大,像在"播放一段视频" @@ -131,7 +131,7 @@ bool isSettled() { return abs(position - target) < epsilon && abs(velocity) < epsilon; } -```text +``` ⚠️ 这意味着弹簧可能在理论上"永远"不结束(阻尼过低时会无限振荡)。实际使用中,框架会在若干帧后强制结束,避免空耗 CPU。 diff --git a/document/HandBook/ui/components/timing_animation.md b/document/HandBook/ui/components/timing_animation.md index 048682522..0d9ee2ca1 100644 --- a/document/HandBook/ui/components/timing_animation.md +++ b/document/HandBook/ui/components/timing_animation.md @@ -14,7 +14,7 @@ description: 是基于时间的动画基类,使用固定的持续时间和缓 ```text progress = easing(elapsed / duration) value = from + progress * (to - from) -```text +``` ## 创建动画 @@ -27,7 +27,7 @@ value = from + progress * (to - from) // 假设 theme->motionSpec() 返回有效的 IMotionSpec auto* motionSpec = theme->motionSpec(); auto* anim = new CFTimingAnimation(motionSpec, this); -```text +``` ⚠️ `IMotionSpec` 指针必须在动画的生命周期内保持有效。这个设计是经过权衡的——动画工厂创建动画时持有对主题的引用,而主题拥有 motion spec,所以生命周期是绑定的,不需要额外拷贝。 @@ -44,7 +44,7 @@ anim->setRange(0.0f, 255.0f); // 从位置 A 到位置 B anim->setRange(startX, endX); -```text +``` `setRange()` 可以在动画运行时调用,会即时改变当前帧的计算基准,但通常建议在 `start()` 前设置好。 @@ -54,7 +54,7 @@ anim->setRange(startX, endX); ```cpp float current = anim->currentValue(); -```bash +``` ## 时间动画 vs 弹簧动画 @@ -89,7 +89,7 @@ connect(fadeIn, &ICFAbstractAnimation::finished, []() { // 启动 fadeIn->start(); -```text +``` ## 线程安全 diff --git a/document/HandBook/ui/core/color_scheme.md b/document/HandBook/ui/core/color_scheme.md index 8bdec3f2b..a0835994f 100644 --- a/document/HandBook/ui/core/color_scheme.md +++ b/document/HandBook/ui/core/color_scheme.md @@ -21,7 +21,7 @@ QColor primary = colors.queryColor("md.primary"); QColor onPrimary = colors.queryColor("md.onPrimary"); QColor surface = colors.queryColor("md.surface"); QColor error = colors.queryColor("md.error"); -```text +``` `queryColor()` 返回的是 `QColor` 的副本,适合直接使用。如果需要避免拷贝开销(比如在循环中频繁访问),可以用 `queryExpectedColor()` 获取引用: @@ -32,7 +32,7 @@ for (int i = 0; i < 1000; ++i) { // 使用 primary,不会触发拷贝 painter.setBrush(primary); } -```text +``` ⚠️ `queryExpectedColor()` 返回的引用生命周期和 color scheme 对象绑定。如果 color scheme 被销毁,引用就会悬空——这个坑在异步代码里特别容易出现,务必注意。 @@ -53,7 +53,7 @@ for (int i = 0; i < 1000; ++i) { "md.surface" // 表面色 "md.error" // 错误色 "md.outline" // 边框/分割线色 -```text +``` 具体的 token 列表由实现类决定,可以在运行时动态扩展。如果查询不存在的 token,实现类的行为取决于具体实现——我们推荐返回一个明显的 fallback 颜色(比如品红色),方便调试。 @@ -67,7 +67,7 @@ QColor& queryExpectedColor(const char* name); // 返回拷贝,只读便捷方法 QColor queryColor(const char* name) const; -```text +``` `queryExpectedColor()` 返回非 const 引用是有意为之的——这允许调用方动态修改颜色值,实现运行时主题调整。比如实现"跟随系统 accent color"的功能时,可以直接修改对应的颜色槽位: @@ -77,7 +77,7 @@ void updateAccentColor(ICFColorScheme& colors, const QColor& newAccent) { colors.queryExpectedColor("md.primary") = newAccent; colors.queryExpectedColor("md.primaryContainer") = newAccent.lighter(120%); } -```text +``` ⚠️ 直接修改颜色会影响所有使用这个 color scheme 的组件。如果只是局部需要特殊颜色,应该在查询后自行调整,而不是修改共享的 scheme。 diff --git a/document/HandBook/ui/core/font_type.md b/document/HandBook/ui/core/font_type.md index ec578f236..4c7702b57 100644 --- a/document/HandBook/ui/core/font_type.md +++ b/document/HandBook/ui/core/font_type.md @@ -21,7 +21,7 @@ QFont bodyLarge = fonts.queryTargetFont("bodyLarge"); QFont headlineMedium = fonts.queryTargetFont("headlineMedium"); QFont titleSmall = fonts.queryTargetFont("titleSmall"); QFont labelLarge = fonts.queryTargetFont("labelLarge"); -```text +``` `queryTargetFont()` 返回的是 `QFont` 的副本,适合直接使用。Qt 的 `QFont` 采用隐式共享(写时拷贝)机制,所以即使返回副本,实际拷贝开销也很小——只有在修改字体时才会真正复制数据。 @@ -48,7 +48,7 @@ QFont labelLarge = fonts.queryTargetFont("labelLarge"); "labelLarge" // 标签大号 "labelMedium" // 标签中号 "labelSmall" // 标签小号 -```text +``` 具体的 token 列表由实现类决定,可以在运行时动态扩展。如果查询不存在的 token,实现类的行为取决于具体实现——我们推荐返回一个默认的 fallback 字体,比如系统等宽字体,方便调试。 @@ -75,7 +75,7 @@ protected: painter.drawText(rect().adjusted(0, 30, 0, 0), "Body text"); } }; -```text +``` ⚠️ 不要在每次绘制时都查询字体——把字体对象缓存起来更好。`QFont` 的隐式共享机制让缓存几乎没有开销: @@ -90,7 +90,7 @@ public: m_titleFont = fonts.queryTargetFont("headlineMedium"); } }; -```text +``` ## 实现要点 diff --git a/document/HandBook/ui/core/motion_spec.md b/document/HandBook/ui/core/motion_spec.md index 4e9d65156..52bf6f182 100644 --- a/document/HandBook/ui/core/motion_spec.md +++ b/document/HandBook/ui/core/motion_spec.md @@ -25,7 +25,7 @@ int easing = motion.queryEasing("md.motion.standard"); // 线性/缓入 // 查询动画延迟(毫秒) int delay = motion.queryDelay("md.motion.shortEnter"); // 通常为 0 -```text +``` 这三个参数组合起来可以构造完整的动画配置: @@ -39,7 +39,7 @@ animation->setEasingCurve(static_cast( animation->setStartValue(startRect); animation->setEndValue(endRect); animation->start(); -```text +``` ## Token 命名约定 @@ -58,7 +58,7 @@ animation->start(); "md.motion.mediumDuration" // 中等持续时间(250ms) "md.motion.longDuration" // 长持续时间(350ms) "md.motion.extraLongDuration" // 超长持续时间(500ms+) -```text +``` 缓动类型返回的是整数值,需要映射到具体的缓动曲线枚举。Qt 的 `QEasingCurve::Type` 是一个常见的映射目标: @@ -73,7 +73,7 @@ animation->start(); // 33 -> OutCubic // 34 -> InOutCubic // ... -```text +``` 具体的映射关系由实现类决定,可以在文档中说明。 @@ -99,7 +99,7 @@ void fadeInWidget(QWidget* widget, const char* motionToken) { // 使用 fadeInWidget(myWidget, "md.motion.shortEnter"); // 快速淡入 -```text +``` 对于需要延迟的动画序列: @@ -117,7 +117,7 @@ void staggeredShow(QList widgets) { animation->start(); } } -```text +``` ## 缓动曲线映射 @@ -145,7 +145,7 @@ auto curve = EasingCurveMapper::fromMotionValue( motion.queryEasing("md.motion.standard") ); animation->setEasingCurve(curve); -```text +``` ## 实现要点 diff --git a/document/HandBook/ui/core/radius_scale.md b/document/HandBook/ui/core/radius_scale.md index d48292e73..a5c1b0f56 100644 --- a/document/HandBook/ui/core/radius_scale.md +++ b/document/HandBook/ui/core/radius_scale.md @@ -21,7 +21,7 @@ float small = radius.queryRadiusScale("cornerSmall"); float medium = radius.queryRadiusScale("cornerMedium"); float large = radius.queryRadiusScale("cornerLarge"); float extraLarge = radius.queryRadiusScale("cornerExtraLarge"); -```text +``` `queryRadiusScale()` 返回的是浮点数值,单位是密度无关像素(dp)。返回值可以直接用于 Qt 组件的圆角设置: @@ -30,7 +30,7 @@ float extraLarge = radius.queryRadiusScale("cornerExtraLarge"); QPushButton* button = new QPushButton("Click Me"); float cornerRadius = radius.queryRadiusScale("cornerMedium"); button->setStyleSheet(QString("border-radius: %1px;").arg(cornerRadius)); -```text +``` ## Token 命名约定 @@ -45,7 +45,7 @@ button->setStyleSheet(QString("border-radius: %1px;").arg(cornerRadius)); "cornerLarge" // 大圆角(16dp) "cornerExtraLarge" // 极大圆角(28dp) "cornerFull" // 完全圆角(形状变成胶囊或圆形) -```text +``` 具体的 token 列表由实现类决定,可以在运行时动态扩展。如果查询不存在的 token,接口规定返回 0——这是一个便于判断的 fallback 值,但可能隐藏配置错误。 @@ -78,7 +78,7 @@ protected: painter.fillPath(path, palette().button()); } }; -```text +``` 使用时: @@ -88,7 +88,7 @@ auto* smallBtn = new RoundedButton("cornerSmall"); // 轻微圆角 auto* mediumBtn = new RoundedButton("cornerMedium"); // 标准圆角 auto* largeBtn = new RoundedButton("cornerLarge"); // 大圆角 auto* pillBtn = new RoundedButton("cornerFull"); // 胶囊形状 -```text +``` ## 与样式表集成 @@ -102,7 +102,7 @@ void applyCornerRadius(QWidget* widget, const char* cornerToken) { "QWidget { border-radius: %1px; }" ).arg(r)); } -```text +``` ⚠️ 样式表中的圆角是按像素计算的,而 `queryRadiusScale()` 返回的是 dp 值。在高 DPI 屏幕上,需要手动乘以设备像素比率: @@ -113,7 +113,7 @@ float dpToPx(float dp) { // 正确的高 DPI 处理 float r = dpToPx(radius.queryRadiusScale("cornerMedium")); -```text +``` ## 实现要点 diff --git a/document/HandBook/ui/core/theme.md b/document/HandBook/ui/core/theme.md index 4f1bf1ab9..b28b5728e 100644 --- a/document/HandBook/ui/core/theme.md +++ b/document/HandBook/ui/core/theme.md @@ -21,7 +21,7 @@ auto& colors = theme.color_scheme(); // ICFColorScheme& auto& motion = theme.motion_spec(); // IMotionSpec& auto& radius = theme.radius_scale(); // IRadiusScale& auto& fonts = theme.font_type(); // IFontType& -```text +``` 所有访问器都返回引用,这是因为子组件的生命周期由 `ICFTheme` 的实现类管理,调用方不需要关心所有权问题。 @@ -42,7 +42,7 @@ auto custom_theme = factory->fromJson(json); // 序列化主题 QByteArray serialized = factory->toJson(theme.get()); -```text +``` 使用工厂模式的好处是可以在运行时动态切换主题实现,比如从文件加载的 JSON 主题和硬编码的默认主题可以共存。 @@ -64,7 +64,7 @@ float corner = theme.radius_scale().queryRadiusScale("cornerSmall"); // 获取字体 QFont body_font = theme.font_type().queryTargetFont("bodyLarge"); -```text +``` 注意这里每个子组件都有自己的查询语法——这是为兼容 Material Design 3 的 token 命名设计的。具体的 token 名称由各个子组件的实现决定,`ICFTheme` 不做统一规定。 diff --git a/document/HandBook/ui/core/theme_factory.md b/document/HandBook/ui/core/theme_factory.md index 793280c07..45a03d9ea 100644 --- a/document/HandBook/ui/core/theme_factory.md +++ b/document/HandBook/ui/core/theme_factory.md @@ -25,7 +25,7 @@ public: // 将主题序列化为 JSON QByteArray toJson(cf::ui::core::ICFTheme* raw_theme) override; }; -```text +``` 注意返回类型是 `std::unique_ptr`,调用者获得主题的所有权。这个设计很自然——工厂负责生产,消费者负责销毁。 @@ -54,7 +54,7 @@ std::unique_ptr MyThemeFactory::fromName(const char* nam return theme; } -```text +``` `name` 参数来自 `ThemeManager::insert_one` 时注册的名字,但工厂内部可以有自己的命名空间逻辑——你可以用同一个工厂支持多个主题变体,或者完全忽略名字只返回固定配置。 @@ -94,7 +94,7 @@ std::unique_ptr MyThemeFactory::fromJson(const QByteArra return theme; } -```text +``` JSON 格式完全由你定义,只要工厂能解析就行。我们建议遵循一定的约定,比如 `colors` 对象包含颜色、`motion` 对象包含动画参数,这样不同主题的配置文件可以保持一致性。 @@ -127,7 +127,7 @@ QByteArray MyThemeFactory::toJson(cf::ui::core::ICFTheme* raw_theme) { return QJsonDocument(root).toJson(QJsonDocument::Compact); } -```text +``` `raw_theme` 是裸指针而不是引用,这是为了兼容 Qt 的信号槽参数传递习惯。但在实际使用中,你传进来的应该总是一个有效的主题指针。 @@ -147,7 +147,7 @@ cf::ui::core::ThemeManager::instance().insert_one("builtin", []() { cf::ui::core::ThemeManager::instance().insert_one("custom", []() { return std::make_unique(); }); -```text +``` 注意这里用的是 lambda 而不是直接传工厂实例。`ThemeManager` 会在需要时调用 lambda 来创建工厂,而不是在注册时就创建。这个延迟创建机制可以减少启动时的开销,特别是当你注册了很多插件主题但大部分都不会被使用时。 @@ -182,7 +182,7 @@ std::unique_ptr MyThemeFactory::fromJson(const QByteArra // ... 构造主题 } -```text +``` 返回 `nullptr` 表示"无法创建",`ThemeManager` 会把这个情况传达给调用方。我们不建议在工厂方法里抛异常,因为 Qt 的信号槽机制和异常配合得不太好,而且这也让错误处理逻辑更清晰。 @@ -254,7 +254,7 @@ public: return QJsonDocument(root).toJson(); } }; -```text +``` ## 相关文档 diff --git a/document/HandBook/ui/core/theme_manager.md b/document/HandBook/ui/core/theme_manager.md index 1e214886f..c0ff23d55 100644 --- a/document/HandBook/ui/core/theme_manager.md +++ b/document/HandBook/ui/core/theme_manager.md @@ -15,7 +15,7 @@ description: 是整个 UI 主题系统的入口,负责注册主题工厂、管 #include "ui/core/theme_manager.h" auto& manager = cf::ui::core::ThemeManager::instance(); -```text +``` 单例的初始化是线程安全的(C++11 魔法静态变量保证),不用担心多线程竞态。但我们只在 UI 线程里使用它,Qt 的信号机制本身就要求如此。 @@ -33,7 +33,7 @@ manager.insert_one("default", []() { manager.insert_one("dark_blue", []() { return std::make_unique(); }); -```text +``` `insert_one` 返回 `bool`,如果名字已经存在会返回 `false`。这个设计挺实用,可以用来检测插件冲突——同一主题被两个插件注册时,至少有一个会失败。 @@ -49,7 +49,7 @@ manager.setThemeTo("dark_blue"); // 切换时可以选择不广播(特殊场景下使用) manager.setThemeTo("default", false); -```text +``` 默认情况下,切换主题会广播 `themeChanged` 信号到所有通过 `install_widget` 注册的 widget。`doBroadcast` 参数设为 `false` 会跳过广播,这主要用于批量操作时避免重复通知——比如你要连续切换三个主题,中间的两次就不需要广播。 @@ -85,7 +85,7 @@ private slots: .arg(colors.background().name())); } }; -```text +``` 管理器持有的是 `QWidget` 的裸指针观察者,不负责生命周期。widget 析构时必须调用 `remove_widget`,否则管理器里会留下悬空指针。这个设计有点不安全,但 Qt 的对象树机制使得在 widget 析构时自动清理变得复杂,我们选择了简单粗暴的显式管理方式。 @@ -103,7 +103,7 @@ const cf::ui::core::ICFTheme& theme = manager.theme("default"); // 读取颜色配置 const auto& colors = theme.color_scheme(); QColor bg = colors.background(); -```text +``` `theme()` 方法返回的是常量引用,主题实例会被缓存起来,后续访问直接从缓存取。第一次访问时才会调用工厂创建,所以第一次可能会有轻微延迟。 @@ -113,7 +113,7 @@ QColor bg = colors.background(); ```cpp manager.remove_one("old_theme"); -```text +``` 移除操作不会影响当前正在使用的主题——即使你移除的是当前主题,管理器里仍然保留着它的实例,直到切换到另一个主题。这个行为可能是 feature 也可能是 bug,取决于你从哪个角度看,但它确实避免了一些诡妙的崩溃。 @@ -183,7 +183,7 @@ void MyWidget::setupThemeConnection() { void SettingsDialog::onThemeSelected(const QString& name) { cf::ui::core::ThemeManager::instance().setThemeTo(name.toStdString()); } -```text +``` ## 相关文档 diff --git a/document/HandBook/ui/core/token/material_scheme/cfmaterial_token_literals.md b/document/HandBook/ui/core/token/material_scheme/cfmaterial_token_literals.md index 11d76f5af..1850214ab 100644 --- a/document/HandBook/ui/core/token/material_scheme/cfmaterial_token_literals.md +++ b/document/HandBook/ui/core/token/material_scheme/cfmaterial_token_literals.md @@ -35,7 +35,7 @@ const char* onTertiary = ON_TERTIARY; // Error 色系 - 错误状态和危险操作 const char* error = ERROR; const char* onError = ON_ERROR; -```text +``` 注意那个 "On" 前缀——这不是"打开"的意思,而是"绘制在...之上"(On)。`ON_PRIMARY` 就是绘制在 Primary 颜色上的文字和图标的颜色,Material 的配色算法会自动计算对比度,保证可读性。 @@ -55,7 +55,7 @@ Container 色是 Material You 新增的概念。它们是基色的"调色版本" // 场景:一个强调区域 // area_bg 使用 tertiaryContainer // area_icon 使用 onTertiaryContainer -```text +``` 这样设计的好处是,组件的颜色关系由语义决定,而不是由具体的颜色值决定。动态换肤时,整个应用的颜色关系依然保持一致。 @@ -79,7 +79,7 @@ const char* onSurfaceVariant = ON_SURFACE_VARIANT; // 表面变体上的文字 // 边框颜色 const char* outline = OUTLINE; // 边框和分割线 const char* outlineVariant = OUTLINE_VARIANT; // 微差分的边框色 -```text +``` `surfaceVariant` 和 `outlineVariant` 这两个名字确实有点拗口。它们的作用是在深色模式下提供"不那么突兀"的边框和背景,避免视觉噪音过多。 @@ -96,7 +96,7 @@ const char* scrim = SCRIM; // 模态背景遮罩 const char* inverseSurface = INVERSE_SURFACE; // 反转的表面色 const char* inverseOnSurface = INVERSE_ON_SURFACE; // 反转表面上的文字 const char* inversePrimary = INVERSE_PRIMARY; // 反转的主色 -```text +``` `scrim` 是当弹出模态对话框时,后面那层半透明的黑色遮罩。深色模式下它可能是半透明的白色,取决于主题的动态生成逻辑。 @@ -119,7 +119,7 @@ auto buttonBg = theme.resolve(PRIMARY); auto buttonText = theme.resolve(ON_PRIMARY); auto cardBg = theme.resolve(SURFACE); auto cardBorder = theme.resolve(OUTLINE); -```text +``` ## 批量遍历 @@ -134,7 +134,7 @@ size_t count = TOKEN_COUNT; // 26 个 for (size_t i = 0; i < TOKEN_COUNT; ++i) { printf("Token %zu: %s\n", i, ALL_TOKENS[i]); } -```text +``` ## Token 命名规则 diff --git a/document/HandBook/ui/core/token/motion/cfmaterial_motion_token_literals.md b/document/HandBook/ui/core/token/motion/cfmaterial_motion_token_literals.md index 8d60a79f0..bafe98521 100644 --- a/document/HandBook/ui/core/token/motion/cfmaterial_motion_token_literals.md +++ b/document/HandBook/ui/core/token/motion/cfmaterial_motion_token_literals.md @@ -46,7 +46,7 @@ const char* rippleExpandDuration = MOTION_RIPPLE_EXPAND_DURATION; // 涟漪消散 - 150ms const char* rippleFadeDuration = MOTION_RIPPLE_FADE_DURATION; -```text +``` 注意退出时长通常比进入时长短,这是个刻意的设计——用户等待东西出现比等待东西消失更不耐烦。 @@ -80,7 +80,7 @@ const char* rippleExpandEasing = MOTION_RIPPLE_EXPAND_EASING; // 涟漪消散缓动 - Linear const char* rippleFadeEasing = MOTION_RIPPLE_FADE_EASING; -```bash +``` ## 缓动函数说明 @@ -133,7 +133,7 @@ auto dialogEnter = theme.resolveMotion( MOTION_LONG_ENTER_EASING ); dialog.animate(dialogEnter); -```text +``` ## 场景选择指南 @@ -178,7 +178,7 @@ auto rippleExpand = resolveMotion( MOTION_RIPPLE_EXPAND_DURATION, MOTION_RIPPLE_EXPAND_EASING ); -```text +``` ⚠️ 一个常见的错误是用错缓动方向。进入动画应该用 Decelerate(减速),这样元素到达终点时会柔和地停下来;退出动画应该用 Accelerate(加速),元素快速离开不留痕迹。用反了会感觉很怪——进入时"哐"一下撞上终点,退出时拖泥带水。 @@ -191,7 +191,7 @@ auto rippleExpand = resolveMotion( for (size_t i = 0; i < MOTION_TOKEN_COUNT; ++i) { printf("Motion Token %zu: %s\n", i, ALL_MOTION_TOKENS[i]); } -```text +``` ## 自定义动效参数 @@ -205,7 +205,7 @@ for (size_t i = 0; i < MOTION_TOKEN_COUNT; ++i) { // 对于特殊场景,直接传值更实际 MotionSpec custom = {500, CustomEasing}; element.animate(custom); -```text +``` 建议把常用的自定义动效也纳入 Token 管理,保持代码的一致性。 @@ -220,7 +220,7 @@ element.animate(spec); // 大量元素同时动画时,考虑简化动效 // 比如用短时长代替长时长,或者取消缓动 -```text +``` Material 的动效时长(最大 450ms)是经过平衡的——既足够传达视觉反馈,又不会让用户等太久。如果你的动效感觉"拖",可能不是时长问题,而是缓动曲线选择不当。 @@ -237,7 +237,7 @@ if (userPrefersReducedMotion()) { // 正常动画 element.animate(spec); } -```text +``` Material 的动效设计已经考虑了可访问性,但提供降级选项仍然是好实践。 diff --git a/document/HandBook/ui/core/token/radius_scale/cfmaterial_radius_scale_literals.md b/document/HandBook/ui/core/token/radius_scale/cfmaterial_radius_scale_literals.md index ba853e24e..d58ea1c6b 100644 --- a/document/HandBook/ui/core/token/radius_scale/cfmaterial_radius_scale_literals.md +++ b/document/HandBook/ui/core/token/radius_scale/cfmaterial_radius_scale_literals.md @@ -38,7 +38,7 @@ const char* cornerXL = CORNER_EXTRA_LARGE; // "md.shape.cornerExtraLarge" // 超超大圆角 - 32dp const char* cornerXXL = CORNER_EXTRA_EXTRA_LARGE; // "md.shape.cornerExtraExtraLarge" -```text +``` 注意这里用的是 XS/XL 这种缩写,而不是 ExtraSmall/ExtraLarge。常量名为了可读性用的完整拼写,但命名空间内提供别名是个不错的实践(不过当前实现里还没加,需要的话可以自己补)。 @@ -79,7 +79,7 @@ const char* modalRadius = CORNER_EXTRA_LARGE; // 32dp - 超超大圆角 // 用于:导航抽屉、全屏模态 const char* drawerRadius = CORNER_EXTRA_EXTRA_LARGE; -```text +``` ## 在主题系统中使用 @@ -107,7 +107,7 @@ card.setCornerRadius(cardRadius); // 设置 FAB 圆角 float fabRadius = theme.resolveRadius(CORNER_EXTRA_LARGE); fab.setCornerRadius(fabRadius); -```text +``` ## 圆角选择指南 @@ -129,7 +129,7 @@ const char* largeComponentRadius = CORNER_LARGE; // 正圆形组件(如 FAB) // 用 28dp 或 32dp,接近半圆 const char* circularComponentRadius = CORNER_EXTRA_LARGE; -```text +``` ⚠️ 一个常见的错误是给小型组件用太大的圆角。比如一个高度只有 32dp 的按钮用 16dp 圆角,结果圆角占了一半高度,看起来会很奇怪。圆角值应该和组件尺寸成比例。 @@ -144,7 +144,7 @@ const char* circularComponentRadius = CORNER_EXTRA_LARGE; // 方式 2:直接使用数值(不推荐,但现实) // 对于特别的设计需求,有时候直接传值更实际 button.setCornerRadius(20.0f); // 自定义值 -```text +``` 如果团队有统一的设计规范,建议把常用的自定义圆角也加到 Token 系统里,保持代码的语义化。 @@ -157,7 +157,7 @@ button.setCornerRadius(20.0f); // 自定义值 for (size_t i = 0; i < RADIUS_TOKEN_COUNT; ++i) { printf("Radius Token %zu: %s\n", i, ALL_RADIUS_TOKENS[i]); } -```text +``` ## 响应式考虑 @@ -168,7 +168,7 @@ for (size_t i = 0; i < RADIUS_TOKEN_COUNT; ++i) { float scale = getDeviceDensity(); float radiusInPx = theme.resolveRadius(CORNER_MEDIUM) * scale; view.setCornerRadiusPx(radiusInPx); -```text +``` ## 相关文档 diff --git a/document/HandBook/ui/core/token/typography/cfmaterial_typography_token_literals.md b/document/HandBook/ui/core/token/typography/cfmaterial_typography_token_literals.md index 146fdf1bd..5261ed93e 100644 --- a/document/HandBook/ui/core/token/typography/cfmaterial_typography_token_literals.md +++ b/document/HandBook/ui/core/token/typography/cfmaterial_typography_token_literals.md @@ -42,7 +42,7 @@ const char* bodySmall = TYPOGRAPHY_BODY_SMALL; // 12sp, 辅助文字 const char* labelLarge = TYPOGRAPHY_LABEL_LARGE; // 14sp, 按钮文字 const char* labelMedium = TYPOGRAPHY_LABEL_MEDIUM; // 12sp, 标签 const char* labelSmall = TYPOGRAPHY_LABEL_SMALL; // 11sp, 小标签 -```bash +``` ## 字体属性说明 @@ -87,7 +87,7 @@ button.setFontWeight(buttonStyle.fontWeight); // 设置正文 auto bodyStyle = theme.resolveTypography(TYPOGRAPHY_BODY_MEDIUM); textLabel.setTextStyle(bodyStyle); -```text +``` ## 行高 Token @@ -103,7 +103,7 @@ const char* lineHeightTitle = LINEHEIGHT_TITLE_LARGE; // "md.lineHeight.titleLar // 使用场景:某些框架需要分别设置 theme.setTypography(TYPOGRAPHY_BODY_MEDIUM); theme.setLineHeight(LINEHEIGHT_BODY_MEDIUM); -```text +``` 不过在我们的推荐使用方式中,行高应该由 Typography Token 统一管理,单独使用行高 Token 是比较边缘的场景。 @@ -131,7 +131,7 @@ theme.setLineHeight(LINEHEIGHT_BODY_MEDIUM); // 场景 5:时间戳、元数据 // 选 Label Small(11sp)或 Body Small(12sp) // Label Small 字重更重,适合标签;Body Small 更轻,适合辅助信息 -```text +``` ## 批量遍历 @@ -147,7 +147,7 @@ for (size_t i = 0; i < TYPOGRAPHY_TOKEN_COUNT; ++i) { for (size_t i = 0; i < LINEHEIGHT_TOKEN_COUNT; ++i) { printf("LineHeight Token %zu: %s\n", i, ALL_LINEHEIGHT_TOKENS[i]); } -```text +``` ## 可访问性考虑 diff --git a/document/HandBook/ui/material/animation/cfmaterial_animation_factory.md b/document/HandBook/ui/material/animation/cfmaterial_animation_factory.md index f9ce4daf8..97834696b 100644 --- a/document/HandBook/ui/material/animation/cfmaterial_animation_factory.md +++ b/document/HandBook/ui/material/animation/cfmaterial_animation_factory.md @@ -44,7 +44,7 @@ if (fadeIn) { fadeIn->setTargetWidget(myWidget); fadeIn->start(); } -```text +``` 工厂拥有创建的动画实例,返回的是 `WeakPtr`。这个设计是为了避免悬空指针——如果工厂被销毁,所有返回的 WeakPtr 都会自动失效。 @@ -72,7 +72,7 @@ factory->getAnimation(ANIMATION_SCALE_DOWN); // md.animation.scaleDown // 旋转 factory->getAnimation(ANIMATION_ROTATE_IN); // md.animation.rotateIn factory->getAnimation(ANIMATION_ROTATE_OUT); // md.animation.rotateOut -```text +``` ## 动画策略模式 @@ -98,7 +98,7 @@ public: // 设置策略 factory->setStrategy(std::make_unique()); -```text +``` 策略在动画创建之前被调用,所以你可以基于 widget 的状态、尺寸或者任何你关心的条件来调整动画参数。 @@ -121,7 +121,7 @@ auto customFade = factory->createAnimation(desc, myWidget); if (customFade) { customFade->start(); } -```text +``` ⚠️ 注意:`createAnimation()` 总是创建新的动画实例,而 `getAnimation()` 会复用已存在的实例。如果你需要多个独立的动画实例(比如同时控制多个 widget),应该用 `createAnimation()`。 @@ -139,7 +139,7 @@ factory->setEnabledAll(true); if (QGuiApplication::styleHints()->showIsFullScreen() == false) { factory->setEnabledAll(false); } -```text +``` `setEnabledAll()` 只影响新创建的动画,已经运行的动画不会被中断。 @@ -153,7 +153,7 @@ auto anim = factory->getAnimation("md.animation.fadeIn"); if (anim) { // 这里会失败,WeakPtr 已经失效 anim->start(); } -```text +``` ⚠️ 这个坑在异步代码里尤其容易出现——如果你把 WeakPtr 存起来延迟使用,一定要检查有效性。 diff --git a/document/HandBook/ui/material/animation/cfmaterial_animation_strategy.md b/document/HandBook/ui/material/animation/cfmaterial_animation_strategy.md index fa8354ee8..0d109e061 100644 --- a/document/HandBook/ui/material/animation/cfmaterial_animation_strategy.md +++ b/document/HandBook/ui/material/animation/cfmaterial_animation_strategy.md @@ -32,7 +32,7 @@ struct AnimationDescriptor { float toValue; // 结束值 int delayMs = 0; // 延迟时间(毫秒) }; -```text +``` 策略可以修改其中任何一个字段。比如你可以把一个 `slideUp` 改成 `fadeIn`,或者把 `mediumEnter` 时长替换为 `shortEnter`。 @@ -53,7 +53,7 @@ public: return desc; } }; -```text +``` 这个实现其实就是 `DefaultAnimationStrategy` 的做法——当你不设置策略时,工厂默认使用的就是这个。 @@ -81,7 +81,7 @@ public: return adjusted; } }; -```text +``` Material Design 3 的时长标准是:shortEnter=200ms、mediumEnter=300ms、longEnter=400ms,对应的 exit 时长稍短一些。 @@ -106,7 +106,7 @@ public: return globalEnabled_; } }; -```text +``` `shouldEnable()` 返回 false 时,工厂的 `getAnimation()` 和 `createAnimation()` 会返回无效的 WeakPtr。 @@ -159,7 +159,7 @@ private: return desc; } }; -```text +``` ## 全局启用状态 @@ -167,7 +167,7 @@ private: ```cpp strategy->setGlobalEnabled(false); // 禁用所有使用此策略的动画 -```text +``` 这个设置不会影响 `shouldEnable()` 的其他逻辑——你的实现仍然可以在 `globalEnabled_` 为 false 时返回 true。 diff --git a/document/HandBook/ui/material/animation/cfmaterial_fade_animation.md b/document/HandBook/ui/material/animation/cfmaterial_fade_animation.md index 391e4fc89..071bd5075 100644 --- a/document/HandBook/ui/material/animation/cfmaterial_fade_animation.md +++ b/document/HandBook/ui/material/animation/cfmaterial_fade_animation.md @@ -37,7 +37,7 @@ fadeAnim->setTargetWidget(myDialog); // 开始动画(Forward = 淡入,Backward = 淡出) fadeAnim->start(Direction::Forward); -```text +``` 动画通过 `QGraphicsOpacityEffect` 作用于 widget,这意味着它适用于所有继承自 `QWidget` 的控件,包括那些本身不直接支持透明度属性的组件。 @@ -52,7 +52,7 @@ connect(fadeAnim.get(), &ICFAbstractAnimation::finished, this, [widget]() { // 设置为某个中间透明度值 widget->setWindowOpacity(0.8); }); -```bash +``` ## 时序控制 @@ -79,14 +79,14 @@ widget->setGraphicsEffect(blurEffect); // 淡入动画会创建独立的 opacity effect // 不会影响已有的模糊效果 fadeAnim->setTargetWidget(widget); -```text +``` 第二,effect 的生命周期由动画管理。当动画销毁时,如果 effect 是它创建的,也会一并销毁。如果你需要在动画结束后保留最终的透明度状态,需要注意 effect 的所有权问题: ```cpp // 动画结束后 effect 会被清理,widget 会恢复到不透明状态 // 如果需要保持半透明,考虑监听 finished 信号并手动设置样式 -```text +``` ## 生命周期控制 @@ -102,7 +102,7 @@ fadeAnim->pause(); // 暂停在当前状态 fadeAnim->reverse(); // 从当前状态反向播放(淡出) fadeAnim->stop(); // 停止并重置到初始状态 -```text +``` `reverse()` 是一个很方便的操作,它会让动画从当前进度开始反向播放,而不是跳回起点。这在实现"鼠标悬停显示,移开隐藏"这类交互时特别好用。 @@ -127,7 +127,7 @@ void showModalDialog(QWidget* dialog) { fadeAnim->setTargetWidget(dialog); fadeAnim->start(Direction::Forward); } -```text +``` ### 加载状态切换 @@ -145,7 +145,7 @@ void hideLoadingIndicator() { fadeAnim->setTargetWidget(loadingLabel); fadeAnim->start(); } -```text +``` ### 组合动画 @@ -161,7 +161,7 @@ fadeAnim->setTargetWidget(widget); slideAnim->start(); fadeAnim->start(); -```text +``` ## 相关文档 diff --git a/document/HandBook/ui/material/animation/cfmaterial_scale_animation.md b/document/HandBook/ui/material/animation/cfmaterial_scale_animation.md index 19b019b5f..2078a20f3 100644 --- a/document/HandBook/ui/material/animation/cfmaterial_scale_animation.md +++ b/document/HandBook/ui/material/animation/cfmaterial_scale_animation.md @@ -37,7 +37,7 @@ scaleAnim->setTargetWidget(myDialog); // 开始动画(Forward = 放大,Backward = 缩小) scaleAnim->start(Direction::Forward); -```text +``` 默认的缩放范围是 0.8 到 1.0(从 80% 放大到 100%),这是 Material 推荐的"弹出"效果参数。 @@ -53,14 +53,14 @@ scaleAnim->setTargetWidget(widget); scaleAnim->start(); // widget 会保持中心位置不变,四周同时收缩/扩张 -```text +``` 如果需要从左上角或其他位置缩放,可以关闭中心缩放模式: ```cpp scaleAnim->setScaleFromCenter(false); // 此时缩放会以 widget 几何原点(左上角)为锚点 -```bash +``` ⚠️ 非中心缩放在某些布局下可能会导致 widget 位置发生明显偏移,使用时需要谨慎测试。 @@ -80,7 +80,7 @@ scaleAnim->start(); // ... 动画进行中 ... qDebug() << widget->width(); // 当前缩放后的宽度 qDebug() << widget->height(); // 当前缩放后的高度 -```bash +``` ## 时序参数 @@ -115,7 +115,7 @@ void showDialog(QWidget* dialog) { scaleAnim->setTargetWidget(dialog); scaleAnim->start(Direction::Forward); } -```text +``` ### 按钮按下效果 @@ -138,7 +138,7 @@ public: QPushButton::mouseReleaseEvent(event); } }; -```text +``` ### 菜单展开 @@ -154,7 +154,7 @@ void showMenu(QWidget* menu) { scaleAnim->setTargetWidget(menu); scaleAnim->start(); } -```text +``` ### 组合动画:弹出 + 淡入 @@ -173,7 +173,7 @@ void showCard(QWidget* card) { scaleAnim->start(); fadeAnim->start(); } -```text +``` 两个动画同步运行能产生"从无到有"的完整视觉体验——缩放负责空间维度,透明度负责视觉维度。 @@ -195,7 +195,7 @@ void showCard(QWidget* card) { // 微妙的强调:从 0.95 开始 // 适合小元素或需要低调的场景 -```text +``` 缩放到 1.0 以上(放大效果)在 Material Design 中比较少见,通常用于特殊交互如图片预览。这种场景下可能需要配合淡入淡出来避免突兀。 @@ -213,7 +213,7 @@ layout->addWidget(myWidget); auto scaleAnim = std::make_unique(&motionSpec); scaleAnim->setTargetWidget(myWidget); scaleAnim->start(); // 可能导致布局抖动 -```cpp +``` 解决这个问题有几种方案: diff --git a/document/HandBook/ui/material/animation/cfmaterial_slide_animation.md b/document/HandBook/ui/material/animation/cfmaterial_slide_animation.md index 23c4ac393..4873337cf 100644 --- a/document/HandBook/ui/material/animation/cfmaterial_slide_animation.md +++ b/document/HandBook/ui/material/animation/cfmaterial_slide_animation.md @@ -42,7 +42,7 @@ slideAnim->setDistance(100.0f); // 设置目标并启动 slideAnim->setTargetWidget(myCard); slideAnim->start(Direction::Forward); -```text +``` 方向枚举的命名可能会让你困惑——`SlideDirection::Up` 表示 widget 向上移动,视觉上是"从下方滑入"。反过来想:枚举值描述的是 widget 移动的轨迹方向,而不是来源方向。 @@ -60,7 +60,7 @@ slideAnim->setDistance(parentWidget->height()); // Snackbar 从底部轻微上浮 slideAnim->setDistance(50.0f); -```text +``` ⚠️ 距离值应该是正数。方向由 `SlideDirection` 控制,不需要用负距离来表示反向移动。 @@ -74,7 +74,7 @@ slideAnim->setTargetWidget(widget); slideAnim->start(Direction::Forward); // ... 动画运行中,widget 位置被修改 ... slideAnim->stop(); // widget 回到 (100, 100) -```text +``` 但如果你在动画过程中手动移动了 widget,动画停止时恢复的位置仍然是启动时的原始位置,这可能导致视觉跳跃。如果需要在动画结束后将 widget 定位到新位置,应该在 `finished` 信号中处理: @@ -83,7 +83,7 @@ connect(slideAnim.get(), &ICFAbstractAnimation::finished, this, [widget]() { // 动画结束后,手动设置到目标位置 widget->move(200, 100); }); -```text +``` ## 方向详解 @@ -105,7 +105,7 @@ offset = QPoint(-distance, 0) // SlideDirection::Right: widget 向右移动(从左侧进入) // 位移应用到 x 轴,offset 为正值 offset = QPoint(distance, 0) -```bash +``` 理解这个逻辑有助于你在调试时判断问题方向——如果动画朝反方向移动,大概率是方向枚举选错了。 @@ -140,7 +140,7 @@ void showBottomSheet(QWidget* sheet, QWidget* parent) { slideAnim->setTargetWidget(sheet); slideAnim->start(); } -```text +``` ### 侧边菜单滑入 @@ -157,7 +157,7 @@ void showSideMenu(QWidget* menu, QWidget* parent) { slideAnim->setTargetWidget(menu); slideAnim->start(); } -```text +``` ### 列表项交错动画 @@ -174,7 +174,7 @@ void showListItems(const QList& items) { slideAnim->start(); } } -```text +``` ### 下拉刷新 @@ -188,7 +188,7 @@ void onPullDown(float offset) { // 反向播放:回到原位 slideAnim->start(Direction::Backward); } -```text +``` ## 性能优化建议 @@ -212,7 +212,7 @@ fadeAnim->setTargetWidget(widget); slideAnim->start(); fadeAnim->start(); -```yaml +``` 这种组合在卡片、对话框等"重要元素"出现时特别有效。 diff --git a/document/HandBook/ui/material/base/elevation_controller.md b/document/HandBook/ui/material/base/elevation_controller.md index 21d7aa545..b3d928620 100644 --- a/document/HandBook/ui/material/base/elevation_controller.md +++ b/document/HandBook/ui/material/base/elevation_controller.md @@ -48,7 +48,7 @@ public: private: base::MdElevationController* m_elevation; }; -```text +``` ## 设置高程级别 @@ -59,7 +59,7 @@ private: m_elevation->setElevation(0); // 无阴影 m_elevation->setElevation(3); // 中等阴影 m_elevation->setElevation(5); // 最强阴影 -```text +``` 高程值会被 clamp 在 [0, 5] 范围内,超出范围的值会自动截断。 @@ -86,7 +86,7 @@ void MyWidget::mouseReleaseEvent(QMouseEvent* event) { m_elevation->setPressed(false); m_elevation->animateTo(2, core::MotionSpec::standard()); } -```text +``` `MotionSpec` 定义了动画的缓动曲线和时长,使用 Material 标准值可确保动画感觉一致。 @@ -108,7 +108,7 @@ void MyWidget::paintEvent(QPaintEvent* event) { // 其他绘制... } -```text +``` ⚠️ 阴影必须先绘制,否则会覆盖控件内容。绘制顺序错了视觉效果会很奇怪。 @@ -125,7 +125,7 @@ m_elevation->setLightSourceAngle(0.0f); // 光源来自右侧 m_elevation->setLightSourceAngle(-30.0f); -```text +``` 角度正值表示光源从左侧来,阴影向右投射;负值表示光源从右侧来。这个参数影响阴影的水平偏移量。 @@ -148,7 +148,7 @@ void MyWidget::paintEvent(QPaintEvent* event) { // 正常绘制... } -```text +``` 按压时阴影会缩小并靠近控件(约 50%),同时控件向下移动,产生"按下"的视觉反馈。 @@ -160,7 +160,7 @@ void MyWidget::paintEvent(QPaintEvent* event) { CFColor MdElevationController::tonalOverlay(CFColor surface, CFColor primary) const { // 返回混合后的表面颜色 } -```text +``` 使用方式: @@ -176,7 +176,7 @@ if (isDark) { // 亮色主题使用阴影 backgroundColor = surfaceColor; } -```bash +``` 色调叠加量与高程级别成正比,级别越高叠加越多。 diff --git a/document/HandBook/ui/material/base/focus_ring.md b/document/HandBook/ui/material/base/focus_ring.md index 39841afd7..d0684f64d 100644 --- a/document/HandBook/ui/material/base/focus_ring.md +++ b/document/HandBook/ui/material/base/focus_ring.md @@ -46,7 +46,7 @@ public: private: base::MdFocusIndicator* m_focusIndicator; }; -```text +``` ## 事件处理 @@ -64,7 +64,7 @@ void MyWidget::focusOutEvent(QFocusEvent* event) { m_focusIndicator->onFocusOut(); update(); } -```text +``` ⚠️ 记得在事件处理函数中先调用父类实现,否则 Qt 的焦点系统可能无法正常工作。 @@ -87,7 +87,7 @@ void MyWidget::paintEvent(QPaintEvent* event) { m_focusIndicator->paint(&p, shape(), indicatorColor); } } -```text +``` 聚焦环的颜色通常使用 `onSurface` 角色获取,这样可以与控件内容保持一致的对比度。 @@ -109,7 +109,7 @@ m_focusIndicator->paint(&p, shape, indicatorColor); // 自定义形状 QPainterPath shape = customShape(); m_focusIndicator->paint(&p, shape, indicatorColor); -```text +``` 环会自动沿着形状的边界向内偏移绘制,不需要手动计算偏移量。 @@ -123,7 +123,7 @@ auto factory = Application::animationFactory(); if (factory) { factory->setEnabledAll(false); } -```text +``` 这对于低端设备或性能敏感的场景很有用。 @@ -136,7 +136,7 @@ MyWidget::MyWidget(QWidget* parent) : QWidget(parent) { setFocusPolicy(Qt::StrongFocus); // ... } -```text +``` 对于纯装饰性的控件,使用 `Qt::NoFocus` 避免干扰键盘导航流。 diff --git a/document/HandBook/ui/material/base/painter_layer.md b/document/HandBook/ui/material/base/painter_layer.md index 0e71865bd..093751773 100644 --- a/document/HandBook/ui/material/base/painter_layer.md +++ b/document/HandBook/ui/material/base/painter_layer.md @@ -30,7 +30,7 @@ public: private: base::PainterLayer* m_stateLayer; }; -```text +``` ## 绘制调用 @@ -49,7 +49,7 @@ void MyWidget::paintEvent(QPaintEvent* event) { // 再绘制其他内容... } -```text +``` `paint()` 方法内部会处理透明度小于等于零的情况直接返回,所以不需要在外层判断。 @@ -67,7 +67,7 @@ void PainterLayer::paint(QPainter* painter, const QPainterPath& clipPath) { painter->fillPath(clipPath, color); } -```text +``` 这意味着如果颜色本身是半透明的(比如 alpha = 0.5),再设置 opacity = 0.5,最终 alpha 会是 0.25。这个设计允许你用一个基础颜色控制整体色调,用 opacity 控制当前状态下的强度。 @@ -102,7 +102,7 @@ class MyWidget : public QWidget { update(); } }; -```text +``` 实际项目中我们通常配合 `StateMachine` 来管理这些状态变化,而不是在每个事件里手动设置。 @@ -120,7 +120,7 @@ void MyWidget::updateStateLayerColor() { m_stateLayer->setColor(cf::ui::base::CFColor(onSurface)); } -```text +``` ⚠️ 不要用背景色做状态层颜色,那会改变控件的"色调"而不是"深浅"。Material 的状态层是通过叠加一层半透明的文本颜色来模拟"变深"或"变亮"的视觉效果。 @@ -132,7 +132,7 @@ void MyWidget::updateStateLayerColor() { // 手动触发重绘 m_stateLayer->setOpacity(newOpacity); update(); // 别忘了这个 -```text +``` 这和 `RippleHelper` 的设计不同——后者内部管理动画并主动发出 `repaintNeeded()` 信号,因为涟漪是"主动"的视觉效果,而状态层是"被动"的。 @@ -178,7 +178,7 @@ private: base::PainterLayer* m_stateLayer; base::PainterLayer* m_maskLayer; }; -```text +``` 绘制顺序很重要:背景 → 状态层 → 遮罩层 → 内容。改变顺序会破坏视觉层次。 @@ -194,7 +194,7 @@ m_layer = new base::PainterLayer(this); m_layer = new base::PainterLayer(nullptr); // ... 使用完毕后 delete m_layer; -```text +``` ## 为什么不直接用 QColor @@ -204,7 +204,7 @@ delete m_layer; // 看起来更简单的方式 QColor m_stateColor; float m_stateOpacity = 0.0f; -```text +``` 问题在于一致性——当有多个控件需要状态层、多个层需要管理时,每个控件都要自己实现"填充带透明度的颜色到路径"的逻辑,容易出错。抽出 `PainterLayer` 后: diff --git a/document/HandBook/ui/material/base/ripple_helper.md b/document/HandBook/ui/material/base/ripple_helper.md index eb5d1bd79..6602efb3e 100644 --- a/document/HandBook/ui/material/base/ripple_helper.md +++ b/document/HandBook/ui/material/base/ripple_helper.md @@ -22,7 +22,7 @@ float maxRadius(const QRectF& rect, const QPointF& center) { float d4 = std::hypot(center.x() - rect.bottomRight().x(), center.y() - rect.bottomRight().y()); return std::max({d1, d2, d3, d4}); } -```text +``` ## 基本用法 @@ -55,7 +55,7 @@ public: private: base::RippleHelper* m_rippleHelper; }; -```text +``` ## 事件处理 @@ -73,7 +73,7 @@ void MyWidget::mouseReleaseEvent(QMouseEvent* event) { m_rippleHelper->onRelease(); update(); } -```text +``` `onCancel()` 用于取消未释放的涟漪,比如鼠标拖出控件范围时: @@ -83,7 +83,7 @@ void MyWidget::leaveEvent(QEvent* event) { m_rippleHelper->onCancel(); // 清除所有涟漪 update(); } -```text +``` ⚠️ `onCancel()` 会立即清除所有涟漪,不做淡出动画。这是有意为之的设计——当用户明确"取消"交互时,不需要延迟反馈。 @@ -99,7 +99,7 @@ enum class Mode { // 设置模式 m_rippleHelper->setMode(base::RippleHelper::Mode::Bounded); -```text +``` 大多数控件应该使用 `Bounded` 模式,让涟漪限制在圆角矩形内。`Unbounded` 模式适用于特殊场景,比如浮动操作按钮(FAB)的涟漪可以扩散到圆形边界外。 @@ -126,7 +126,7 @@ void MyWidget::paintEvent(QPaintEvent* event) { // 绘制内容 // drawContent(p); } -```text +``` ## 颜色设置 @@ -140,7 +140,7 @@ QColor labelColor = colors->queryExpectedColor("md.onSurface"); // 设置涟漪颜色 m_rippleHelper->setColor(cf::ui::base::CFColor(labelColor)); -```text +``` 在浅色主题上使用深色涟漪,深色主题上使用浅色涟漪,这是确保对比度的基本要求。 @@ -154,7 +154,7 @@ QRadialGradient gradient(ripple.center, ripple.radius); gradient.setColorAt(0.0f, color); // 中心完全实色 gradient.setColorAt(0.7f, color); // 70% 半径处仍是实色 gradient.setColorAt(1.0f, transparent); // 边缘渐变到透明 -```text +``` 这种处理避免了锯齿边缘,使涟漪看起来更自然。 @@ -168,7 +168,7 @@ auto factory = Application::animationFactory(); if (factory) { factory->setEnabledAll(false); } -```bash +``` 这对于低端设备或性能敏感的场景很有用。另外,`hasActiveRipple()` 可以用来判断是否需要重绘,避免无效的 `paintEvent` 调用。 diff --git a/document/HandBook/ui/material/cfmaterial_fonttype.md b/document/HandBook/ui/material/cfmaterial_fonttype.md index 366a67070..f6b13c234 100644 --- a/document/HandBook/ui/material/cfmaterial_fonttype.md +++ b/document/HandBook/ui/material/cfmaterial_fonttype.md @@ -42,7 +42,7 @@ auto typography = cf::ui::core::material::defaultTypography(); // 查询字体 QFont titleFont = typography.queryTargetFont("md.typography.titleLarge"); QFont bodyFont = typography.queryTargetFont("md.typography.bodyMedium"); -```text +``` 字体名称采用 `md.typography.` 前缀,后跟 Material 官方定义的样式名称。 @@ -59,7 +59,7 @@ TitleFonts title = typography.title(); // 获取正文组 BodyFonts body = typography.body(); -```text +``` 字体组结构体(`DisplayFonts`、`HeadlineFonts` 等)主要是为了类型安全和代码可读性。 @@ -72,7 +72,7 @@ MaterialTypography typography = material::defaultTypography(); float lineHeight = typography.getLineHeight("md.typography.bodyLarge"); // 返回 24.0(单位 sp) -```bash +``` 行高在多行文本布局时特别重要,确保行与行之间有合适的呼吸空间。 @@ -94,7 +94,7 @@ MaterialTypography typography = material::defaultTypography(); QFont customFont("Roboto"); customFont.setPointSize(16); typography.registry().set("md.typography.bodyMedium", customFont); -```text +``` ## 缓存机制 @@ -106,7 +106,7 @@ QFont font1 = typography.queryTargetFont("md.typography.titleLarge"); // 后续查询从缓存返回 QFont font2 = typography.queryTargetFont("md.typography.titleLarge"); -```text +``` 缓存在 `MaterialTypography` 对象生命周期内有效。 @@ -122,7 +122,7 @@ void MyLabel::updateFont() { QFont titleFont = typography->queryTargetFont("md.typography.titleLarge"); setFont(titleFont); } -```text +``` 也可以直接设置到 `QPainter`: @@ -136,7 +136,7 @@ void paintEvent(QPaintEvent*) { painter.drawText(rect(), "Hello Material"); } -```text +``` ## 大小单位说明 @@ -146,7 +146,7 @@ Material 规范使用 `sp`(Scale-independent Pixels)作为字体大小单位 // Material 规范:16sp QFont font; font.setPointSize(16); // Qt 中用 pointSize 近似 -```text +``` 这个近似在实际使用中效果足够好,因为 Qt 的字体系统已经有良好的 DPI 处理。 diff --git a/document/HandBook/ui/material/cfmaterial_motion.md b/document/HandBook/ui/material/cfmaterial_motion.md index b0e32cc1f..d5b51259d 100644 --- a/document/HandBook/ui/material/cfmaterial_motion.md +++ b/document/HandBook/ui/material/cfmaterial_motion.md @@ -43,7 +43,7 @@ int easing = motion.queryEasing("shortEnter"); MotionSpec spec = motion.getMotionSpec("mediumEnter"); // spec.durationMs = 300 // spec.easing = Easing::Type::EmphasizedDecelerate -```text +``` ## 运动规格结构 @@ -55,7 +55,7 @@ struct MotionSpec { cf::ui::base::Easing::Type easing; // 缓动类型 int delayMs = 0; // 延迟(毫秒) }; -```text +``` 这个结构可以直接用于动画设置: @@ -71,7 +71,7 @@ void MyWidget::animateIn() { anim->setEndValue(targetPos()); anim->start(QAbstractAnimation::DeleteWhenStopped); } -```text +``` ## 静态预设函数 @@ -89,7 +89,7 @@ MotionSpec spec = MotionPresets::mediumExit(); // 状态切换动画 MotionSpec spec = MotionPresets::stateChange(); // durationMs = 200, easing = Standard -```text +``` 静态函数在编译期就能确定值,没有字符串查找开销,性能更好。 @@ -105,7 +105,7 @@ auto all = motion.presets(); useSpec(all.shortEnter); useSpec(all.mediumExit); useSpec(all.rippleExpand); -```bash +``` 这个设计方便在需要同时使用多个预设时减少代码重复。 @@ -137,7 +137,7 @@ if (size.width() < 200) { } else { spec = MotionPresets::longEnter(); // 大元素 } -```text +``` 这个对应关系能保证动画速度和元素大小匹配——大物体运动慢,小物体运动快,符合物理直觉。 @@ -151,7 +151,7 @@ animateIn(MotionPresets::shortEnter()); // 退出:150ms(短) animateOut(MotionPresets::shortExit()); -```text +``` 同样的"短"级别,进入比退出慢 50ms。 @@ -171,7 +171,7 @@ void RippleEffect::start() { m_fadeSpec = motion->getMotionSpec("rippleFade"); // 150ms, Linear } -```text +``` 水波纹淡出用 Linear 是有原因的——淡出是透明度变化,用线性曲线更自然。 @@ -184,7 +184,7 @@ MotionSpec customSpec; customSpec.durationMs = 500; customSpec.easing = cf::ui::base::Easing::Type::Emphasized; customSpec.delayMs = 100; -```text +``` 或者基于预设修改: @@ -192,7 +192,7 @@ customSpec.delayMs = 100; MotionSpec spec = MotionPresets::mediumEnter(); spec.delayMs = 200; // 添加延迟 spec.durationMs = 400; // 延长时长 -```text +``` ## 相关文档 diff --git a/document/HandBook/ui/material/cfmaterial_radius_scale.md b/document/HandBook/ui/material/cfmaterial_radius_scale.md index e108cc7da..f779acc3a 100644 --- a/document/HandBook/ui/material/cfmaterial_radius_scale.md +++ b/document/HandBook/ui/material/cfmaterial_radius_scale.md @@ -37,7 +37,7 @@ auto radiusScale = cf::ui::core::material::defaultRadiusScale(); float smallRadius = radiusScale.queryRadiusScale("md.shape.cornerSmall"); // 8.0f float mediumRadius = radiusScale.queryRadiusScale("md.shape.cornerMedium"); // 12.0f float largeRadius = radiusScale.queryRadiusScale("md.shape.cornerLarge"); // 16.0f -```text +``` 圆角名称采用 `md.shape.` 前缀,后跟 Material 官方定义的规格名称。 @@ -67,7 +67,7 @@ void MyCard::paintEvent(QPaintEvent*) { QRectF rect = this->rect().adjusted(1, 1, -1, -1); painter.drawRoundedRect(rect, m_borderRadius, m_borderRadius); } -```text +``` ## 组件对应关系 @@ -88,7 +88,7 @@ float fabRadius = radiusScale.queryRadiusScale("md.shape.cornerExtraLarge"); // // 侧边栏用超大超大圆角 float drawerRadius = radiusScale.queryRadiusScale("md.shape.cornerExtraExtraLarge"); // 32dp -```text +``` ## 自定义圆角 @@ -102,7 +102,7 @@ radiusScale.registry().set("md.shape.cornerCustom", 20.0f); // 覆盖现有圆角 radiusScale.registry().set("md.shape.cornerMedium", 16.0f); // 改成和 Large 一样 -```text +``` 这个设计让系统既支持标准 Material 规范,也允许根据品牌需求调整。 @@ -116,7 +116,7 @@ float r1 = radiusScale.queryRadiusScale("md.shape.cornerLarge"); // 后续查询从缓存返回 float r2 = radiusScale.queryRadiusScale("md.shape.cornerLarge"); -```text +``` 缓存在 `MaterialRadiusScale` 对象生命周期内有效。 @@ -131,7 +131,7 @@ float radiusDp = radiusScale.queryRadiusScale("md.shape.cornerMedium"); // 转换为物理像素 qreal scaleFactor = devicePixelRatioF(); float radiusPx = radiusDp * scaleFactor; -```text +``` 大多数情况下直接用 dp 值传入 Qt 函数就可以,Qt 会自动处理 DPI 缩放。 @@ -146,7 +146,7 @@ painter.setRenderHint(QPainter::Antialiasing); // 使用 float 精度的绘制函数 painter.drawRoundedRect(rect, radius, radius); -```text +``` 小圆角值(如 4dp 的 ExtraSmall)在高 DPI 下可能会被平滑成几乎直线,这是正常行为。 diff --git a/document/HandBook/ui/material/cfmaterial_scheme.md b/document/HandBook/ui/material/cfmaterial_scheme.md index f9798cf32..1dea18ae9 100644 --- a/document/HandBook/ui/material/cfmaterial_scheme.md +++ b/document/HandBook/ui/material/cfmaterial_scheme.md @@ -35,7 +35,7 @@ auto darkScheme = cf::ui::core::material::dark(); QColor primary = lightScheme.queryExpectedColor("md.primary"); QColor onPrimary = lightScheme.queryExpectedColor("md.onPrimary"); QColor surface = lightScheme.queryExpectedColor("md.surface"); -```text +``` 颜色名称采用 `md.` 前缀,后跟 Material 官方定义的 token 名称。这样设计是为了和其他主题系统(比如我们未来可能实现的 Fluent)做区分。 @@ -55,7 +55,7 @@ PrimaryColors primary = scheme.primary(); // 需要实际颜色值时还是通过查询 QColor primaryColor = scheme.queryExpectedColor("md.primary"); QColor containerColor = scheme.queryExpectedColor("md.primaryContainer"); -```text +``` 颜色组结构体(`PrimaryColors`、`SecondaryColors` 等)主要是为了类型安全和文档目的,实际颜色值还是从 registry 中查询。 @@ -72,7 +72,7 @@ auto scheme = cf::ui::core::material::fromKeyColor(seedColor); // 生成深色版本 auto darkScheme = cf::ui::core::material::fromKeyColor(seedColor, true); -```text +``` 这个功能内部使用 HCT 色彩空间和 Material 的色调调色板算法。HCT(Hue-Chroma-Tone)是 Material 团队专门开发的色彩空间,比 HSL 更符合人眼对颜色的感知——这也是为什么 Material 的配色看起来特别和谐的原因。 @@ -105,7 +105,7 @@ if (result) { qDebug() << "JSON 解析失败:" << err.message.c_str(); } } -```text +``` 也支持直接传入颜色对象的方式(没有 `schemes` 包装的扁平结构)。 @@ -121,7 +121,7 @@ QByteArray json = material::toJson(scheme); QFile file("my_theme.json"); file.open(QIODevice::WriteOnly); file.write(json); -```text +``` ## Token 注册表 @@ -135,7 +135,7 @@ auto& registry = scheme.registry(); // 可以手动修改或添加 Token registry.set("md.customColor", QColor("#FF5722")); -```text +``` 这个设计让系统既支持预定义的 Material 颜色,也允许扩展自定义颜色。 @@ -149,7 +149,7 @@ QColor color1 = scheme.queryColor("md.primary"); // 后续查询从缓存返回 QColor color2 = scheme.queryColor("md.primary"); -```text +``` 缓存在 `MaterialColorScheme` 对象生命周期内有效。如果需要修改某个颜色后立即生效,直接修改 registry 即可——查询接口每次都会从 registry 读取最新值。 @@ -176,7 +176,7 @@ Material Design 3 定义了 26 个颜色角色,对应的查询名称如下: 工具色组: md.shadow, md.scrim, md.inverseSurface, md.inverseOnSurface, md.inversePrimary -```text +``` ## 相关文档 diff --git a/document/HandBook/ui/material/cfmaterial_theme.md b/document/HandBook/ui/material/cfmaterial_theme.md index 7658bac35..92b429554 100644 --- a/document/HandBook/ui/material/cfmaterial_theme.md +++ b/document/HandBook/ui/material/cfmaterial_theme.md @@ -34,7 +34,7 @@ auto darkTheme = factory.fromName("theme.material.dark"); // 从 JSON 创建 QByteArray json = loadThemeJson(); auto customTheme = factory.fromJson(json); -```text +``` 支持的预定义主题名称: - `theme.material.light`:默认浅色主题 @@ -62,7 +62,7 @@ float cardRadius = radiusScale->queryRadiusScale("md.shape.cornerMedium"); // 访问动画 auto* motion = static_cast(theme->motion_spec()); int duration = motion->queryDuration("shortEnter"); -```text +``` 这里需要 `static_cast` 是因为 `ICFTheme` 接口返回的是基类指针。在实际使用中,既然我们明确知道是 Material 主题,这样转换是安全的。 @@ -90,7 +90,7 @@ void MyWidget::paintEvent(QPaintEvent*) { QColor backgroundColor = colors->queryExpectedColor("md.surface"); // 使用背景色绘制... } -```text +``` ## 组件协调性 @@ -108,7 +108,7 @@ MaterialTheme theme2 = std::move(theme1); // 正确做法:使用工厂创建新实例 auto theme2 = factory.fromName("theme.material.light"); -```bash +``` ## Material Design 3 规范对应 diff --git a/document/HandBook/ui/material/material_factory_class.md b/document/HandBook/ui/material/material_factory_class.md index aa8110cd4..674401c8e 100644 --- a/document/HandBook/ui/material/material_factory_class.md +++ b/document/HandBook/ui/material/material_factory_class.md @@ -26,7 +26,7 @@ auto darkTheme = factory.fromName("theme.material.dark"); if (!lightTheme) { qDebug() << "主题创建失败,可能是名称错误"; } -```text +``` 如果传入无法识别的名称,`fromName` 会返回 `nullptr`。支持的名称列表: - `theme.material.light`:Material 默认浅色主题 @@ -57,7 +57,7 @@ auto theme = factory.fromJson(json); if (!theme) { qDebug() << "JSON 解析失败"; } -```text +``` JSON 格式可以只包含颜色,也可以包含完整的主题配置。如果某个部分缺失,工厂会使用默认值填充。 @@ -75,7 +75,7 @@ QByteArray json = factory.toJson(theme.get()); QFile file("my_theme.json"); file.open(QIODevice::WriteOnly); file.write(json); -```text +``` 导出的 JSON 兼容 Material Theme Builder 的导入格式,可以在在线工具中编辑后再导回。 @@ -89,7 +89,7 @@ setThemeFactory(std::move(factory)); // 后续可以通过通用接口创建主题 auto theme = themeFactory()->fromName("theme.material.light"); -```text +``` 这样设计的好处是可以在运行时切换不同的主题系统(比如未来添加 Fluent Design 支持),而不需要修改业务代码。 @@ -110,7 +110,7 @@ if (!result) { const auto& err = result.error(); // 可以根据 err.kind 判断具体错误类型 } -```text +``` 这是我们在后续版本中计划改进的地方。 @@ -127,7 +127,7 @@ auto t1 = factory.fromName("theme.material.light"); // 线程 2 auto t2 = factory.fromName("theme.material.dark"); -```text +``` 创建出来的 `MaterialTheme` 对象也是独立不共享的,可以在线程间传递。 diff --git a/document/HandBook/ui/material/material_factory_hpp.md b/document/HandBook/ui/material/material_factory_hpp.md index c5507f6bb..45e6abdd2 100644 --- a/document/HandBook/ui/material/material_factory_hpp.md +++ b/document/HandBook/ui/material/material_factory_hpp.md @@ -19,7 +19,7 @@ auto light = cf::ui::core::material::light(); // 默认深色方案 auto dark = cf::ui::core::material::dark(); -```text +``` 这两个函数返回 `MaterialColorScheme` 对象(不是指针),可以直接使用或移动。 @@ -36,7 +36,7 @@ auto scheme = cf::ui::core::material::fromKeyColor(seed); // 生成深色版本 auto darkScheme = cf::ui::core::material::fromKeyColor(seed, true); -```text +``` 种子颜色可以是用户选择的品牌色、墙纸的主色等等。生成的配色会自动计算 26 个颜色角色的值,确保视觉协调。 @@ -68,7 +68,7 @@ if (result) { break; } } -```text +``` 这个错误处理比 `MaterialFactory::fromJson` 的空指针友好得多。 @@ -84,7 +84,7 @@ QByteArray json = material::toJson(scheme); QFile file("theme.json"); file.open(QIODevice::WriteOnly); file.write(json); -```text +``` 导出的格式兼容 Material Theme Builder。 @@ -97,7 +97,7 @@ auto typography = cf::ui::core::material::defaultTypography(); QFont titleFont = typography.queryTargetFont("md.typography.titleLarge"); QFont bodyFont = typography.queryTargetFont("md.typography.bodyMedium"); -```text +``` 默认字体会根据平台自动选择——Windows 用 Segoe UI,macOS 用 .SF NS Text,Linux 用 Ubuntu。 @@ -111,7 +111,7 @@ auto radius = cf::ui::core::material::defaultRadiusScale(); float small = radius.queryRadiusScale("md.shape.cornerSmall"); // 8.0f float medium = radius.queryRadiusScale("md.shape.cornerMedium"); // 12.0f float large = radius.queryRadiusScale("md.shape.cornerLarge"); // 16.0f -```text +``` ## 动画工厂 @@ -122,7 +122,7 @@ auto motion = cf::ui::core::material::motion(); int duration = motion.queryDuration("shortEnter"); // 200 auto spec = motion.getMotionSpec("mediumExit"); -```text +``` ## 完整工作流 @@ -142,7 +142,7 @@ QColor primary = colors.queryExpectedColor("md.primary"); QFont titleFont = typography.queryTargetFont("md.typography.titleLarge"); float cardRadius = radius.queryRadiusScale("md.shape.cornerMedium"); auto enterSpec = motion.getMotionSpec("mediumEnter"); -```text +``` 如果需要完整的 `MaterialTheme` 对象,还是得用 `MaterialFactory` 类。 @@ -168,7 +168,7 @@ void applyCustomTheme(const QColor& brandColor) { updateTypography(&typography); // ... } -```bash +``` 这种方式让应用可以轻松实现"品牌色换肤"功能。 diff --git a/document/HandBook/ui/material/widget/button.md b/document/HandBook/ui/material/widget/button.md index 0da48eb9e..3b6e39234 100644 --- a/document/HandBook/ui/material/widget/button.md +++ b/document/HandBook/ui/material/widget/button.md @@ -19,7 +19,7 @@ enum class ButtonVariant { Text, // 文本按钮 - 最低强调 Elevated, // 浮起按钮 - 带阴影 }; -```text +``` 选择哪种变体取决于按钮在界面中的层级关系。主操作用 Filled,次要操作用 Tonal 或 Outlined,低优先级操作用 Text 或 Elevated。 @@ -41,7 +41,7 @@ auto* button3 = new Button("Low priority", ButtonVariant::Text, this); // 连接信号(与 QPushButton 兼容) connect(button1, &Button::clicked, this, &MyClass::onButtonClick); -```text +``` ## 图标按钮 @@ -54,7 +54,7 @@ button->setLeadingIcon(icon); // 或者使用 setIcon(别名) button->setIcon(icon); -```bash +``` 图标尺寸固定为 18dp,与文本间距 8dp,这是 Material 规范要求的。 @@ -83,7 +83,7 @@ float contentHeight = helper.dpToPx(40.0f); // 固定高度 float hPadding = helper.dpToPx(24.0f); // 水平内边距 float iconWidth = helper.dpToPx(18.0f); // 图标宽度 float iconGap = helper.dpToPx(8.0f); // 图标与文本间距 -```text +``` ⚠️ 按钮的最小宽度不是固定的,而是由内容决定的。如果需要确保触摸目标大小(至少 48x48dp),需要在布局时留出足够的间距。 @@ -117,7 +117,7 @@ void Button::paintEvent(QPaintEvent* event) { // Step 7: 绘制焦点指示器 drawFocusIndicator(p, shape); } -```text +``` 这个顺序很重要——状态层在背景之上、内容之下,水波纹在状态层之上,焦点环在最外层。改变顺序会导致视觉效果不符合 Material 规范。 @@ -131,7 +131,7 @@ button->setElevation(2); // 设置光源角度(默认 15 度,来自左上方) button->setLightSourceAngle(15.0f); -```text +``` 海拔级别影响阴影的模糊半径和偏移量。按钮默认使用 level 2,按压时会临时增加,产生"下沉"的视觉效果。 @@ -142,7 +142,7 @@ button->setLightSourceAngle(15.0f); ```cpp // 禁用按压效果(仅状态层动画保留) button->setPressEffectEnabled(false); -```text +``` 禁用后,按钮的视觉反馈会减弱,但仍然有水波纹和状态层。这在某些自定义场景下有用。 @@ -154,7 +154,7 @@ button->setPressEffectEnabled(false); // Filled: container = PRIMARY, label = ON_PRIMARY // Tonal: container = SECONDARY_CONTAINER, label = ON_SECONDARY_CONTAINER // Outlined/Text/Elevated: container = SURFACE, label = PRIMARY -```text +``` 如果主题不可用,会使用硬编码的 fallback 颜色。这在开发阶段很有用,但生产环境应该总是配置正确的主题。 @@ -164,7 +164,7 @@ button->setPressEffectEnabled(false); ```cpp float cornerRadius = height() / 2.0f; -```text +``` 这在视觉上形成了胶囊形状,是 Material 3 的默认样式。如果需要方角按钮,需要子类化并重写 `cornerRadius()` 方法。 @@ -176,7 +176,7 @@ float cornerRadius = height() / 2.0f; if (!isEnabled()) { color.setAlphaF(0.38f); } -```text +``` 禁用时状态层不显示,交互事件也不会触发状态变化。 @@ -195,7 +195,7 @@ layout->addWidget(new Button("OK", ButtonVariant::Filled, dialog)); auto* cardLayout = new QHBoxLayout(card); cardLayout->addWidget(new Button("Action", ButtonVariant::Outlined, card)); cardLayout->addStretch(); -```text +``` ## 相关文档 diff --git a/document/HandBook/ui/material/widget/checkbox.md b/document/HandBook/ui/material/widget/checkbox.md index a4d876890..a2f42428a 100644 --- a/document/HandBook/ui/material/widget/checkbox.md +++ b/document/HandBook/ui/material/widget/checkbox.md @@ -16,7 +16,7 @@ class CheckBox : public QCheckBox { Q_OBJECT Q_PROPERTY(bool error READ hasError WRITE setError NOTIFY errorChanged) }; -```text +``` 头文件:`ui/widget/material/widget/checkbox/checkbox.h` @@ -41,7 +41,7 @@ cb2->setCheckState(Qt::PartiallyChecked); // 连接信号(与 QCheckBox 兼容) connect(cb2, &QCheckBox::stateChanged, this, &MyClass::onStateChanged); -```text +``` ## 错误状态 @@ -53,7 +53,7 @@ if (!agreedToTerms) { } else { checkBox->setError(false); } -```bash +``` 错误状态下,复选框边框使用 error 颜色,提供明确的视觉反馈。 diff --git a/document/HandBook/ui/material/widget/combobox.md b/document/HandBook/ui/material/widget/combobox.md index 00803821e..9bc174cd2 100644 --- a/document/HandBook/ui/material/widget/combobox.md +++ b/document/HandBook/ui/material/widget/combobox.md @@ -15,7 +15,7 @@ namespace cf::ui::widget::material; class ComboBox : public QComboBox { Q_OBJECT }; -```text +``` 头文件:`ui/widget/material/widget/comboBox/combobox.h` @@ -26,7 +26,7 @@ enum class ComboBoxVariant { Filled, // 填充背景 + 边框 Outlined // 仅描边边框 }; -```text +``` ## 基本用法 @@ -50,7 +50,7 @@ outlined->setVariant(ComboBoxVariant::Outlined); // 连接信号(与 QComboBox 兼容) connect(combo, QOverload::of(&QComboBox::currentIndexChanged), this, &MyClass::onSelectionChanged); -```text +``` ## 下拉箭头动画 @@ -63,7 +63,7 @@ ComboBox 的下拉箭头有旋转动画: // 箭头旋转由 m_arrowRotation 控制 // showPopup() 触发箭头旋转到 180 度 // hidePopup() 触发箭头旋转回 0 度 -```text +``` ## 自定义弹出列表 @@ -79,7 +79,7 @@ ComboBox 的下拉箭头有旋转动画: // hidePopup() 内部: // 1. 箭头旋转回下方 // 2. 关闭弹出容器 -```bash +``` ## 交互状态 diff --git a/document/HandBook/ui/material/widget/doublespinbox.md b/document/HandBook/ui/material/widget/doublespinbox.md index 730163a8e..016376164 100644 --- a/document/HandBook/ui/material/widget/doublespinbox.md +++ b/document/HandBook/ui/material/widget/doublespinbox.md @@ -15,7 +15,7 @@ namespace cf::ui::widget::material; class DoubleSpinBox : public QDoubleSpinBox { Q_OBJECT }; -```text +``` 头文件:`ui/widget/material/widget/doublespinbox/doublespinbox.h` @@ -40,7 +40,7 @@ spin->setSuffix(" mm"); // 连接信号(与 QDoubleSpinBox 兼容) connect(spin, QOverload::of(&QDoubleSpinBox::valueChanged), this, &MyClass::onValueChanged); -```bash +``` ## 与 SpinBox 的区别 @@ -61,7 +61,7 @@ connect(spin, QOverload::of(&QDoubleSpinBox::valueChanged), // 增加按钮(incrementButtonRect) // 减少按钮(decrementButtonRect) // 每个按钮有独立的 hover/pressed 状态追踪 -```bash +``` ## 交互状态 @@ -93,7 +93,7 @@ DoubleSpinBox 的 `paintEvent` 实现 7 步 Material Design 绘制流程: ```cpp // 将内部 lineEdit 限制在文本区域 // 避免输入框覆盖增减按钮区域 -```bash +``` ## 颜色系统 diff --git a/document/HandBook/ui/material/widget/groupbox.md b/document/HandBook/ui/material/widget/groupbox.md index 879a51402..f73ca6752 100644 --- a/document/HandBook/ui/material/widget/groupbox.md +++ b/document/HandBook/ui/material/widget/groupbox.md @@ -18,7 +18,7 @@ class GroupBox : public QGroupBox { Q_PROPERTY(float cornerRadius READ cornerRadius WRITE setCornerRadius) Q_PROPERTY(bool hasBorder READ hasBorder WRITE setHasBorder) }; -```text +``` 头文件:`ui/widget/material/widget/groupbox/groupbox.h` @@ -39,7 +39,7 @@ layout->addWidget(new TextField(TextFieldVariant::Outlined, group)); // 创建不带标题的分组框 auto* group2 = new GroupBox(this); -```text +``` ## 海拔与阴影 @@ -51,7 +51,7 @@ group->setElevation(2); // 海拔级别越高,阴影越明显 group->setElevation(4); -```text +``` 海拔级别影响阴影的模糊半径和偏移量,遵循 Material Design 的标准级别定义。 @@ -66,7 +66,7 @@ group->setCornerRadius(12.0f); // 重置为默认值 group->setCornerRadius(0); -```text +``` ## 边框控制 @@ -74,7 +74,7 @@ group->setCornerRadius(0); // 启用/禁用边框(默认启用) group->setHasBorder(true); // 显示边框 group->setHasBorder(false); // 仅显示阴影(如果 elevation > 0) -```bash +``` 禁用边框时,分组框仅依靠阴影来区分层级,适合卡片式布局。 @@ -122,7 +122,7 @@ addressLayout->addWidget(new TextField("Street", TextFieldVariant::Outlined)); addressLayout->addWidget(new TextField("City", TextFieldVariant::Outlined)); mainLayout->addWidget(addressGroup); -```text +``` ## 相关文档 diff --git a/document/HandBook/ui/material/widget/label.md b/document/HandBook/ui/material/widget/label.md index 8e9fb5e42..cc725b5c5 100644 --- a/document/HandBook/ui/material/widget/label.md +++ b/document/HandBook/ui/material/widget/label.md @@ -18,7 +18,7 @@ class Label : public QLabel { Q_PROPERTY(LabelColorVariant colorVariant READ colorVariant WRITE setColorVariant) Q_PROPERTY(bool autoHiding READ autoHiding WRITE setAutoHiding) }; -```text +``` 头文件:`ui/widget/material/widget/label/label.h` @@ -43,7 +43,7 @@ enum class TypographyStyle { // 标签样式(14sp, 12sp, 11sp)- 用于辅助信息 LabelLarge, LabelMedium, LabelSmall }; -```text +``` ## 颜色变体 @@ -62,7 +62,7 @@ enum class LabelColorVariant { InverseSurface, // 反转表面颜色 InverseOnSurface // 反转表面上的文本 }; -```text +``` ## 基本用法 @@ -82,7 +82,7 @@ title->setColorVariant(LabelColorVariant::Primary); // 创建展示文本 auto* hero = new Label("Welcome", TypographyStyle::DisplayLarge, this); -```bash +``` ## 排版样式选择指南 @@ -105,7 +105,7 @@ label->setAutoHiding(true); // 当 text 为空时,label->hide() 自动调用 // 当 text 非空时,label->show() 自动调用 -```text +``` 这在动态内容的场景下很有用,比如错误提示标签在没有错误时不占用布局空间。 @@ -119,7 +119,7 @@ Label 从当前主题中自动获取颜色和字体: // 字体根据 typographyStyle 从主题获取对应的排版令牌 // 查询通过 typographyTokenName() 转换样式为令牌名称 -```bash +``` 为了优化性能,Label 缓存了最近查询的颜色值(`cachedColor_`),避免重复的主题查询。 diff --git a/document/HandBook/ui/material/widget/listview.md b/document/HandBook/ui/material/widget/listview.md index e9cca9800..1e41eb195 100644 --- a/document/HandBook/ui/material/widget/listview.md +++ b/document/HandBook/ui/material/widget/listview.md @@ -18,7 +18,7 @@ class ListView : public QListView { Q_PROPERTY(bool showSeparator READ showSeparator WRITE setShowSeparator) Q_PROPERTY(bool rippleEnabled READ rippleEnabled WRITE setRippleEnabled) }; -```text +``` 头文件:`ui/widget/material/widget/listview/listview.h` @@ -32,7 +32,7 @@ enum class ItemHeight { TwoLine = 72, // 72dp - 双行项目 ThreeLine = 88 // 88dp - 三行项目 }; -```text +``` ## 基本用法 @@ -59,7 +59,7 @@ list->setModel(model); // 连接信号 connect(list, &QListView::clicked, this, &MyClass::onItemClicked); -```text +``` ## 分隔线 @@ -69,7 +69,7 @@ list->setShowSeparator(true); // 禁用分隔线 list->setShowSeparator(false); -```text +``` 分隔线使用 `OutlineVariant` 颜色绘制,遵循 Material Design 的视觉规范。 @@ -78,7 +78,7 @@ list->setShowSeparator(false); ```cpp // 启用/禁用水波纹效果(默认启用) list->setRippleEnabled(true); -```bash +``` 水波纹在列表项被点击时从点击位置向外扩散,使用 `RippleHelper` 实现。 @@ -103,7 +103,7 @@ ListView 使用内部委托(`ListViewDelegate`)控制列表项的大小: ```cpp // 委托在 .cpp 中定义,自动设置 uniformItemSizes // 根据 ItemHeight 设置每项的高度 -```bash +``` ## 绘制流程 diff --git a/document/HandBook/ui/material/widget/progressbar.md b/document/HandBook/ui/material/widget/progressbar.md index deacdb1a3..147440c07 100644 --- a/document/HandBook/ui/material/widget/progressbar.md +++ b/document/HandBook/ui/material/widget/progressbar.md @@ -15,7 +15,7 @@ namespace cf::ui::widget::material; class ProgressBar : public QProgressBar { Q_OBJECT }; -```text +``` 头文件:`ui/widget/material/widget/progressbar/progressbar.h` @@ -37,7 +37,7 @@ loading->setRange(0, 0); // min=max 表示不确定模式 // 连接信号(与 QProgressBar 兼容) connect(progress, &QProgressBar::valueChanged, this, &MyClass::onProgressChanged); -```bash +``` ## 确定模式 vs 不确定模式 @@ -53,7 +53,7 @@ connect(progress, &QProgressBar::valueChanged, this, &MyClass::onProgressChanged ```cpp progress->setRange(0, 100); progress->setValue(75); // 填充 75% 的宽度 -```text +``` ### 不确定模式 @@ -61,7 +61,7 @@ progress->setValue(75); // 填充 75% 的宽度 ```cpp progress->setRange(0, 0); // 进入不确定模式 -```bash +``` 动画通过 `m_indeterminatePosition`(0.0 到 1.0)控制位置,以循环方式运行。 @@ -111,7 +111,7 @@ ProgressBar 的 `paintEvent` 实现以下绘制步骤: // m_indeterminatePosition 从 0.0 循环到 1.0 // 动画速度由 CFMaterialAnimationFactory 控制 // startIndeterminateAnimation() / stopIndeterminateAnimation() 管理生命周期 -```bash +``` ## 主要方法 diff --git a/document/HandBook/ui/material/widget/radiobutton.md b/document/HandBook/ui/material/widget/radiobutton.md index 5b8d87122..32c19fcea 100644 --- a/document/HandBook/ui/material/widget/radiobutton.md +++ b/document/HandBook/ui/material/widget/radiobutton.md @@ -17,7 +17,7 @@ class RadioButton : public QRadioButton { Q_PROPERTY(bool error READ hasError WRITE setError) Q_PROPERTY(bool pressEffectEnabled READ pressEffectEnabled WRITE setPressEffectEnabled) }; -```text +``` 头文件:`ui/widget/material/widget/radiobutton/radiobutton.h` @@ -44,7 +44,7 @@ option1->setChecked(true); // 连接信号 connect(group, &QButtonGroup::idClicked, this, &MyClass::onOptionSelected); -```text +``` ## 错误状态 @@ -56,7 +56,7 @@ if (!group->checkedButton()) { option2->setError(true); option3->setError(true); } -```text +``` 错误状态下,外环和内部圆使用 error 颜色。 @@ -67,7 +67,7 @@ if (!group->checkedButton()) { ```cpp // 禁用按压效果 radioButton->setPressEffectEnabled(false); -```bash +``` 禁用后,点击时不会触发水波纹动画,但状态层仍然正常工作。 @@ -90,7 +90,7 @@ radioButton->setPressEffectEnabled(false); // setChecked 会同步内部圆的缩放状态 radioButton->setChecked(true); // 内部圆从 0 缩放到目标尺寸 radioButton->setChecked(false); // 内部圆从目标尺寸缩放到 0 -```bash +``` ## 尺寸规范 diff --git a/document/HandBook/ui/material/widget/scrollview.md b/document/HandBook/ui/material/widget/scrollview.md index be0ab69bd..263623d20 100644 --- a/document/HandBook/ui/material/widget/scrollview.md +++ b/document/HandBook/ui/material/widget/scrollview.md @@ -18,7 +18,7 @@ class ScrollView : public QScrollArea { Q_PROPERTY(int scrollbarFadeDelay READ scrollbarFadeDelay WRITE setScrollbarFadeDelay) Q_PROPERTY(bool scrollbarHoverExpansion READ scrollbarHoverExpansion WRITE setScrollbarHoverExpansion) }; -```text +``` 头文件:`ui/widget/material/widget/scrollview/scrollview.h` @@ -32,7 +32,7 @@ enum class ScrollbarState { Hovered, // 悬停 - 100% 透明度,16dp 宽度 Dragged // 拖拽 - 100% 透明度,16dp 宽度,状态层叠加 }; -```text +``` ## 基本用法 @@ -56,7 +56,7 @@ scroll->setScrollbarFadeDelay(500); // 500ms 后淡出 // 启用悬停扩展 scroll->setScrollbarHoverExpansion(true); -```text +``` ## 自定义滚动条 @@ -72,7 +72,7 @@ ScrollView 完全重写了默认滚动条渲染,使用自定义绘制: // ANIMATION_FRAME_MS = 16ms (~60fps) // ANIMATION_SPEED_WIDTH = 0.3f // ANIMATION_SPEED_OPACITY = 0.2f -```text +``` ## 淡入淡出效果 @@ -86,7 +86,7 @@ scroll->setScrollbarFadeDelay(500); // 滚动时自动显示滚动条 // 停止滚动后延迟隐藏 // 鼠标悬停时保持显示 -```text +``` ## 悬停扩展 @@ -97,7 +97,7 @@ scroll->setScrollbarHoverExpansion(true); // 悬停时: // 1. 宽度从 12dp 平滑过渡到 16dp // 2. 透明度从 40% 过渡到 100% -```text +``` ## 滑块拖动 @@ -113,7 +113,7 @@ ScrollView 支持直接拖动滚动条滑块: // isPointOverHorizontalThumb() // isPointOverVerticalTrack() // isPointOverHorizontalTrack() -```text +``` ## 滚动条覆盖层 @@ -123,7 +123,7 @@ ScrollView 使用内部 `ScrollbarOverlay` 小部件在视口上绘制滚动条 // ScrollbarOverlay 是内部实现,定义在 .cpp 中 // 通过 eventFilter 跟踪视口几何变化 // 保持滚动条与视口同步 -```bash +``` ## 绘制流程 diff --git a/document/HandBook/ui/material/widget/separator.md b/document/HandBook/ui/material/widget/separator.md index 910924cfb..dbea5047f 100644 --- a/document/HandBook/ui/material/widget/separator.md +++ b/document/HandBook/ui/material/widget/separator.md @@ -16,7 +16,7 @@ class Separator : public QFrame { Q_OBJECT Q_PROPERTY(SeparatorMode mode READ mode WRITE setMode) }; -```text +``` 头文件:`ui/widget/material/widget/separator/separator.h` @@ -30,7 +30,7 @@ enum class SeparatorMode { Inset, // 两侧各缩进 16dp MiddleInset // 仅在起始侧缩进 16dp }; -```text +``` ## 基本用法 @@ -52,7 +52,7 @@ inset->setMode(SeparatorMode::Inset); // 中内缩模式(起始侧 16dp 边距) auto* middleInset = new Separator(Qt::Horizontal, this); middleInset->setMode(SeparatorMode::MiddleInset); -```text +``` ## 方向控制 @@ -63,7 +63,7 @@ separator->setOrientation(Qt::Vertical); // 垂直分隔线 // 获取当前方向 Qt::Orientation orient = separator->orientation(); -```bash +``` ## 视觉规格 @@ -98,7 +98,7 @@ auto* hLayout = new QHBoxLayout(); hLayout->addWidget(sidebar); hLayout->addWidget(new Separator(Qt::Vertical)); hLayout->addWidget(content); -```bash +``` ## 绘制 diff --git a/document/HandBook/ui/material/widget/slider.md b/document/HandBook/ui/material/widget/slider.md index 17b1bcd73..f0be6cedd 100644 --- a/document/HandBook/ui/material/widget/slider.md +++ b/document/HandBook/ui/material/widget/slider.md @@ -15,7 +15,7 @@ namespace cf::ui::widget::material; class Slider : public QSlider { Q_OBJECT }; -```text +``` 头文件:`ui/widget/material/widget/slider/slider.h` @@ -37,7 +37,7 @@ vSlider->setRange(0, 100); // 连接信号(与 QSlider 兼容) connect(slider, &QSlider::valueChanged, this, &MyClass::onValueChanged); -```text +``` ## 方向 @@ -49,7 +49,7 @@ auto* horizontal = new Slider(Qt::Horizontal, this); // 垂直方向 auto* vertical = new Slider(Qt::Vertical, this); -```text +``` ## 轨道绘制 @@ -61,7 +61,7 @@ auto* vertical = new Slider(Qt::Vertical, this); ```cpp // 轨道高度遵循 Material Design 规范 // 滑块半径遵循 Material Design 规范 -```text +``` ## 滑块与海拔 @@ -78,7 +78,7 @@ Slider 支持刻度标记(Tick Marks)绘制: // 通过 QSlider 的标准属性控制刻度 slider->setTickPosition(QSlider::TicksBelow); slider->setTickInterval(10); -```bash +``` 刻度标记会沿着轨道均匀分布,使用 `inactiveTrackColor` 绘制。 diff --git a/document/HandBook/ui/material/widget/spinbox.md b/document/HandBook/ui/material/widget/spinbox.md index 04c3a2b06..aba9f37fe 100644 --- a/document/HandBook/ui/material/widget/spinbox.md +++ b/document/HandBook/ui/material/widget/spinbox.md @@ -15,7 +15,7 @@ namespace cf::ui::widget::material; class SpinBox : public QSpinBox { Q_OBJECT }; -```text +``` 头文件:`ui/widget/material/widget/spinbox/spinbox.h` @@ -39,7 +39,7 @@ spin->setSuffix(" px"); // 连接信号(与 QSpinBox 兼容) connect(spin, QOverload::of(&QSpinBox::valueChanged), this, &MyClass::onValueChanged); -```text +``` ## 增减按钮 @@ -49,7 +49,7 @@ SpinBox 在控件右侧提供增减按钮: // 增加按钮(incrementButtonRect) // 减少按钮(decrementButtonRect) // 鼠标悬停在按钮上时有独立的 hover 状态 -```bash +``` 每个按钮有独立的悬停和按压状态追踪: @@ -88,7 +88,7 @@ SpinBox 重写了 `resizeEvent` 以约束内部 LineEdit: ```cpp // resizeEvent() 将内部 lineEdit 限制在文本区域 // 避免输入框覆盖增减按钮区域 -```bash +``` ## 颜色系统 diff --git a/document/HandBook/ui/material/widget/state_machine.md b/document/HandBook/ui/material/widget/state_machine.md index 65567ed0c..a736f1956 100644 --- a/document/HandBook/ui/material/widget/state_machine.md +++ b/document/HandBook/ui/material/widget/state_machine.md @@ -21,7 +21,7 @@ enum class State { StateChecked = 0x10, // 选中状态(如 ToggleButton) StateDragged = 0x20, // 拖拽状态 }; -```bash +``` 这些状态可以组合存在(比如同时有焦点和悬停),状态机内部通过位运算处理优先级。 @@ -70,7 +70,7 @@ public: private: base::StateMachine* m_stateMachine; }; -```text +``` ## 事件处理 @@ -112,7 +112,7 @@ void MyWidget::focusOutEvent(QFocusEvent* event) { m_stateMachine->onFocusOut(); update(); } -```text +``` 禁用状态的监听稍微特殊,因为它通过 `changeEvent` 触发: @@ -128,7 +128,7 @@ void MyWidget::changeEvent(QEvent* event) { update(); } } -```text +``` ## 绘制状态层 @@ -153,7 +153,7 @@ void MyWidget::paintEvent(QPaintEvent* event) { // 再绘制其他内容... } -```text +``` ## 选中状态 @@ -167,7 +167,7 @@ void MyWidget::setChecked(bool checked) { update(); } } -```text +``` ⚠️ 选中状态(Checked)只是一种"持久化"的悬停状态,它不应该阻止其他交互状态的叠加。 @@ -185,7 +185,7 @@ auto factory = Application::animationFactory(); if (factory) { factory->setEnabledAll(false); } -```text +``` ## 相关文档 diff --git a/document/HandBook/ui/material/widget/switch.md b/document/HandBook/ui/material/widget/switch.md index 71090fb5c..b2a3cd2f5 100644 --- a/document/HandBook/ui/material/widget/switch.md +++ b/document/HandBook/ui/material/widget/switch.md @@ -15,7 +15,7 @@ namespace cf::ui::widget::material; class Switch : public QCheckBox { Q_OBJECT }; -```text +``` 头文件:`ui/widget/material/widget/switch/switch.h` @@ -35,7 +35,7 @@ wifiSwitch->setChecked(true); // 连接信号(与 QCheckBox 兼容) connect(wifiSwitch, &QCheckBox::toggled, this, &MyClass::onWifiToggled); -```bash +``` ## 开关尺寸 @@ -58,7 +58,7 @@ Switch 遵循 Material Design 3 的尺寸规范: toggle->setChecked(true); // 滑块从左滑到右 toggle->setChecked(false); // 滑块从右滑到左 -```bash +``` 内部使用 `m_inNextCheckState` 标志防止 `setChecked()` 在状态切换时直接跳过动画。 diff --git a/document/HandBook/ui/material/widget/tableview.md b/document/HandBook/ui/material/widget/tableview.md index 9ce65a748..9ab7cfff4 100644 --- a/document/HandBook/ui/material/widget/tableview.md +++ b/document/HandBook/ui/material/widget/tableview.md @@ -20,7 +20,7 @@ class TableView : public QTableView { Q_PROPERTY(bool alternatingRowColors READ alternatingRowColors WRITE setAlternatingRowColors) Q_PROPERTY(bool rippleEnabled READ rippleEnabled WRITE setRippleEnabled) }; -```text +``` 头文件:`ui/widget/material/widget/tableview/tableview.h` @@ -31,7 +31,7 @@ enum class TableRowHeight { Compact, // 48dp - 紧凑模式,适合密集数据 Standard // 56dp - 标准模式(默认) }; -```text +``` ## 网格线样式 @@ -42,7 +42,7 @@ enum class TableGridStyle { Vertical, // 仅垂直线 Both // 水平和垂直线(默认) }; -```text +``` ## 基本用法 @@ -77,7 +77,7 @@ table->setModel(model); // 连接信号 connect(table, &QTableView::clicked, this, &MyClass::onCellClicked); -```text +``` ## 交替行颜色 @@ -87,7 +87,7 @@ table->setAlternatingRowColors(true); // 禁用交替行颜色 table->setAlternatingRowColors(false); -```text +``` 交替行颜色使用 `SurfaceVariant` 的 5% 透明度,提供轻微的视觉区分而不影响阅读。 @@ -99,7 +99,7 @@ table->setAlternatingRowColors(false); // 选中行有 PrimaryContainer 叠加层 // 水波纹效果从点击位置扩散 // 通过 m_hoveredRow 和 m_pressedRow 追踪状态 -```text +``` ## 表头 @@ -108,7 +108,7 @@ table->setAlternatingRowColors(false); ```cpp // 显示/隐藏表头 table->setShowHeader(true); -```bash +``` 表头支持排序指示器和列调整大小的视觉反馈。 diff --git a/document/HandBook/ui/material/widget/tabview.md b/document/HandBook/ui/material/widget/tabview.md index 5e5f3fb44..ba323e45c 100644 --- a/document/HandBook/ui/material/widget/tabview.md +++ b/document/HandBook/ui/material/widget/tabview.md @@ -18,7 +18,7 @@ class TabView : public QTabWidget { Q_PROPERTY(int tabMinWidth READ tabMinWidth WRITE setTabMinWidth) Q_PROPERTY(bool showIndicator READ showIndicator WRITE setShowIndicator) }; -```text +``` 头文件:`ui/widget/material/widget/tabview/tabview.h` @@ -42,7 +42,7 @@ tabs->setTabHeight(48); // 连接信号 connect(tabs, &QTabWidget::currentChanged, this, &MyClass::onTabChanged); -```text +``` ## 标签高度和宽度 @@ -52,7 +52,7 @@ tabs->setTabHeight(48); // 设置标签最小宽度(默认遵循 Material Design 3 规范) tabs->setTabMinWidth(90); -```text +``` ## 选中指示器 @@ -62,7 +62,7 @@ tabs->setShowIndicator(true); // 选中指示器是一个滑动条,在标签之间平滑过渡 // 使用 Primary 颜色绘制 -```text +``` 指示器在标签切换时通过滑动动画移动到新的选中标签位置。 @@ -75,7 +75,7 @@ tabs->setTabCloseable(1, true); // 第二个标签可关闭 // 连接关闭信号 connect(tabs, &TabView::tabCloseRequested, this, &MyClass::onTabClose); -```text +``` 可关闭的标签会显示关闭按钮。 @@ -86,7 +86,7 @@ TabView 使用内部 `MaterialTabBar` 实现标签栏: ```cpp // MaterialTabBar 是内部实现,不暴露给外部 // 负责标签的绘制、选中指示器动画和滚动 -```bash +``` ## 绘制流程 diff --git a/document/HandBook/ui/material/widget/textarea.md b/document/HandBook/ui/material/widget/textarea.md index e2fcdbb1d..795ddaf27 100644 --- a/document/HandBook/ui/material/widget/textarea.md +++ b/document/HandBook/ui/material/widget/textarea.md @@ -24,7 +24,7 @@ class TextArea : public QTextEdit { Q_PROPERTY(int minLines READ minLines WRITE setMinLines) Q_PROPERTY(int maxLines READ maxLines WRITE setMaxLines) }; -```text +``` 头文件:`ui/widget/material/widget/textarea/textarea.h` @@ -35,7 +35,7 @@ enum class TextAreaVariant { Filled, // 填充变体 - 背景填充色,底部指示线 Outlined, // 描边变体 - 圆角边框,无背景填充 }; -```text +``` ## 基本用法 @@ -59,7 +59,7 @@ textarea->setShowCharacterCounter(true); // 连接信号 connect(textarea, &QTextEdit::textChanged, this, &MyClass::onContentChanged); -```text +``` ## 自动调整高度 @@ -74,7 +74,7 @@ textarea->setMaxLines(6); // 当内容增加时,文本框会从 minLines 增长到 maxLines // 超过 maxLines 后,内部会出现滚动条 -```text +``` `keyPressEvent` 会在达到 `maxLines` 限制时阻止 Enter 键产生新行。 @@ -87,7 +87,7 @@ textarea->setLabel("Comments"); // 浮动动画由 CFMaterialAnimationFactory 驱动 // floatingProgress 从 0.0(静止)到 1.0(浮动) -```text +``` ## 帮助文本与错误文本 @@ -97,7 +97,7 @@ textarea->setHelperText("Maximum 500 characters"); // 错误文本优先级高于帮助文本 textarea->setErrorText("Content exceeds maximum length"); textarea->setErrorText(""); // 清除错误 -```bash +``` ## 与 TextField 的区别 diff --git a/document/HandBook/ui/material/widget/textfield.md b/document/HandBook/ui/material/widget/textfield.md index 9b56539fe..227c653cb 100644 --- a/document/HandBook/ui/material/widget/textfield.md +++ b/document/HandBook/ui/material/widget/textfield.md @@ -24,7 +24,7 @@ class TextField : public QLineEdit { Q_PROPERTY(QIcon prefixIcon READ prefixIcon WRITE setPrefixIcon) Q_PROPERTY(QIcon suffixIcon READ suffixIcon WRITE setSuffixIcon) }; -```text +``` 头文件:`ui/widget/material/widget/textfield/textfield.h` @@ -37,7 +37,7 @@ enum class TextFieldVariant { Filled, // 填充变体 - 背景填充色,底部指示线 Outlined, // 描边变体 - 圆角边框,无背景填充 }; -```text +``` 选择哪种变体取决于整体设计风格。Filled 变体适合密集表单,Outlined 变体适合突出显示的场景。 @@ -63,7 +63,7 @@ search->setLabel("Search"); // 连接信号 connect(field1, &QLineEdit::textChanged, this, &MyClass::onTextChanged); -```text +``` ## 浮动标签 @@ -75,7 +75,7 @@ field->setLabel("Password"); // 标签行为: // - 空内容 + 无焦点:标签在输入区域内(占位符位置) // - 有内容或有焦点:标签浮动到输入框上方(小字体) -```text +``` 浮动动画通过 `m_floatingProgress`(0.0 到 1.0)控制,由 `CFMaterialAnimationFactory` 驱动平滑过渡。 @@ -90,7 +90,7 @@ field->setErrorText("Password is too short"); // 清除错误 field->setErrorText(""); // 恢复显示帮助文本 -```text +``` 错误状态下,输入框边框/指示线使用 error 颜色。 @@ -105,7 +105,7 @@ field->setSuffixIcon(QIcon::fromTheme("visibility_off")); // 密码模式(使用 QLineEdit 的内置功能) field->setEchoMode(QLineEdit::Password); -```text +``` ## 字符计数器 @@ -116,7 +116,7 @@ field->setShowCharacterCounter(true); // 显示格式:当前字符数 / 最大长度 // 例如:"42 / 100" -```bash +``` 字符计数器显示在帮助文本区域的右侧。 diff --git a/document/HandBook/ui/material/widget/treeview.md b/document/HandBook/ui/material/widget/treeview.md index 67579bfd6..a235b99db 100644 --- a/document/HandBook/ui/material/widget/treeview.md +++ b/document/HandBook/ui/material/widget/treeview.md @@ -19,7 +19,7 @@ class TreeView : public QTreeView { Q_PROPERTY(bool showTreeLines READ showTreeLines WRITE setShowTreeLines) Q_PROPERTY(bool rootIsDecorated READ rootIsDecorated WRITE setRootIsDecorated) }; -```text +``` 头文件:`ui/widget/material/widget/treeview/treeview.h` @@ -30,7 +30,7 @@ enum class TreeItemHeight { Compact, // 48dp - 紧凑模式 Standard // 56dp - 标准模式(默认) }; -```text +``` ## 缩进样式 @@ -39,7 +39,7 @@ enum class TreeIndentStyle { Material, // 56dp 每级 + 引导线 Classic // 传统嵌套缩进 }; -```text +``` ## 基本用法 @@ -69,7 +69,7 @@ tree->setModel(model); // 连接信号 connect(tree, &QTreeView::clicked, this, &MyClass::onItemClicked); -```text +``` ## 树连接线 @@ -79,7 +79,7 @@ tree->setShowTreeLines(true); // 连接线在父节点和子节点之间绘制 // 使用 OutlineVariant 颜色 -```text +``` ## 展开/折叠 @@ -88,7 +88,7 @@ TreeView 使用 `TreeViewItemDelegate` 处理展开/折叠图标的渲染: ```cpp // drawBranches() 被重写为空实现 // 所有展开/折叠图标由委托渲染,避免与默认 Qt 渲染冲突 -```text +``` ## 根节点装饰 @@ -98,7 +98,7 @@ tree->setRootIsDecorated(true); // 隐藏根节点装饰 tree->setRootIsDecorated(false); -```bash +``` ## 交互状态 diff --git a/document/ci/ci-build-entry.md b/document/ci/ci-build-entry.md index f9120adbb..ef1884bcf 100644 --- a/document/ci/ci-build-entry.md +++ b/document/ci/ci-build-entry.md @@ -27,7 +27,7 @@ scripts/build_helpers/ ├── build_ci_config.ini # AMD64 CI 配置 ├── build_ci_aarch64_config.ini # ARM64 CI 配置 └── build_ci_armhf_config.ini # ARM32 CI 配置 -```text +``` ## CI 构建配置 @@ -49,7 +49,7 @@ build_dir=out/build_ci # CI 专用构建目录 [options] jobs=16 # 并行编译任务数 -```text +``` ### build_ci_aarch64_config.ini (ARM64) @@ -69,7 +69,7 @@ build_dir=out/build_ci_aarch64 # ARM64 专用构建目录 [options] jobs=16 -```text +``` ### build_ci_armhf_config.ini (ARM32) @@ -89,7 +89,7 @@ build_dir=out/build_ci_armhf # ARM32 专用构建目录 [options] jobs=8 -```text +``` ## CI 构建入口脚本 @@ -111,7 +111,7 @@ bash scripts/build_helpers/ci_build_entry.sh ci # 仅运行测试 bash scripts/build_helpers/ci_build_entry.sh test -```text +``` ### 架构检测流程 @@ -131,7 +131,7 @@ ci_build_entry.sh │ └── 执行构建 └──> linux_develop_build.sh ci -c -```bash +``` ## 日志系统 @@ -157,7 +157,7 @@ ci_build_entry.sh ... [2026-03-07 10:35:00] [SUCCESS] CI build completed successfully! [2026-03-07 10:35:00] [INFO] ======================================== -```text +``` ## 集成方式 @@ -177,14 +177,14 @@ docker run --rm --platform linux/arm64 \ -v $(pwd):/project \ cfdesktop-build:arm64 \ bash scripts/build_helpers/ci_build_entry.sh ci -```text +``` ### 直接运行 ```bash # 在 Linux 环境中直接运行 bash scripts/build_helpers/ci_build_entry.sh ci -```text +``` ## 错误处理 @@ -199,7 +199,7 @@ else log "CI build failed with exit code: $exit_code" "ERROR" exit $exit_code fi -```text +``` ## 验证方法 @@ -211,7 +211,7 @@ bash scripts/build_helpers/docker_start.sh --verify # ARM64 bash scripts/build_helpers/docker_start.sh --arch arm64 --verify -```text +``` ### 2. 验证配置 @@ -224,7 +224,7 @@ cat scripts/build_helpers/build_ci_config.ini # generator=Unix Makefiles # toolchain=linux/ci-x86_64 # build_type=Release -```text +``` ### 3. 验证构建目录 @@ -234,7 +234,7 @@ ls -la out/build_ci/ # 与开发构建分离 ls -la out/build_develop/ -```text +``` ## 技术细节 @@ -257,14 +257,14 @@ case "$ARCH" in exit 1 ;; esac -```text +``` ### 脚本位置计算 ```bash SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)" PROJECT_ROOT="$(cd "$SCRIPT_DIR/../.." && pwd)" -```bash +``` 这确保脚本可以从任何位置正确调用,并找到相关文件。 @@ -297,7 +297,7 @@ docker run --rm --platform linux/amd64 ... # ARM64 docker run --rm --platform linux/arm64 ... -```bash +``` ### Q: 构建产物在哪里? diff --git a/document/ci/docker-environment.md b/document/ci/docker-environment.md index dc21eb218..37a3428c8 100644 --- a/document/ci/docker-environment.md +++ b/document/ci/docker-environment.md @@ -30,7 +30,7 @@ scripts/ └── build_helpers/ ├── docker_start.sh # Linux 启动脚本 └── docker_start.ps1 # Windows 启动脚本 -```text +``` ## Docker 镜像 @@ -53,7 +53,7 @@ ARG QT_VERSION=6.8.1 # Qt 版本 ARG QT_ARCH=linux_gcc_64 # Qt 架构 (gcc_64/gcc_arm64) ARG QT_MIRROR= # Qt 镜像源 ARG INSTALL_DEPS=1 # 是否安装依赖 -```text +``` **构建镜像**: ```bash @@ -67,7 +67,7 @@ docker build --platform linux/arm64 \ -f scripts/docker/Dockerfile.build \ --build-arg QT_ARCH=linux_gcc_arm64 \ -t cfdesktop-build:arm64 . -```text +``` ## Docker Compose 配置 @@ -79,7 +79,7 @@ services: build-amd64: # AMD64 构建环境 build-arm64: # ARM64 构建环境 verify: # 快速验证服务 -```text +``` **使用方式**: ```bash @@ -94,7 +94,7 @@ docker-compose -f scripts/docker/docker-compose.yml run build-arm64 # 运行验证 docker-compose -f scripts/docker/docker-compose.yml run verify -```text +``` ## 启动脚本 @@ -117,7 +117,7 @@ docker-compose -f scripts/docker/docker-compose.yml run verify --verify # 运行 CI 构建验证 --no-log # 禁用文件日志 --help # 显示帮助信息 -```text +``` **使用示例**: ```bash @@ -132,7 +132,7 @@ bash scripts/build_helpers/docker_start.sh --verify # ARM64 构建 bash scripts/build_helpers/docker_start.sh --arch arm64 -```bash +``` ### Windows: docker_start.ps1 @@ -177,7 +177,7 @@ MOUNT_PATH="$(pwd | sed 's|^\([A-Za-z]\):|/\1|' | sed 's|\\|/|g')" # Linux / macOS MOUNT_PATH="$PROJECT_ROOT" -```text +``` ## 验证方法 @@ -194,7 +194,7 @@ docker build --platform linux/arm64 \ -f scripts/docker/Dockerfile.build \ --build-arg QT_ARCH=linux_gcc_arm64 \ -t cfdesktop-build:arm64 . -```text +``` ### 2. 验证环境 @@ -208,7 +208,7 @@ docker run --rm --platform linux/amd64 \ which cmake # /usr/bin/cmake which qmake6 # /opt/Qt/6.8.1/gcc_64/bin/qmake6 gcc --version # Ubuntu GCC -```text +``` ### 3. 运行构建 @@ -221,7 +221,7 @@ docker run --rm --platform linux/amd64 \ -v $(pwd):/project \ cfdesktop-build \ bash scripts/build_helpers/ci_build_entry.sh ci -```text +``` ## 日志系统 @@ -239,7 +239,7 @@ docker run --rm --platform linux/amd64 \ ```text 错误: Docker 未安装或未运行 请安装 Docker Desktop: https://www.docker.com/products/docker-desktop -```text +``` ### ARM64 在 x86_64 主机上无法运行 @@ -247,7 +247,7 @@ docker run --rm --platform linux/amd64 \ ```bash # 安装 QEMU docker run --privileged --rm tonistiigi/binfmt --install all -```yaml +``` ### Windows 路径问题 diff --git a/document/ci/toolchain-setup.md b/document/ci/toolchain-setup.md index 84acfb647..08c2bedd1 100644 --- a/document/ci/toolchain-setup.md +++ b/document/ci/toolchain-setup.md @@ -37,7 +37,7 @@ cmake/ └── linux/ # 扩展 ├── ci-x86_64-toolchain.cmake # AMD64 CI 工具链 └── ci-aarch64-toolchain.cmake # ARM64 CI 工具链 -```text +``` ## 工具链文件说明 @@ -50,7 +50,7 @@ cmake/ **使用方式**: ```bash cmake -DUSE_TOOLCHAIN=linux/ci-x86_64 -S . -B build -```text +``` ### ci-aarch64-toolchain.cmake @@ -61,7 +61,7 @@ cmake -DUSE_TOOLCHAIN=linux/ci-x86_64 -S . -B build **使用方式**: ```bash cmake -DUSE_TOOLCHAIN=linux/ci-aarch64 -S . -B build -```text +``` ## 工具链文件内容 @@ -108,7 +108,7 @@ if(CCACHE_PROGRAM) set(CMAKE_C_COMPILER_LAUNCHER "${CCACHE_PROGRAM}" CACHE FILEPATH "C compiler launcher") set(CMAKE_CXX_COMPILER_LAUNCHER "${CCACHE_PROGRAM}" CACHE FILEPATH "CXX compiler launcher") endif() -```text +``` ## ccache 支持 @@ -122,7 +122,7 @@ ccache 是一个编译缓存工具,可以显著加速重复构建: ```bash # 在 Dockerfile 中配置 ccache --max-size=5G --set-config=compiler_check=%compiler%2S -s -```text +``` ## 多架构支持 @@ -138,7 +138,7 @@ ccache --max-size=5G --set-config=compiler_check=%compiler%2S -s │ Qt路径: gcc_64 │ Qt路径: gcc_arm64 │ │ 编译器: x86_64-gcc │ 编译器: aarch64-gcc │ └──────────────────────────┴──────────────────────────────┘ -```text +``` CI 构建脚本会自动检测容器架构并选择对应的工具链。 @@ -158,7 +158,7 @@ file out/build_ci/bin/* # AMD64 预期输出: # ELF 64-bit LSB executable, x86-64, ... -```text +``` ### Docker 容器验证 @@ -170,7 +170,7 @@ bash scripts/build_helpers/docker_start.sh --arch amd64 --mode build # 测试 ARM64 bash scripts/build_helpers/docker_start.sh --arch arm64 --mode build -```bash +``` ## 预期结果 @@ -198,7 +198,7 @@ ls cmake/cmake_toolchain/linux/ci-aarch64-toolchain.cmake # 确认使用正确的简写格式 cmake -DUSE_TOOLCHAIN=linux/ci-x86_64 -S . -B build cmake -DUSE_TOOLCHAIN=linux/ci-aarch64 -S . -B build -```text +``` ### Q: Qt6 not found @@ -211,7 +211,7 @@ find /opt/Qt -name "Qt6Config.cmake" 2>/dev/null # 在容器内安装 Qt6(使用 aqtinstall) python3 -m aqt install-qt --outputdir /opt/Qt 6.8.1 linux desktop gcc_64 -```text +``` ### Q: ccache 未启用 @@ -221,7 +221,7 @@ python3 -m aqt install-qt --outputdir /opt/Qt 6.8.1 linux desktop gcc_64 ```bash # 在 Dockerfile 中添加 RUN apt-get install -y ccache -```text +``` ## 与现有系统集成 @@ -239,7 +239,7 @@ check_toolchain.cmake 解析 查找对应的工具链文件 ↓ 设置: CMAKE_TOOLCHAIN_FILE -```text +``` ### 配置文件示例 @@ -249,7 +249,7 @@ check_toolchain.cmake 解析 generator=Ninja toolchain=linux/ci-x86_64 build_type=Release -```text +``` ```ini # scripts/build_helpers/build_ci_aarch64_config.ini (ARM64) @@ -257,7 +257,7 @@ build_type=Release generator=Ninja toolchain=linux/ci-aarch64 build_type=Release -```yaml +``` ## 后续步骤 diff --git a/document/optimize/pre-release-code-format.md b/document/optimize/pre-release-code-format.md index 05ca22a07..8c2876748 100644 --- a/document/optimize/pre-release-code-format.md +++ b/document/optimize/pre-release-code-format.md @@ -28,7 +28,7 @@ CFDesktop/ ├── example/ # 示例程序 ├── test/ # GoogleTest单元测试 └── scripts/ # 构建和工具脚本 -```bash +``` **技术栈:** Qt6 + C++23 + CMake + GoogleTest diff --git a/document/release_rule/git_hooks_guide.md b/document/release_rule/git_hooks_guide.md index 75909dee8..78dd6d059 100644 --- a/document/release_rule/git_hooks_guide.md +++ b/document/release_rule/git_hooks_guide.md @@ -26,12 +26,12 @@ description: 本项目配置了 Git hooks,在本地进行代码质量检查和 **Linux/macOS:** ```bash bash scripts/release/hooks/install_hooks.sh -```text +``` **Windows (PowerShell):** ```powershell .\scripts\release\hooks\install_hooks.ps1 -```text +``` ### 验证安装 @@ -41,7 +41,7 @@ ls -la .git/hooks/pre-commit .git/hooks/pre-push # Windows dir .git\hooks\pre-commit.* -```bash +``` --- @@ -92,7 +92,7 @@ dir .git\hooks\pre-commit.* **绕过方法**: ```bash git commit --no-verify -m "message" -```bash +``` --- @@ -109,7 +109,7 @@ git commit --no-verify -m "message" **绕过方法**: ```bash git push --no-verify -```bash +``` --- @@ -136,7 +136,7 @@ git push --no-verify 3. 验证失败时回退 $ git reset --hard origin/main -```yaml +``` --- @@ -149,7 +149,7 @@ git push --no-verify ```bash # 等价于执行 docker_start.sh --verify --fast-build --arch amd64 -```bash +``` ### release 分支 @@ -174,7 +174,7 @@ docker_start.sh --verify --fast-build --arch amd64 推送 release/2.0.0 (标签 2.0.0) → Major 变化 → X64 + ARM64 完整构建 -```yaml +``` --- @@ -186,7 +186,7 @@ docker_start.sh --verify --fast-build --arch amd64 ```bash git push --no-verify -```yaml +``` 更好的做法是确保本地代码通过测试后再推送。 @@ -203,7 +203,7 @@ git push --no-verify # Linux sudo systemctl start docker sudo systemctl enable docker -```yaml +``` --- @@ -213,7 +213,7 @@ sudo systemctl enable docker ```text ⚠ clang-format 未安装,跳过格式检查 -```text +``` 不影响提交,但建议安装: @@ -226,7 +226,7 @@ brew install clang-format # Windows # 通过 LLVM 或包管理器安装 -```yaml +``` --- @@ -240,7 +240,7 @@ echo "int x;" >> src/some_file.cpp git add src/some_file.cpp git commit -m "test" # 应该被阻止 -```text +``` 测试 pre-push: @@ -249,7 +249,7 @@ git checkout main # 合并一些更改 git push # 应该触发 Docker 验证 -```yaml +``` --- @@ -260,7 +260,7 @@ git push ```bash git fetch origin git reset --hard origin/main -```yaml +``` **注意**: 这会丢弃本地所有未推送的更改,请确保已备份重要内容。 @@ -278,7 +278,7 @@ git reset --hard origin/main ```bash git tag 1.2.4 git push origin release/1.2 -```yaml +``` --- @@ -302,7 +302,7 @@ rm .git/hooks/pre-commit .git/hooks/pre-push # Windows Remove-Item .git\hooks\pre-commit, .git\hooks\pre-push -```text +``` ### 重新安装 @@ -314,7 +314,7 @@ bash scripts/release/hooks/install_hooks.sh # Windows .\scripts\release\hooks\install_hooks.ps1 -```yaml +``` --- @@ -330,14 +330,14 @@ scripts/release/hooks/ document/release_rule/ └── git_hooks_guide.md # 本文档 -```text +``` 安装后: ```text .git/hooks/ ├── pre-commit # 从 pre-commit.sample 复制 └── pre-push # 从 pre-push.sample 复制 -```yaml +``` --- diff --git a/document/scripts/build_helpers/ci_build_entry.sh.md b/document/scripts/build_helpers/ci_build_entry.sh.md index ef72b3fe5..fa43d447b 100644 --- a/document/scripts/build_helpers/ci_build_entry.sh.md +++ b/document/scripts/build_helpers/ci_build_entry.sh.md @@ -13,7 +13,7 @@ description: "文档编写日期: 2026-03-20,是CI(持续集成)环境下 ```bash ./scripts/build_helpers/ci_build_entry.sh [ci|test] -```bash +``` ## Scripts详解 @@ -41,7 +41,7 @@ description: "文档编写日期: 2026-03-20,是CI(持续集成)环境下 ```bash ./scripts/build_helpers/ci_build_entry.sh ci -```text +``` 该模式会执行: 1. 调用 `linux_develop_build.sh` 进行配置和构建 @@ -54,7 +54,7 @@ description: "文档编写日期: 2026-03-20,是CI(持续集成)环境下 ```bash ./scripts/build_helpers/ci_build_entry.sh test -```text +``` 该模式会调用 `linux_run_tests.sh` 运行已有构建的测试。 @@ -86,7 +86,7 @@ docker run --rm cfdesktop-build bash scripts/build_helpers/ci_build_entry.sh ci # 仅运行测试 docker run --rm cfdesktop-build bash scripts/build_helpers/ci_build_entry.sh test -```text +``` ### 错误处理 @@ -95,7 +95,7 @@ docker run --rm cfdesktop-build bash scripts/build_helpers/ci_build_entry.sh tes ```text ERROR: Unknown architecture: <架构名> Supported: x86_64, aarch64, armv7l -```text +``` ### 注意事项 diff --git a/document/scripts/build_helpers/config_files.md b/document/scripts/build_helpers/config_files.md index 82a3ac49b..418956088 100644 --- a/document/scripts/build_helpers/config_files.md +++ b/document/scripts/build_helpers/config_files.md @@ -104,7 +104,7 @@ build_dir=out/build_develop [options] jobs=12 -```text +``` **特点**:使用 Debug 模式,适合日常开发调试。 @@ -122,7 +122,7 @@ build_dir=out/build_deploy [options] jobs=16 -```text +``` **特点**:使用 Release 模式,最高优化级别,适合生产部署。 @@ -140,7 +140,7 @@ build_dir=out/build_ci [options] jobs=16 -```text +``` **特点**:用于 Docker CI 环境,x86_64 平台标准化构建。 @@ -158,7 +158,7 @@ build_dir=out/build_ci_aarch64 [options] jobs=16 -```text +``` **特点**:在 x86_64 主机上交叉编译 ARM64 程序,使用 `aarch64-linux-gnu-gcc`。 @@ -176,6 +176,6 @@ build_dir=out/build_ci_armhf [options] jobs=16 -```text +``` **特点**:在 x86_64 主机上交叉编译 ARM32 HF 程序,使用 `arm-linux-gnueabihf-gcc`,目标平台包括 IMX6ULL (i.MX 6UltraLite) 等 ARM Cortex-A7 设备。 diff --git a/document/scripts/build_helpers/docker_start.md b/document/scripts/build_helpers/docker_start.md index e348ff80c..78b891268 100644 --- a/document/scripts/build_helpers/docker_start.md +++ b/document/scripts/build_helpers/docker_start.md @@ -12,7 +12,7 @@ description: "文档编写日期: 2026-03-20,本脚本用于在Docker容器中 ### 基本语法 ```powershell .\scripts\build_helpers\docker_start.ps1 [OPTIONS] -```bash +``` ### 参数说明 | 参数 | 类型 | 默认值 | 说明 | @@ -56,7 +56,7 @@ description: "文档编写日期: 2026-03-20,本脚本用于在Docker容器中 # 禁用日志记录 .\scripts\build_helpers\docker_start.ps1 -NoLog -```text +``` ## Scripts详解 @@ -81,14 +81,14 @@ description: "文档编写日期: 2026-03-20,本脚本用于在Docker容器中 启动容器并进入交互式bash shell: ```powershell .\docker_start.ps1 -```text +``` 项目根目录挂载到容器内的 `/project`。 #### 2. CI验证模式 运行完整的CI构建: ```powershell .\docker_start.ps1 -Verify -```text +``` 执行 `scripts/build_helpers/ci_build_entry.sh ci` #### 3. 构建项目模式 @@ -105,7 +105,7 @@ description: "文档编写日期: 2026-03-20,本脚本用于在Docker容器中 运行项目测试: ```powershell .\docker_start.ps1 -RunProjectTest -```bash +``` ### 架构支持 @@ -151,7 +151,7 @@ description: "文档编写日期: 2026-03-20,本脚本用于在Docker容器中 使用 `-StayOnError` 参数,CI构建失败时容器不会退出,可进入交互模式调试: ```powershell .\docker_start.ps1 -Verify -StayOnError -```text +``` ### 快速构建模式 使用 `-FastBuild` 参数复用已有镜像: diff --git a/document/scripts/build_helpers/docker_start.sh.md b/document/scripts/build_helpers/docker_start.sh.md index 780fdd2f8..a6ff6fded 100644 --- a/document/scripts/build_helpers/docker_start.sh.md +++ b/document/scripts/build_helpers/docker_start.sh.md @@ -13,7 +13,7 @@ description: "文档编写日期: 2026-03-20,是CFDesktop项目的Docker构建 ```bash bash scripts/build_helpers/docker_start.sh [options] -```bash +``` ### 参数说明 @@ -55,7 +55,7 @@ bash scripts/build_helpers/docker_start.sh [options] ```bash bash scripts/build_helpers/docker_start.sh -```text +``` 启动一个交互式bash shell,可以手动执行命令。 @@ -63,7 +63,7 @@ bash scripts/build_helpers/docker_start.sh ```bash bash scripts/build_helpers/docker_start.sh --verify -```text +``` 在容器中运行完整的CI构建流程。 @@ -75,13 +75,13 @@ bash scripts/build_helpers/docker_start.sh --build-project # 快速构建 bash scripts/build_helpers/docker_start.sh --build-project-fast -```text +``` #### 测试模式 ```bash bash scripts/build_helpers/docker_start.sh --run-project-test -```text +``` ### 使用示例 @@ -106,7 +106,7 @@ bash scripts/build_helpers/docker_start.sh --no-log # 跳过依赖安装(手动设置) bash scripts/build_helpers/docker_start.sh --no-deps -```text +``` ### 输出示例 @@ -137,7 +137,7 @@ CFDesktop Docker Build Environment [●] RUN - Starting interactive shell → Type 'exit' to leave the container -```bash +``` ### Docker镜像 @@ -163,7 +163,7 @@ CFDesktop Docker Build Environment ```text scripts/docker/logger/ci_build_YYYYMMDD_HHMMSS.log -```text +``` 日志包含: - 开始时间 @@ -177,14 +177,14 @@ scripts/docker/logger/ci_build_YYYYMMDD_HHMMSS.log ```bash bash scripts/build_helpers/docker_start.sh --verify --stay-on-error -```text +``` 失败后会显示: ```text === Build failed, staying in container for debugging === Type "exit" to leave the container -```bash +``` ### 路径处理 @@ -225,11 +225,11 @@ Type "exit" to leave the container # .github/workflows/ci.yml 示例 - name: Build in Docker run: bash scripts/build_helpers/docker_start.sh --verify -```text +``` 或调用测试入口: ```yaml - name: Run Tests run: bash scripts/build_helpers/docker_start.sh --run-project-test -```text +``` diff --git a/document/scripts/build_helpers/linux_configure.sh.md b/document/scripts/build_helpers/linux_configure.sh.md index c453b50cc..67fcc0f65 100644 --- a/document/scripts/build_helpers/linux_configure.sh.md +++ b/document/scripts/build_helpers/linux_configure.sh.md @@ -13,7 +13,7 @@ description: "文档编写日期: 2026-03-20,是专门用于执行CMake配置 ```bash ./scripts/build_helpers/linux_configure.sh [develop|deploy|ci] [-c|--config ] -```bash +``` ### 参数说明 @@ -70,7 +70,7 @@ description: "文档编写日期: 2026-03-20,是专门用于执行CMake配置 # 使用自定义配置文件 ./scripts/build_helpers/linux_configure.sh deploy -c my_config.ini -```text +``` ### 执行流程 @@ -111,7 +111,7 @@ Running CMake configuration... CMake configuration completed successfully! To build the project, run: cmake --build build_develop ======================================== -```bash +``` ### 错误处理 @@ -131,7 +131,7 @@ cmake --build build_develop # 或使用构建脚本 ./scripts/build_helpers/linux_fast_develop_build.sh -```text +``` ### 注意事项 diff --git a/document/scripts/build_helpers/linux_deploy_build.sh.md b/document/scripts/build_helpers/linux_deploy_build.sh.md index b2ba19031..4034d5a2c 100644 --- a/document/scripts/build_helpers/linux_deploy_build.sh.md +++ b/document/scripts/build_helpers/linux_deploy_build.sh.md @@ -13,7 +13,7 @@ description: "文档编写日期: 2026-03-20,是完整的部署构建脚本, ```bash ./scripts/build_helpers/linux_deploy_build.sh [develop|deploy|ci] [-c|--config ] -```bash +``` ### 参数说明 @@ -52,7 +52,7 @@ description: "文档编写日期: 2026-03-20,是完整的部署构建脚本, ```bash Step 1: Cleaning build directory -```text +``` 使用 `lib_build.sh` 中的 `clean_build_dir` 函数清理构建目录。 @@ -60,7 +60,7 @@ Step 1: Cleaning build directory ```bash Step 2: Calling fast build script -```text +``` 调用 `linux_fast_deploy_build.sh` 执行实际的配置和构建。 @@ -68,7 +68,7 @@ Step 2: Calling fast build script ```bash Step 3: Running tests -```text +``` 调用 `linux_run_tests.sh` 运行所有测试。测试失败不会导致脚本退出(仅警告)。 @@ -86,7 +86,7 @@ Step 3: Running tests # 使用自定义配置文件 ./scripts/build_helpers/linux_deploy_build.sh deploy -c my_deploy_config.ini -```text +``` ### 输出示例 @@ -120,7 +120,7 @@ Executing: linux_run_tests.sh deploy ======================================== All tests passed successfully! ======================================== -```text +``` ### 部署配置示例 @@ -138,7 +138,7 @@ build_dir = build_deploy [options] jobs = 4 -```bash +``` ### 使用场景 diff --git a/document/scripts/build_helpers/linux_develop_build.sh.md b/document/scripts/build_helpers/linux_develop_build.sh.md index be46d6871..634cd12bd 100644 --- a/document/scripts/build_helpers/linux_develop_build.sh.md +++ b/document/scripts/build_helpers/linux_develop_build.sh.md @@ -13,7 +13,7 @@ description: "文档编写日期: 2026-03-20,是完整的开发构建脚本, ```bash ./scripts/build_helpers/linux_develop_build.sh [develop|deploy|ci] [-c|--config ] -```bash +``` ### 参数说明 @@ -42,7 +42,7 @@ description: "文档编写日期: 2026-03-20,是完整的开发构建脚本, ```bash Step 1: Cleaning build directory -```text +``` 使用 `lib_build.sh` 中的 `clean_build_dir` 函数清理构建目录。这确保每次都是干净的构建。 @@ -50,7 +50,7 @@ Step 1: Cleaning build directory ```bash Step 2: Calling fast build script -```text +``` 调用 `linux_fast_develop_build.sh` 执行实际的配置和构建。 @@ -58,7 +58,7 @@ Step 2: Calling fast build script ```bash Step 3: Running tests -```text +``` 调用 `linux_run_tests.sh` 运行所有测试。测试失败不会导致脚本退出(仅警告)。 @@ -76,7 +76,7 @@ Step 3: Running tests # 使用自定义配置文件 ./scripts/build_helpers/linux_develop_build.sh develop -c my_config.ini -```text +``` ### 输出示例 @@ -110,7 +110,7 @@ Executing: linux_run_tests.sh develop ======================================== All tests passed successfully! ======================================== -```bash +``` ### 与快速构建的对比 diff --git a/document/scripts/build_helpers/linux_fast_deploy_build.sh.md b/document/scripts/build_helpers/linux_fast_deploy_build.sh.md index 61e533295..eb17f4cc0 100644 --- a/document/scripts/build_helpers/linux_fast_deploy_build.sh.md +++ b/document/scripts/build_helpers/linux_fast_deploy_build.sh.md @@ -13,7 +13,7 @@ description: "文档编写日期: 2026-03-20,是快速部署构建脚本,执 ```bash ./scripts/build_helpers/linux_fast_deploy_build.sh [develop|deploy|ci] [-c|--config ] -```bash +``` ### 参数说明 @@ -52,7 +52,7 @@ description: "文档编写日期: 2026-03-20,是快速部署构建脚本,执 ```bash Step 1: Configuring with CMake -```text +``` 调用 `linux_configure.sh` 执行CMake配置。 @@ -60,7 +60,7 @@ Step 1: Configuring with CMake ```bash Step 2: Building project -```text +``` 使用CMake构建项目。如果配置了并行任务数,会使用 `--parallel` 参数加速编译。 @@ -78,7 +78,7 @@ Step 2: Building project # 使用自定义配置文件 ./scripts/build_helpers/linux_fast_deploy_build.sh deploy -c my_config.ini -```text +``` ### 输出示例 @@ -98,7 +98,7 @@ Step 2: Building project ======================================== Command: cmake --build build_deploy --parallel 4 ... -```text +``` ### 配置参数 @@ -107,7 +107,7 @@ Command: cmake --build build_deploy --parallel 4 ```ini [options] jobs=4 -```text +``` 如果未设置,则不使用并行参数。 @@ -145,4 +145,4 @@ jobs=4 # 需要测试时 ./scripts/build_helpers/linux_run_tests.sh deploy -```text +``` diff --git a/document/scripts/build_helpers/linux_fast_develop_build.sh.md b/document/scripts/build_helpers/linux_fast_develop_build.sh.md index 77d16aa07..d7083f5c1 100644 --- a/document/scripts/build_helpers/linux_fast_develop_build.sh.md +++ b/document/scripts/build_helpers/linux_fast_develop_build.sh.md @@ -13,7 +13,7 @@ description: "文档编写日期: 2026-03-20,是快速开发构建脚本,执 ```bash ./scripts/build_helpers/linux_fast_develop_build.sh [develop|deploy|ci] [-c|--config ] -```bash +``` ### 参数说明 @@ -42,7 +42,7 @@ description: "文档编写日期: 2026-03-20,是快速开发构建脚本,执 ```bash Step 1: Configuring with CMake -```text +``` 调用 `linux_configure.sh` 执行CMake配置。如果构建目录已存在且有有效配置,此步骤会很快完成。 @@ -50,7 +50,7 @@ Step 1: Configuring with CMake ```bash Step 2: Building project -```text +``` 使用CMake构建项目。如果配置了并行任务数,会使用 `--parallel` 参数加速编译。 @@ -68,7 +68,7 @@ Step 2: Building project # 使用自定义配置文件 ./scripts/build_helpers/linux_fast_develop_build.sh develop -c my_config.ini -```text +``` ### 输出示例 @@ -88,7 +88,7 @@ Step 2: Building project ======================================== Command: cmake --build build_develop --parallel 4 ... -```text +``` ### 配置参数 @@ -97,7 +97,7 @@ Command: cmake --build build_develop --parallel 4 ```ini [options] jobs=4 -```bash +``` 如果未设置,则不使用并行参数。 @@ -145,4 +145,4 @@ jobs=4 # 需要测试时 ./scripts/build_helpers/linux_run_tests.sh -```text +``` diff --git a/document/scripts/build_helpers/linux_run_tests.sh.md b/document/scripts/build_helpers/linux_run_tests.sh.md index 6cf6714d7..b948e3b7a 100644 --- a/document/scripts/build_helpers/linux_run_tests.sh.md +++ b/document/scripts/build_helpers/linux_run_tests.sh.md @@ -13,7 +13,7 @@ description: "文档编写日期: 2026-03-20,是测试运行脚本,使用CTe ```bash ./scripts/build_helpers/linux_run_tests.sh [develop|deploy|ci] [-c|--config ] -```bash +``` ### 参数说明 @@ -50,7 +50,7 @@ description: "文档编写日期: 2026-03-20,是测试运行脚本,使用CTe ```text /test/ -```text +``` 例如,如果 `build_dir = build_develop`,则测试目录为 `build_develop/test/`。 @@ -68,7 +68,7 @@ description: "文档编写日期: 2026-03-20,是测试运行脚本,使用CTe # 使用自定义配置文件 ./scripts/build_helpers/linux_run_tests.sh develop -c my_config.ini -```text +``` ### 输出示例 @@ -99,7 +99,7 @@ Total Test time (real) = 0.25 sec ======================================== All tests passed successfully! ======================================== -```text +``` #### 测试失败 @@ -114,7 +114,7 @@ Running Tests (Config: develop) ======================================== Some tests failed with exit code: 8 ======================================== -```bash +``` ### 错误处理 @@ -166,4 +166,4 @@ Some tests failed with exit code: 8 # 或仅运行测试(假设已构建) ./scripts/build_helpers/linux_run_tests.sh ci -```text +``` diff --git a/document/scripts/build_helpers/windows_configure.md b/document/scripts/build_helpers/windows_configure.md index 564a292f9..dfaa11a0c 100644 --- a/document/scripts/build_helpers/windows_configure.md +++ b/document/scripts/build_helpers/windows_configure.md @@ -12,7 +12,7 @@ description: "文档编写日期: 2026-03-20,本脚本仅用于配置项目, ### 基本语法 ```powershell .\scripts\build_helpers\windows_configure.ps1 [-Config ] -```bash +``` ### 参数说明 | 参数 | 类型 | 默认值 | 说明 | @@ -26,7 +26,7 @@ description: "文档编写日期: 2026-03-20,本脚本仅用于配置项目, # 使用部署配置 .\scripts\build_helpers\windows_configure.ps1 -Config deploy -```text +``` ## Scripts详解 @@ -58,7 +58,7 @@ build_type = 构建类型 (Debug/Release/RelWithDebInfo) [paths] source = 源代码目录 (相对于项目根目录) build_dir = 构建输出目录 (相对于项目根目录) -```yaml +``` ### 执行流程 1. 加载配置文件 @@ -86,8 +86,8 @@ build_dir = 构建输出目录 (相对于项目根目录) 配置成功后,可使用以下命令进行编译: ```powershell cmake --build -```yaml +``` 或使用快速构建脚本: ```powershell .\scripts\build_helpers\windows_fast_develop_build.ps1 -```text +``` diff --git a/document/scripts/build_helpers/windows_deploy_build.md b/document/scripts/build_helpers/windows_deploy_build.md index 8c6929840..72526aa97 100644 --- a/document/scripts/build_helpers/windows_deploy_build.md +++ b/document/scripts/build_helpers/windows_deploy_build.md @@ -12,13 +12,13 @@ description: "文档编写日期: 2026-03-20,本脚本执行完整的部署构 ### 基本语法 ```powershell .\scripts\build_helpers\windows_deploy_build.ps1 -```text +``` ### 使用示例 ```powershell # 执行完整的部署构建 (清理 + 配置 + 编译 + 测试) .\scripts\build_helpers\windows_deploy_build.ps1 -```bash +``` ## Scripts详解 diff --git a/document/scripts/build_helpers/windows_develop_build.md b/document/scripts/build_helpers/windows_develop_build.md index 1ad720f6a..76c3a5b00 100644 --- a/document/scripts/build_helpers/windows_develop_build.md +++ b/document/scripts/build_helpers/windows_develop_build.md @@ -12,13 +12,13 @@ description: "文档编写日期: 2026-03-20,本脚本执行完整的开发构 ### 基本语法 ```powershell .\scripts\build_helpers\windows_develop_build.ps1 -```text +``` ### 使用示例 ```powershell # 执行完整的开发构建 (清理 + 配置 + 编译 + 测试) .\scripts\build_helpers\windows_develop_build.ps1 -```bash +``` ## Scripts详解 diff --git a/document/scripts/build_helpers/windows_fast_deploy_build.md b/document/scripts/build_helpers/windows_fast_deploy_build.md index fa255fa92..5f070c70d 100644 --- a/document/scripts/build_helpers/windows_fast_deploy_build.md +++ b/document/scripts/build_helpers/windows_fast_deploy_build.md @@ -12,13 +12,13 @@ description: "文档编写日期: 2026-03-20,本脚本执行快速部署构建 ### 基本语法 ```powershell .\scripts\build_helpers\windows_fast_deploy_build.ps1 -```text +``` ### 使用示例 ```powershell # 执行快速部署构建 (配置 + 编译) .\scripts\build_helpers\windows_fast_deploy_build.ps1 -```text +``` ## Scripts详解 @@ -50,7 +50,7 @@ description: "文档编写日期: 2026-03-20,本脚本执行快速部署构建 从配置文件读取构建目录和并行任务数,然后执行编译: ```bash cmake --build [--parallel ] -```bash +``` ### 构建计时 脚本使用`Start-BuildTimer`和`Stop-BuildTimer`记录编译耗时。 @@ -83,7 +83,7 @@ cmake --build [--parallel ] ```ini [options] jobs = 8 # 并行编译任务数,留空则使用CMake默认值 -```text +``` ### 注意事项 - 本脚本不运行测试,如需测试请使用 `windows_run_tests.ps1 -Config deploy` diff --git a/document/scripts/build_helpers/windows_fast_develop_build.md b/document/scripts/build_helpers/windows_fast_develop_build.md index f448ee15a..4f2e33929 100644 --- a/document/scripts/build_helpers/windows_fast_develop_build.md +++ b/document/scripts/build_helpers/windows_fast_develop_build.md @@ -12,13 +12,13 @@ description: "文档编写日期: 2026-03-20,本脚本执行快速开发构建 ### 基本语法 ```powershell .\scripts\build_helpers\windows_fast_develop_build.ps1 -```text +``` ### 使用示例 ```powershell # 执行快速开发构建 (配置 + 编译) .\scripts\build_helpers\windows_fast_develop_build.ps1 -```text +``` ## Scripts详解 @@ -50,7 +50,7 @@ description: "文档编写日期: 2026-03-20,本脚本执行快速开发构建 从配置文件读取构建目录和并行任务数,然后执行编译: ```bash cmake --build [--parallel ] -```bash +``` ### 构建计时 脚本使用`Start-BuildTimer`和`Stop-BuildTimer`记录编译耗时。 @@ -76,7 +76,7 @@ cmake --build [--parallel ] ```ini [options] jobs = 8 # 并行编译任务数,留空则使用CMake默认值 -```text +``` ### 注意事项 - 本脚本不运行测试,如需测试请使用 `windows_run_tests.ps1` diff --git a/document/scripts/build_helpers/windows_run_tests.md b/document/scripts/build_helpers/windows_run_tests.md index 71c83a987..990af1dcd 100644 --- a/document/scripts/build_helpers/windows_run_tests.md +++ b/document/scripts/build_helpers/windows_run_tests.md @@ -12,7 +12,7 @@ description: "文档编写日期: 2026-03-20,本脚本使用CTest运行项目 ### 基本语法 ```powershell .\scripts\build_helpers\windows_run_tests.ps1 [-Config ] -```bash +``` ### 参数说明 | 参数 | 类型 | 默认值 | 说明 | @@ -26,7 +26,7 @@ description: "文档编写日期: 2026-03-20,本脚本使用CTest运行项目 # 使用部署配置运行测试 .\scripts\build_helpers\windows_run_tests.ps1 -Config deploy -```text +``` ## Scripts详解 @@ -65,7 +65,7 @@ description: "文档编写日期: 2026-03-20,本脚本使用CTest运行项目 #### 4. 执行测试 ```bash ctest --test-dir --output-on-failure -```text +``` ### CTest参数说明 - `--test-dir`: 指定测试目录 diff --git a/document/scripts/dependency/install_build_dependencies.sh.md b/document/scripts/dependency/install_build_dependencies.sh.md index d43df44f3..69d918ca5 100644 --- a/document/scripts/dependency/install_build_dependencies.sh.md +++ b/document/scripts/dependency/install_build_dependencies.sh.md @@ -12,7 +12,7 @@ description: "文档编写日期: 2026-03-20,本脚本用于安装CFDesktop项 ### 基本语法 ```bash ./scripts/dependency/install_build_dependencies.sh -```bash +``` ### 环境变量 | 环境变量 | 默认值 | 说明 | @@ -37,7 +37,7 @@ QT_MIRROR=https://mirrors.tuna.tsinghua.edu.cn/qt ./scripts/dependency/install_b # 指定APT镜像 QT_MIRROR_APT=https://mirrors.ustc.edu.cn/ubuntu ./scripts/dependency/install_build_dependencies.sh -```bash +``` ## Scripts详解 (Detailed Explanation) @@ -107,7 +107,7 @@ QT_MIRROR_APT=https://mirrors.ustc.edu.cn/ubuntu ./scripts/dependency/install_bu export Qt6_DIR=/opt/Qt/6.8.1//lib/cmake/Qt6 export PATH=$Qt6_DIR/bin:$PATH export LD_LIBRARY_PATH=$Qt6_DIR/lib:$LD_LIBRARY_PATH -```text +``` ### 安装目录结构 ```text @@ -122,7 +122,7 @@ export LD_LIBRARY_PATH=$Qt6_DIR/lib:$LD_LIBRARY_PATH ├── bin/ ├── lib/ └── ... -```text +``` ### 注意事项 - 需要 **root 权限** 执行 diff --git a/document/scripts/develop/format_cpp.ps1.md b/document/scripts/develop/format_cpp.ps1.md index f7dd14089..b755bdcac 100644 --- a/document/scripts/develop/format_cpp.ps1.md +++ b/document/scripts/develop/format_cpp.ps1.md @@ -13,7 +13,7 @@ description: "文档编写日期: 2026-03-20,使用 自动格式化项目中 ```powershell .\scripts\develop\format_cpp.ps1 [OPTIONS] -```bash +``` ### 参数说明 @@ -33,7 +33,7 @@ description: "文档编写日期: 2026-03-20,使用 自动格式化项目中 # 预览格式化效果 .\scripts\develop\format_cpp.ps1 -DryRun -```bash +``` ## Scripts详解 (Detailed Explanation) @@ -74,7 +74,7 @@ description: "文档编写日期: 2026-03-20,使用 自动格式化项目中 # 或使用 WSL/Linux 子系统 sudo apt install clang-format -```bash +``` ### 工作模式 @@ -91,7 +91,7 @@ sudo apt install clang-format ```powershell Import-Module LibCommon.psm1 # 提供日志函数 (Write-LogInfo, Write-LogSuccess 等) Import-Module LibPaths.psm1 # 提供路径函数 (Get-ProjectRoot) -```bash +``` ### 输出日志 diff --git a/document/scripts/develop/format_cpp.sh.md b/document/scripts/develop/format_cpp.sh.md index be65bc2f7..f696d81f5 100644 --- a/document/scripts/develop/format_cpp.sh.md +++ b/document/scripts/develop/format_cpp.sh.md @@ -13,7 +13,7 @@ description: "文档编写日期: 2026-03-20,使用 自动格式化项目中 ```bash ./scripts/develop/format_cpp.sh [OPTIONS] -```bash +``` ### 参数说明 @@ -34,7 +34,7 @@ description: "文档编写日期: 2026-03-20,使用 自动格式化项目中 # 预览格式化效果 ./scripts/develop/format_cpp.sh --dry-run -```bash +``` ## Scripts详解 (Detailed Explanation) @@ -77,7 +77,7 @@ sudo pacman -S clang # macOS brew install clang-format -```bash +``` ### 工作模式 diff --git a/document/scripts/develop/remove_trailing_space.ps1.md b/document/scripts/develop/remove_trailing_space.ps1.md index 3ac229680..08e63e600 100644 --- a/document/scripts/develop/remove_trailing_space.ps1.md +++ b/document/scripts/develop/remove_trailing_space.ps1.md @@ -12,7 +12,7 @@ description: "文档编写日期: 2026-03-20,Windows PowerShell版本的行尾 ### 基本语法 ```powershell .\scripts\develop\remove_trailing_space.ps1 [OPTIONS] -```bash +``` ### 参数说明 | 参数 | 说明 | @@ -36,7 +36,7 @@ description: "文档编写日期: 2026-03-20,Windows PowerShell版本的行尾 # 检查暂存文件(pre-commit钩子) .\scripts\develop\remove_trailing_space.ps1 -StagedCheck -```powershell +``` ## Scripts详解 (Detailed Explanation) @@ -83,7 +83,7 @@ src/main.cpp: === Summary === Processed: 150 files Fixed: 2 files -```bash +``` ### 退出码 | 退出码 | 说明 | diff --git a/document/scripts/develop/remove_trailing_space.sh.md b/document/scripts/develop/remove_trailing_space.sh.md index f2fbfca9b..bd03bee5c 100644 --- a/document/scripts/develop/remove_trailing_space.sh.md +++ b/document/scripts/develop/remove_trailing_space.sh.md @@ -12,7 +12,7 @@ description: "文档编写日期: 2026-03-20,删除项目中所有文本文件 ### 基本语法 ```bash ./scripts/develop/remove_trailing_space.sh [OPTIONS] -```bash +``` ### 参数说明 | 参数 | 说明 | @@ -36,7 +36,7 @@ description: "文档编写日期: 2026-03-20,删除项目中所有文本文件 # 检查模式(CI/CD场景) ./scripts/develop/remove_trailing_space.sh --check -```bash +``` ## Scripts详解 (Detailed Explanation) @@ -78,7 +78,7 @@ src/main.cpp: === Summary === Processed: 150 files Fixed: 2 files -```bash +``` ### 退出码 | 退出码 | 说明 | diff --git a/document/scripts/docker/Dockerfile.build.md b/document/scripts/docker/Dockerfile.build.md index d31e734e6..cd925778d 100644 --- a/document/scripts/docker/Dockerfile.build.md +++ b/document/scripts/docker/Dockerfile.build.md @@ -21,7 +21,7 @@ docker buildx build --platform linux/amd64,linux/arm64 \ # ARM64构建 docker build --build-arg QT_ARCH=linux_gcc_arm64 --platform linux/arm64 \ -f scripts/docker/Dockerfile.build -t cfdesktop-build:arm64 . -```text +``` ### 运行容器 ```bash @@ -30,7 +30,7 @@ docker run --rm --platform linux/amd64 -v $(pwd):/project cfdesktop-build # ARM64平台 docker run --rm --platform linux/arm64 -v $(pwd):/project cfdesktop-build -```bash +``` ## Scripts详解 diff --git a/document/scripts/docker/docker-compose.yml.md b/document/scripts/docker/docker-compose.yml.md index bcd4da38d..b646a40ca 100644 --- a/document/scripts/docker/docker-compose.yml.md +++ b/document/scripts/docker/docker-compose.yml.md @@ -12,7 +12,7 @@ description: "文档编写日期: 2026-03-20,所有服务挂载项目根目录 ### 基本语法 ```bash docker-compose -f scripts/docker/docker-compose.yml [COMMAND] -```text +``` ### 常用命令 ```bash @@ -27,7 +27,7 @@ docker-compose -f scripts/docker/docker-compose.yml run build-arm64 # 运行验证服务 docker-compose -f scripts/docker/docker-compose.yml run verify -```bash +``` ## Scripts详解 @@ -62,4 +62,4 @@ docker-compose -f scripts/docker/docker-compose.yml run verify verify服务目前使用`/bin/bash`作为默认命令。在完成Phase 3后,可取消注释以下命令以使用`ci_build_entry.sh`: ```bash command: bash scripts/build_helpers/ci_build_entry.sh ci -```text +``` diff --git a/document/scripts/document/index.md b/document/scripts/document/index.md index e7d00106d..4a3056642 100644 --- a/document/scripts/document/index.md +++ b/document/scripts/document/index.md @@ -14,7 +14,7 @@ pnpm install pnpm dev pnpm build pnpm preview -```bash +``` ## 配置文件 diff --git a/document/scripts/doxygen/lint.py.md b/document/scripts/doxygen/lint.py.md index 6ebe5070d..d53fa41c3 100644 --- a/document/scripts/doxygen/lint.py.md +++ b/document/scripts/doxygen/lint.py.md @@ -12,12 +12,12 @@ description: "文档编写日期: 2026-03-20,- 递归扫描指定目录(默 ### 基本语法 ```bash python3 scripts/doxygen/lint.py -```text +``` ### 指定目录检查 ```bash python3 scripts/doxygen/lint.py /path/to/directory -```text +``` ### 工作模式 - 递归扫描指定目录(默认为项目根目录) @@ -67,7 +67,7 @@ python3 scripts/doxygen/lint.py /path/to/directory #### 成功输出 ```text All Doxygen checks passed -```text +``` - 返回码: `0` - 删除已存在的 `FAILED_DOXYGEN.md` 文件 @@ -75,7 +75,7 @@ All Doxygen checks passed ```text FAILED: N violations See: /path/to/FAILED_DOXYGEN.md -```bash +``` - 返回码: `1` - 生成 `FAILED_DOXYGEN.md` 详细报告,包含: - 违规总数 @@ -100,7 +100,7 @@ See: /path/to/FAILED_DOXYGEN.md ```bash # .git/hooks/pre-commit python3 scripts/doxygen/lint.py || exit 1 -```text +``` #### CMake集成 ```cmake @@ -109,7 +109,7 @@ add_custom_target(doxygen_lint WORKING_DIRECTORY ${CMAKE_SOURCE_DIR} COMMENT "Checking Doxygen compliance..." ) -```text +``` ### 注意事项 - 仅检查 `.h` 和 `.hpp` 头文件 diff --git a/document/scripts/lib/bash/lib_args.sh.md b/document/scripts/lib/bash/lib_args.sh.md index 0942cd3a1..e676ec370 100644 --- a/document/scripts/lib/bash/lib_args.sh.md +++ b/document/scripts/lib/bash/lib_args.sh.md @@ -12,7 +12,7 @@ description: "文档编写日期: 2026-03-20,提供命令行参数解析的辅 ### 加载方式 ```bash source scripts/lib/bash/lib_args.sh -```text +``` ### 基本用法示例 ```bash @@ -36,7 +36,7 @@ while [[ $# -gt 0 ]]; do ;; esac done -```bash +``` ## Scripts详解 @@ -69,7 +69,7 @@ done ```bash parse_config_mode "develop" # 输出: develop, 返回: 0 parse_config_mode "invalid" # 无输出, 返回: 1 -```text +``` #### is_valid_config_mode 检查给定字符串是否为有效的配置模式。 @@ -77,7 +77,7 @@ parse_config_mode "invalid" # 无输出, 返回: 1 if is_valid_config_mode "$1"; then CONFIG="$1" fi -```text +``` #### parse_config_file 解析配置文件参数,仅对 `-c` 或 `--config` 有效。 @@ -85,7 +85,7 @@ fi parse_config_file "-c" "config.ini" # 输出: config.ini, 返回: 0 parse_config_file "--config" "a.conf" # 输出: a.conf, 返回: 0 parse_config_file "--verbose" "on" # 无输出, 返回: 1 -```text +``` #### show_standard_usage 显示一行标准用法信息。 @@ -95,13 +95,13 @@ show_standard_usage show_standard_usage "my_build.sh" # 输出: Usage: my_build.sh [develop|deploy|ci] [-c|--config ] -```text +``` #### show_detailed_usage 显示格式化的详细帮助信息。 ```bash show_detailed_usage "build.sh" "这是一个构建工具脚本" -```text +``` 输出示例: ```yaml @@ -123,7 +123,7 @@ Options: Examples: build.sh develop build.sh deploy -c custom_config.ini -```text +``` #### validate_arg_count 验证参数数量是否满足最小要求。 @@ -132,7 +132,7 @@ if ! validate_arg_count "$#" 2; then echo "错误: 参数不足" exit 1 fi -```text +``` #### is_help_arg 检查参数是否为帮助请求。 @@ -141,7 +141,7 @@ if is_help_arg "$1"; then show_detailed_usage "$(basename "$0")" exit 0 fi -```text +``` 支持的格式:`-h`, `--help`, `help` @@ -152,7 +152,7 @@ show_unknown_arg_error "--invalid" "build.sh" # 输出到 stderr: # ERROR: Unknown argument '--invalid' # Run 'build.sh --help' for usage information. -```text +``` #### show_missing_value_error 显示参数值缺失的错误信息。 @@ -160,7 +160,7 @@ show_unknown_arg_error "--invalid" "build.sh" show_missing_value_error "--config" # 输出到 stderr: # ERROR: Missing value for argument '--config' -```text +``` ### 依赖关系 - Bash 内置命令 diff --git a/document/scripts/lib/bash/lib_build.sh.md b/document/scripts/lib/bash/lib_build.sh.md index 890d59ba6..d1b029671 100644 --- a/document/scripts/lib/bash/lib_build.sh.md +++ b/document/scripts/lib/bash/lib_build.sh.md @@ -12,7 +12,7 @@ description: "文档编写日期: 2026-03-20,注意: 此模块依赖 ,会 ### 加载方式 ```bash source scripts/lib/bash/lib_build.sh -```bash +``` **注意:** 此模块依赖 `lib_common.sh`,会自动加载。 @@ -57,7 +57,7 @@ source scripts/lib/bash/lib_build.sh **示例:** ```bash clean_build_dir "$BUILD_DIR" -```text +``` ### ensure_build_dir(build_path) @@ -69,7 +69,7 @@ clean_build_dir "$BUILD_DIR" **示例:** ```bash ensure_build_dir "$BUILD_DIR" -```text +``` ### run_cmake_configure(generator, build_type, source_dir, build_dir, [extra_args]) @@ -90,7 +90,7 @@ ensure_build_dir "$BUILD_DIR" ```bash run_cmake_configure "Ninja" "Release" "$SOURCE_DIR" "$BUILD_DIR" run_cmake_configure "Unix Makefiles" "Debug" "$SOURCE_DIR" "$BUILD_DIR" "-DENABLE_TESTS=ON" -```text +``` ### run_cmake_build(build_dir, [target], [jobs]) @@ -109,7 +109,7 @@ run_cmake_configure "Unix Makefiles" "Debug" "$SOURCE_DIR" "$BUILD_DIR" "-DENABL ```bash run_cmake_build "$BUILD_DIR" run_cmake_build "$BUILD_DIR" "mytarget" 4 -```text +``` ### has_cmake_cache(build_dir) @@ -127,7 +127,7 @@ run_cmake_build "$BUILD_DIR" "mytarget" 4 if has_cmake_cache "$BUILD_DIR"; then log_info "CMake 已配置,跳过配置步骤" fi -```text +``` ### get_cmake_cache_var(build_dir, var_name) @@ -143,7 +143,7 @@ fi **示例:** ```bash generator=$(get_cmake_cache_var "$BUILD_DIR" "CMAKE_GENERATOR") -```text +``` ### get_parallel_job_count() @@ -156,7 +156,7 @@ generator=$(get_cmake_cache_var "$BUILD_DIR" "CMAKE_GENERATOR") ```bash jobs=$(get_parallel_job_count) run_cmake_build "$BUILD_DIR" "--all" "$jobs" -```text +``` ### build_timer_start() @@ -165,7 +165,7 @@ run_cmake_build "$BUILD_DIR" "--all" "$jobs" **示例:** ```bash build_timer_start -```text +``` ### build_timer_end() @@ -177,7 +177,7 @@ build_timer_start # ... 执行构建 ... build_timer_end # 输出: Build time: 2m 15s -```yaml +``` --- @@ -208,4 +208,4 @@ run_cmake_build "$BUILD_DIR" "--all" "$jobs" # 构建计时结束 build_timer_end -```text +``` diff --git a/document/scripts/lib/bash/lib_common.sh.md b/document/scripts/lib/bash/lib_common.sh.md index c3836628f..e1f4e3f8c 100644 --- a/document/scripts/lib/bash/lib_common.sh.md +++ b/document/scripts/lib/bash/lib_common.sh.md @@ -12,7 +12,7 @@ description: libcommon.sh 的详细文档 ### 加载方式 ```bash source scripts/lib/bash/lib_common.sh -```bash +``` ## Scripts详解 @@ -59,7 +59,7 @@ log "构建开始" "INFO" log "操作成功" "SUCCESS" log "警告信息" "WARNING" log "发生错误" "ERROR" -```text +``` ### log_info(message) @@ -71,7 +71,7 @@ log "发生错误" "ERROR" **示例:** ```bash log_info "正在处理文件..." -```text +``` ### log_success(message) @@ -83,7 +83,7 @@ log_info "正在处理文件..." **示例:** ```bash log_success "构建完成!" -```text +``` ### log_warn(message) @@ -95,7 +95,7 @@ log_success "构建完成!" **示例:** ```bash log_warn "配置文件不存在,使用默认值" -```text +``` ### log_error(message) @@ -107,7 +107,7 @@ log_warn "配置文件不存在,使用默认值" **示例:** ```bash log_error "构建失败,请检查日志" -```text +``` ### log_cyan(message) @@ -119,7 +119,7 @@ log_error "构建失败,请检查日志" **示例:** ```bash log_cyan "这是重要提示" -```text +``` ### log_separator(char, width) @@ -133,7 +133,7 @@ log_cyan "这是重要提示" ```bash log_separator # 输出 40 个 = log_separator "-" 60 # 输出 60 个 - -```text +``` ### log_debug(message) @@ -145,7 +145,7 @@ log_separator "-" 60 # 输出 60 个 - **示例:** ```bash DEBUG=true log_debug "调试信息" -```text +``` ### log_progress(current, total, message) @@ -160,7 +160,7 @@ DEBUG=true log_debug "调试信息" ```bash log_progress 5 10 "处理文件中" # 输出: [5/10] (50%) 处理文件中 -```yaml +``` --- @@ -183,4 +183,4 @@ done log_success "所有文件处理完成!" log_separator -```text +``` diff --git a/document/scripts/lib/bash/lib_config.sh.md b/document/scripts/lib/bash/lib_config.sh.md index 152a1c5c5..b5052b51b 100644 --- a/document/scripts/lib/bash/lib_config.sh.md +++ b/document/scripts/lib/bash/lib_config.sh.md @@ -12,7 +12,7 @@ description: libconfig.sh 的详细文档 ### 加载方式 ```bash source scripts/lib/bash/lib_config.sh -```bash +``` ## Scripts详解 @@ -63,14 +63,14 @@ build_type = Release [paths] build_dir = build output_dir = out -```text +``` 使用方式: ```bash eval "$(get_ini_config config.ini)" echo "$config_cmake_generator" # 输出: Ninja echo "$config_paths_build_dir" # 输出: build -```text +``` ### get_ini_value(filepath, section, key) @@ -88,7 +88,7 @@ echo "$config_paths_build_dir" # 输出: build ```bash value=$(get_ini_value "config.ini" "cmake" "generator") echo "$value" # 输出: Ninja -```text +``` ### has_ini_value(filepath, section, key) @@ -108,7 +108,7 @@ echo "$value" # 输出: Ninja if has_ini_value "config.ini" "cmake" "generator"; then log_info "Generator 已配置" fi -```bash +``` ### get_default_config_file(mode) @@ -131,7 +131,7 @@ fi ```bash config_file=$(get_default_config_file "develop") eval "$(get_ini_config "$config_file")" -```yaml +``` --- @@ -161,7 +161,7 @@ if has_ini_value "$CONFIG_FILE" "cmake" "generator"; then else echo "使用默认生成器" fi -```text +``` ### 条件配置示例 @@ -182,7 +182,7 @@ BUILD_TYPE="$config_cmake_build_type" log_info "配置模式: $MODE" log_info "构建目录: $BUILD_DIR" log_info "构建类型: $BUILD_TYPE" -```text +``` ### INI 配置文件示例 @@ -203,4 +203,4 @@ install_dir = /usr/local parallel = true jobs = 8 verbose = false -```text +``` diff --git a/document/scripts/lib/bash/lib_git.sh.md b/document/scripts/lib/bash/lib_git.sh.md index ab1253b1b..e9ce19072 100644 --- a/document/scripts/lib/bash/lib_git.sh.md +++ b/document/scripts/lib/bash/lib_git.sh.md @@ -12,7 +12,7 @@ description: "文档编写日期: 2026-03-20,提供 Git 相关的辅助函数 ### 加载方式 ```bash source scripts/lib/bash/lib_git.sh -```bash +``` ## Scripts详解 @@ -58,7 +58,7 @@ echo $? # 输出: 1 (第一个版本大于第二个版本) compare_versions "1.0.0" "1.0.0" echo $? # 输出: 0 (版本相等) -```text +``` #### determine_verify_level 根据本地版本和远程版本的差异,确定需要进行的验证级别。 @@ -66,7 +66,7 @@ echo $? # 输出: 0 (版本相等) determine_verify_level "1.2.3" "1.3.0" # 输出: minor determine_verify_level "1.2.3" "2.0.0" # 输出: major determine_verify_level "1.2.3" "1.2.4" # 输出: patch -```text +``` 验证级别含义: - **major**: X64 + ARM64 完整构建 + 测试 @@ -77,7 +77,7 @@ determine_verify_level "1.2.3" "1.2.4" # 输出: patch 从项目根目录的 CMakeLists.txt 中提取版本号。 ```bash get_cmake_version "/path/to/project" # 输出: 1.2.3 -```text +``` ### 依赖关系 - Git 命令行工具(git) diff --git a/document/scripts/lib/bash/lib_paths.sh.md b/document/scripts/lib/bash/lib_paths.sh.md index 67d93bb83..c5e91291f 100644 --- a/document/scripts/lib/bash/lib_paths.sh.md +++ b/document/scripts/lib/bash/lib_paths.sh.md @@ -14,7 +14,7 @@ description: "文档编写日期: 2026-03-20,加载后可使用以下环境变 # 推荐方式:在脚本中先设置 SCRIPT_DIR,然后加载 SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)" source "$SCRIPT_DIR/../lib/bash/lib_paths.sh" -```bash +``` 加载后可使用以下环境变量或调用对应的 getter 函数: - `$PROJECT_ROOT` 或 `get_project_root()` - 项目根目录 @@ -60,7 +60,7 @@ PROJECT_ROOT/ │ └── build_helpers/ │ └── your_script.sh └── ... -```text +``` ### 核心函数详解 @@ -70,13 +70,13 @@ PROJECT_ROOT/ if path_exists "/some/path"; then echo "路径存在" fi -```text +``` #### ensure_dir 确保目录存在,如果不存在则创建(包括父目录)。 ```bash ensure_dir "$PROJECT_ROOT/build/output" -```text +``` ### 依赖关系 - Bash 内置命令 diff --git a/document/scripts/lib/powershell/LibArgs.psm1.md b/document/scripts/lib/powershell/LibArgs.psm1.md index bd51ebf9a..56e3ea10d 100644 --- a/document/scripts/lib/powershell/LibArgs.psm1.md +++ b/document/scripts/lib/powershell/LibArgs.psm1.md @@ -12,7 +12,7 @@ description: "文档编写日期: 2026-03-20,LibArgs.psm1 提供命令行参 ### 加载方式 ```powershell Import-Module scripts/lib/powershell/LibArgs.psm1 -```bash +``` ## Scripts详解 @@ -36,7 +36,7 @@ LibArgs.psm1 提供命令行参数解析和用户帮助信息显示功能。该 #### Parse-ConfigMode ```powershell Parse-ConfigMode [-Mode] -```text +``` 解析并验证配置模式参数。 **有效模式:** @@ -51,12 +51,12 @@ Parse-ConfigMode [-Mode] ```powershell $mode = Parse-ConfigMode "develop" # 返回 "develop" $mode = Parse-ConfigMode "invalid" # 返回 $null -```text +``` #### Show-DetailedUsage ```powershell Show-DetailedUsage [[-ScriptName] ] [[-Description] ] -```text +``` 显示格式化的详细帮助信息,包括: - 脚本名称(带边框) - 脚本描述(如果提供) @@ -88,12 +88,12 @@ Options: Examples: .\build.ps1 develop .\build.ps1 deploy -c custom_config.ini -```text +``` #### Test-HelpArg ```powershell Test-HelpArg [-Arg] -```text +``` 检查参数是否为帮助请求。 **识别的帮助参数:** @@ -106,7 +106,7 @@ if (Test-HelpArg $args[0]) { Show-DetailedUsage exit } -```text +``` ### 标准命令行接口 @@ -114,7 +114,7 @@ if (Test-HelpArg $args[0]) { ```text script.ps1 [develop|deploy|ci] [-c|--config ] [-h|--help] -```cmake +``` **参数说明:** | 位置参数 | 说明 | diff --git a/document/scripts/lib/powershell/LibBuild.psm1.md b/document/scripts/lib/powershell/LibBuild.psm1.md index e2becf750..c3fe22ccd 100644 --- a/document/scripts/lib/powershell/LibBuild.psm1.md +++ b/document/scripts/lib/powershell/LibBuild.psm1.md @@ -15,14 +15,14 @@ description: "文档编写日期: 2026-03-20,描述: 确保构建目录存在 # LibBuild 依赖 LibCommon,需先加载 Import-Module scripts/lib/powershell/LibCommon.psm1 Import-Module scripts/lib/powershell/LibBuild.psm1 -```text +``` 或者: ```powershell . "$PSScriptRoot\LibCommon.psm1" . "$PSScriptRoot\LibBuild.psm1" -```bash +``` ## Scripts详解 @@ -61,7 +61,7 @@ Import-Module scripts/lib/powershell/LibBuild.psm1 **示例**: ```powershell Clean-BuildDir "C:\Build\Output" -```yaml +``` --- @@ -77,7 +77,7 @@ Clean-BuildDir "C:\Build\Output" **示例**: ```powershell Ensure-BuildDir "C:\Build\Output" -```yaml +``` --- @@ -97,12 +97,12 @@ Ensure-BuildDir "C:\Build\Output" **示例**: ```powershell Invoke-CMakeConfigure -Generator "Ninja" -BuildType "Release" -SourceDir "." -BuildDir "build" -```text +``` 带额外参数: ```powershell Invoke-CMakeConfigure -Generator "Ninja" -BuildType "Release" -SourceDir "." -BuildDir "build" -ExtraArgs @("-DCMAKE_EXPORT_COMPILE_COMMANDS=ON") -```yaml +``` --- @@ -124,7 +124,7 @@ Invoke-CMakeBuild -BuildDir "build" # 构建特定目标,指定并行数 Invoke-CMakeBuild -BuildDir "build" -Target "myapp" -Parallel 4 -```yaml +``` --- @@ -142,7 +142,7 @@ Invoke-CMakeBuild -BuildDir "build" -Target "myapp" -Parallel 4 if (Test-CmakeCache "build") { Write-LogInfo "CMake cache exists" } -```yaml +``` --- @@ -160,7 +160,7 @@ if (Test-CmakeCache "build") { ```powershell $generator = Get-CmakeCacheVar -BuildDir "build" -VarName "CMAKE_GENERATOR" Write-LogInfo "Generator: $generator" -```yaml +``` --- @@ -176,7 +176,7 @@ Write-LogInfo "Generator: $generator" ```powershell $jobs = Get-ParallelJobCount Write-LogInfo "Using $jobs parallel jobs" -```yaml +``` --- @@ -193,7 +193,7 @@ Write-LogInfo "Using $jobs parallel jobs" Start-BuildTimer # ... 执行构建 ... Stop-BuildTimer -```yaml +``` --- @@ -211,7 +211,7 @@ Start-BuildTimer # ... 执行构建 ... Stop-BuildTimer # 输出示例: [2026-03-20 10:30:45] [INFO] Build time: 2m 15s -```yaml +``` --- diff --git a/document/scripts/lib/powershell/LibCommon.psm1.md b/document/scripts/lib/powershell/LibCommon.psm1.md index f824b1fa2..1e720b488 100644 --- a/document/scripts/lib/powershell/LibCommon.psm1.md +++ b/document/scripts/lib/powershell/LibCommon.psm1.md @@ -13,13 +13,13 @@ description: "文档编写日期: 2026-03-20,描述: 写入 INFO 级别日志 ```powershell Import-Module scripts/lib/powershell/LibCommon.psm1 -```text +``` 或者: ```powershell . "$PSScriptRoot\LibCommon.psm1" -```bash +``` ## Scripts详解 @@ -50,7 +50,7 @@ Import-Module scripts/lib/powershell/LibCommon.psm1 **示例**: ```powershell Write-Log -Message "Build completed" -Level "SUCCESS" -```yaml +``` --- @@ -67,7 +67,7 @@ Write-Log -Message "Build completed" -Level "SUCCESS" ```powershell Write-LogInfo "Starting build process" Write-LogInfo "Processing" "file" "1.txt" -```yaml +``` --- @@ -83,7 +83,7 @@ Write-LogInfo "Processing" "file" "1.txt" **示例**: ```powershell Write-LogSuccess "Build completed successfully" -```yaml +``` --- @@ -99,7 +99,7 @@ Write-LogSuccess "Build completed successfully" **示例**: ```powershell Write-LogWarning "Configuration file not found, using defaults" -```yaml +``` --- @@ -115,7 +115,7 @@ Write-LogWarning "Configuration file not found, using defaults" **示例**: ```powershell Write-LogError "Build failed with exit code: 1" -```yaml +``` --- @@ -133,7 +133,7 @@ Write-LogError "Build failed with exit code: 1" ```powershell Write-LogSeparator Write-LogSeparator -Char "-" -Width 60 -```yaml +``` --- @@ -152,7 +152,7 @@ Write-LogSeparator -Char "-" -Width 60 ```powershell Write-LogProgress -Current 5 -Total 10 -Message "Processing files" # 输出: [5/10] (50%) Processing files -```bash +``` --- diff --git a/document/scripts/lib/powershell/LibConfig.psm1.md b/document/scripts/lib/powershell/LibConfig.psm1.md index 5eec12d04..0ca04eaf7 100644 --- a/document/scripts/lib/powershell/LibConfig.psm1.md +++ b/document/scripts/lib/powershell/LibConfig.psm1.md @@ -13,13 +13,13 @@ description: "文档编写日期: 2026-03-20,描述: 获取特定的配置值" ```powershell Import-Module scripts/lib/powershell/LibConfig.psm1 -```text +``` 或者: ```powershell . "$PSScriptRoot\LibConfig.psm1" -```bash +``` ## Scripts详解 @@ -61,7 +61,7 @@ Import-Module scripts/lib/powershell/LibConfig.psm1 $config = Get-IniConfig -FilePath "build_config.ini" $buildType = $config["cmake"]["build_type"] Write-LogInfo "Build type: $buildType" -```yaml +``` --- @@ -82,7 +82,7 @@ $generator = Get-IniValue -FilePath "config.ini" -Section "cmake" -Key "generato if (-not [string]::IsNullOrEmpty($generator)) { Write-LogInfo "Generator: $generator" } -```yaml +``` --- @@ -104,7 +104,7 @@ if (Test-IniValue -FilePath "config.ini" -Section "cmake" -Key "generator") { } else { Write-LogWarning "Generator is not defined" } -```bash +``` --- @@ -135,7 +135,7 @@ $configFile = Get-DefaultConfigFile -Mode "deploy" # CI 模式配置,指定脚本目录 $configFile = Get-DefaultConfigFile -Mode "ci" -ScriptDir "C:\Scripts" -```yaml +``` --- @@ -162,4 +162,4 @@ $installDir = Get-IniValue -FilePath $configFile -Section "paths" -Key "install_ if (Test-IniValue -FilePath $configFile -Section "cmake" -Key "generator") { Write-LogSuccess "CMake generator is configured" } -```text +``` diff --git a/document/scripts/lib/powershell/LibGit.psm1.md b/document/scripts/lib/powershell/LibGit.psm1.md index e735233d6..6fa1e0620 100644 --- a/document/scripts/lib/powershell/LibGit.psm1.md +++ b/document/scripts/lib/powershell/LibGit.psm1.md @@ -12,7 +12,7 @@ description: "文档编写日期: 2026-03-20,LibGit.psm1 是一个 Git 辅助 ### 加载方式 ```powershell Import-Module scripts/lib/powershell/LibGit.psm1 -```bash +``` ## Scripts详解 @@ -44,7 +44,7 @@ LibGit.psm1 是一个 Git 辅助函数库,提供版本号解析、Git 仓库 #### Determine-VerifyLevel ```powershell Determine-VerifyLevel [-LocalVersion] [-RemoteVersion] -```text +``` 根据本地版本和远程版本的差异,确定需要执行的验证级别: - **major**: 主版本号不同(1.x.x vs 2.x.x),需要 X64 + ARM64 完整构建 + 测试 @@ -54,7 +54,7 @@ Determine-VerifyLevel [-LocalVersion] [-RemoteVersion] #### Compare-Versions ```powershell Compare-Versions -Version1 -Version2 -```text +``` 比较两个语义化版本号的大小: - 返回 `-1`: Version1 < Version2 @@ -67,7 +67,7 @@ Compare-Versions -Version1 -Version2 ```powershell Get-LocalVersion Get-RemoteVersion -```text +``` - `Get-LocalVersion`: 获取当前分支最近的 Git 标签,若无标签返回 `"0.0.0"` - `Get-RemoteVersion`: 自动执行 `git fetch` 获取最新远程信息,然后返回远程 main 分支的最新标签 diff --git a/document/scripts/lib/powershell/LibPaths.psm1.md b/document/scripts/lib/powershell/LibPaths.psm1.md index 90c048e44..861bd50d1 100644 --- a/document/scripts/lib/powershell/LibPaths.psm1.md +++ b/document/scripts/lib/powershell/LibPaths.psm1.md @@ -12,7 +12,7 @@ description: "文档编写日期: 2026-03-20,LibPaths.psm1 提供路径解析 ### 加载方式 ```powershell Import-Module scripts/lib/powershell/LibPaths.psm1 -```bash +``` ## Scripts详解 @@ -36,7 +36,7 @@ LibPaths.psm1 提供路径解析和目录管理功能。该模块专门设计用 #### Get-ScriptDir ```powershell Get-ScriptDir -```text +``` 获取调用此模块的脚本所在目录的绝对路径。这是模块的核心函数,使用多种回退机制确保在各种执行场景下都能正确获取路径: **检测机制(按优先级):** @@ -56,7 +56,7 @@ Get-ScriptDir #### Get-ProjectRoot ```powershell Get-ProjectRoot -```text +``` 获取项目根目录。假设脚本位于 `scripts/` 的子目录中,函数会向上追溯两级目录。 **目录结构假设:** @@ -66,12 +66,12 @@ ProjectRoot/ lib/ LibPaths.psm1 some-script.ps1 <-- 从这里调用 -```text +``` #### ConvertTo-AbsolutePath ```powershell ConvertTo-AbsolutePath [-Path] [[-BasePath] ] -```text +``` 将相对路径转换为绝对路径。 **参数:** @@ -86,7 +86,7 @@ ConvertTo-AbsolutePath [-Path] [[-BasePath] ] #### New-Directory ```powershell New-Directory [-Path] -```text +``` 确保指定目录存在。如果目录不存在则创建,已存在则不做任何操作。 ### 导出的变量 diff --git a/document/scripts/release/hooks/install_hooks.ps1.md b/document/scripts/release/hooks/install_hooks.ps1.md index 180a25e75..b56b04e28 100644 --- a/document/scripts/release/hooks/install_hooks.ps1.md +++ b/document/scripts/release/hooks/install_hooks.ps1.md @@ -13,7 +13,7 @@ description: "文档编写日期: 2026-03-20,Windows PowerShell版本的Git钩 ```powershell # Windows PowerShell .\scripts\release\hooks\install_hooks.ps1 -```text +``` ## Scripts详解 @@ -23,7 +23,7 @@ Windows PowerShell版本的Git钩子安装脚本,自动安装Git钩子到.git/ ### 依赖模块 ```text scripts\lib\powershell\LibPaths.psm1 -```bash +``` 提供路径解析功能模块。 ### 安装的钩子 @@ -63,12 +63,12 @@ scripts\lib\powershell\LibPaths.psm1 ### 卸载方法 ```powershell Remove-Item .git\hooks\pre-commit, .git\hooks\pre-push -```text +``` ### 验证安装 ```powershell dir .git\hooks\pre-* -```text +``` ### 相关文件 - `/home/charliechen/CFDesktop/scripts/release/hooks/install_hooks.ps1` diff --git a/document/scripts/release/hooks/install_hooks.sh.md b/document/scripts/release/hooks/install_hooks.sh.md index a90f0d4cc..025e2bbe9 100644 --- a/document/scripts/release/hooks/install_hooks.sh.md +++ b/document/scripts/release/hooks/install_hooks.sh.md @@ -16,7 +16,7 @@ bash scripts/release/hooks/install_hooks.sh # Windows PowerShell .\scripts\release\hooks\install_hooks.ps1 -```bash +``` ## Scripts详解 @@ -54,7 +54,7 @@ rm .git/hooks/pre-commit .git/hooks/pre-push # Windows PowerShell Remove-Item .git\hooks\pre-commit, .git\hooks\pre-push -```text +``` ### 验证安装 ```bash @@ -63,7 +63,7 @@ ls -la .git/hooks/pre-commit .git/hooks/pre-push # Windows PowerShell dir .git\hooks\pre-* -```text +``` ### 相关文件 - `/home/charliechen/CFDesktop/scripts/release/hooks/install_hooks.sh` diff --git a/document/scripts/release/hooks/pre-commit.sample.md b/document/scripts/release/hooks/pre-commit.sample.md index 92f143733..6924e9449 100644 --- a/document/scripts/release/hooks/pre-commit.sample.md +++ b/document/scripts/release/hooks/pre-commit.sample.md @@ -15,7 +15,7 @@ description: "文档编写日期: 2026-03-20,运行 installhooks.sh/installhoo ### 绕过方法 ```bash git commit --no-verify -m "message" -```text +``` ## Scripts详解 diff --git a/document/scripts/release/hooks/pre-push.sample.md b/document/scripts/release/hooks/pre-push.sample.md index a936d6da8..1d72ea0d0 100644 --- a/document/scripts/release/hooks/pre-push.sample.md +++ b/document/scripts/release/hooks/pre-push.sample.md @@ -12,7 +12,7 @@ description: "文档编写日期: 2026-03-20,在推送前验证Docker构建, ### 绕过方法 ```bash git push --no-verify -```bash +``` ## Scripts详解 diff --git a/document/scripts/release/hooks/version_utils.sh.md b/document/scripts/release/hooks/version_utils.sh.md index ebf069539..b27b34c6f 100644 --- a/document/scripts/release/hooks/version_utils.sh.md +++ b/document/scripts/release/hooks/version_utils.sh.md @@ -12,7 +12,7 @@ description: "文档编写日期: 2026-03-20,为Git钩子提供版本号解析 ### 加载方式 ```bash source scripts/release/hooks/version_utils.sh -```cpp +``` ## Scripts详解 @@ -62,7 +62,7 @@ source scripts/release/hooks/version_utils.sh #### determine_verify_level ```bash determine_verify_level -```text +``` - **参数1**: 本地版本号(如 `1.2.3`) - **参数2**: 远程版本号(如 `1.1.0`) - **返回**: `major`, `minor`, 或 `patch` @@ -71,7 +71,7 @@ determine_verify_level #### get_cmake_version ```bash get_cmake_version -```text +``` - **参数1**: 项目根目录路径 - **返回**: CMakeLists.txt中的版本号(提取 `VERSION x.y.z` 格式) - **失败**: 返回空字符串 @@ -79,7 +79,7 @@ get_cmake_version #### get_remote_cmake_version ```bash get_remote_cmake_version [remote_branch] -```text +``` - **参数1**: 远程分支名(默认: `origin/main`) - **返回**: 远程CMakeLists.txt中的版本号 - **失败**: 返回空字符串 @@ -113,7 +113,7 @@ echo "$(get_verify_level_description "$LEVEL")" # 获取CMake版本 CMAKE_VER=$(get_cmake_version "/path/to/project") echo "$CMAKE_VER" # 输出: x.y.z -```text +``` ### 相关文件 - `/home/charliechen/CFDesktop/scripts/release/hooks/version_utils.sh` diff --git a/document/todo/base/04_testing.md b/document/todo/base/04_testing.md index f8277067b..182442a0f 100644 --- a/document/todo/base/04_testing.md +++ b/document/todo/base/04_testing.md @@ -52,7 +52,7 @@ description: "预计周期: 贯穿全程,依赖阶段: 所有阶段" / 20% 集成 \ / 10% UI \ /____________________________________\ -```yaml +``` --- diff --git a/document/todo/base/99_ui_material_framework.md b/document/todo/base/99_ui_material_framework.md index b3feb272e..f331dad6a 100644 --- a/document/todo/base/99_ui_material_framework.md +++ b/document/todo/base/99_ui_material_framework.md @@ -30,7 +30,7 @@ Layer 4: Material Behavior Layer (StateMachine, Ripple, ...) Layer 3: Animation Engine Layer (TimingAnimation, SpringAnimation, ...) Layer 2: Theme Engine Layer (ThemeManager, ICFColorScheme, ...) Layer 1: Core Math & Utility Layer (math_helper, color, geometry, ...) -```cpp +``` ### 核心约束(RULE-01 至 RULE-09) - [ ] RULE-01: 所有 Material 控件必须继承 Qt 原生控件 diff --git a/document/todo/desktop/milestone_00_overview.md b/document/todo/desktop/milestone_00_overview.md index 5122bb092..9313f1dce 100644 --- a/document/todo/desktop/milestone_00_overview.md +++ b/document/todo/desktop/milestone_00_overview.md @@ -37,7 +37,7 @@ MS1: 桌面骨架 (壁纸+布局) ✅ 已完成 │ │ └──→ MS5: 窗口管理可见 🚧 追踪联动跑通(装饰待做) │ │ │ └──→ MS6: 小组件+控制中心 (可与 MS3/4/5 并行) ⬜ -```bash +``` **关键路径 (最快通路)**: MS1 → MS2 → MS3 → MS4 → MS5 diff --git a/document/todo/desktop/summary.md b/document/todo/desktop/summary.md index 02d5b5ddb..16f479a2c 100644 --- a/document/todo/desktop/summary.md +++ b/document/todo/desktop/summary.md @@ -85,7 +85,7 @@ CFDesktop 是一个基于 **Qt 6.8.3+ / C++23** 开发的**嵌入式桌面 UI ├── Wayland Client → 跑在现有 Wayland 合成器之上 ├── X11 → 旧版 Linux 桌面兼容 └── Windows (Win32) → 开发调试等价环境 -```yaml +``` > 不自研 Wayland Compositor,优先保证嵌入式 EGLFS 直驱路径的稳定性。 @@ -122,7 +122,7 @@ CFDesktop Shell ├── 文件管理器(File Manager App) ├── 媒体控制服务(Media Control Service) └── 硬件性能自适应引擎(HWTier Adaptive Engine) -```bash +``` ### 3.2 双主题风格系统(核心特色) @@ -164,7 +164,7 @@ ThemeStyleManager ├── 注入 AnimationPolicy(动效策略) ├── 注入 LayoutPolicy(圆角/间距/字体) └── 触发 Shell 重新布局 -```text +``` > 主题包切换是**运行时热切换**,无需重启桌面进程。 @@ -185,7 +185,7 @@ ThemeStyleManager NavigationPolicy Interface ├── iOS Policy → BottomGestureBar + BottomTabBar └── Windows Policy → CenteredTaskbar + SystemTray -```text +``` 公共元素(跨主题): - 顶部状态栏(时间、网络、电量、通知角标) @@ -205,7 +205,7 @@ NotificationService(独立进程) ├── 通知中心面板(下拉/侧滑展开,可清除) ├── 角标计数(状态栏图标角标) └── 勿扰模式(Do Not Disturb) -```bash +``` ### 3.6 快捷控制中心 @@ -307,7 +307,7 @@ NotificationService(独立进程) │ Layer 0: Already Completed │ │ ThemeEngine / AnimationManager / DPI / P0 Controls │ └─────────────────────────────────────────────────────────┘ -```bash +``` **设计原则**: - 每层只依赖下层,严禁跨层调用 @@ -641,7 +641,7 @@ Phase A(基础设施) ├──→ Phase K(文件管理器) └──→ Phase L(媒体服务) └──→ Phase M(SDK + P2 控件) -```bash +``` **可并行开发的模块**: - Phase C(P1 控件)可与 Phase A/B 全程并行