综合平台编程器项目的远程存储
Vous ne pouvez pas sélectionner plus de 25 sujets Les noms de sujets doivent commencer par une lettre ou un nombre, peuvent contenir des tirets ('-') et peuvent comporter jusqu'à 35 caractères.
 
 
 
 

725 lignes
24 KiB

  1. #include "hmi_editor_service.h"
  2. #include "domain/hmi_control_registry.h"
  3. #include "project_service.h"
  4. #include "domain/project_limits.h"
  5. #include <algorithm>
  6. #include <cctype>
  7. #include <cstddef>
  8. #include <map>
  9. #include <set>
  10. #include <utility>
  11. namespace {
  12. void setError(std::string *error, const std::string &message)
  13. {
  14. if (error != nullptr)
  15. {
  16. *error = message;
  17. }
  18. }
  19. HmiEditorResult failure(HmiEditorError error, const std::string &message)
  20. {
  21. return {false, error, message, {}};
  22. }
  23. bool isBlank(const std::string &value)
  24. {
  25. return value.empty()
  26. || std::all_of(
  27. value.cbegin(), value.cend(),
  28. [](unsigned char character) { return std::isspace(character) != 0; });
  29. }
  30. std::string makeUniquePageId(const Project &project)
  31. {
  32. int suffix = 1;
  33. while (true)
  34. {
  35. const std::string candidate = "page-" + std::to_string(suffix);
  36. const bool found = std::any_of(
  37. project.hmiPages.cbegin(), project.hmiPages.cend(),
  38. [&candidate](const HmiPage &page) { return page.id == candidate; });
  39. if (!found)
  40. {
  41. return candidate;
  42. }
  43. ++suffix;
  44. }
  45. }
  46. } // namespace
  47. HmiEditorService::HmiEditorService(ProjectService &project_service)
  48. : project_service_(project_service)
  49. {
  50. }
  51. HmiEditorService::HistoryState HmiEditorService::captureState() const
  52. {
  53. const Project &project = project_service_.project();
  54. return {project.hmiPages, project.initialHmiPageId};
  55. }
  56. void HmiEditorService::recordHistory(HistoryState before)
  57. {
  58. const HistoryState after = captureState();
  59. history_.record(std::move(before), after, &HmiEditorService::statesEqual);
  60. }
  61. bool HmiEditorService::statesEqual(
  62. const HistoryState &left, const HistoryState &right)
  63. {
  64. if (left.initial_page_id != right.initial_page_id
  65. || left.pages.size() != right.pages.size())
  66. {
  67. return false;
  68. }
  69. for (std::size_t index = 0; index < left.pages.size(); ++index)
  70. {
  71. if (!pagesEqual(left.pages[index], right.pages[index]))
  72. {
  73. return false;
  74. }
  75. }
  76. return true;
  77. }
  78. bool HmiEditorService::pagesEqual(
  79. const HmiPage &left, const HmiPage &right)
  80. {
  81. if (left.id != right.id || left.name != right.name
  82. || left.width != right.width || left.height != right.height
  83. || left.controls.size() != right.controls.size())
  84. {
  85. return false;
  86. }
  87. for (std::size_t index = 0; index < left.controls.size(); ++index)
  88. {
  89. if (!controlsEqual(left.controls[index], right.controls[index]))
  90. {
  91. return false;
  92. }
  93. }
  94. return true;
  95. }
  96. bool HmiEditorService::controlsEqual(
  97. const HmiControl &left, const HmiControl &right)
  98. {
  99. const bool progress_equal = left.progressBar.has_value()
  100. == right.progressBar.has_value()
  101. && (!left.progressBar.has_value()
  102. || (left.progressBar->minimumValue == right.progressBar->minimumValue
  103. && left.progressBar->maximumValue == right.progressBar->maximumValue
  104. && left.progressBar->showValue == right.progressBar->showValue));
  105. const bool page_jump_equal = left.pageJump.has_value() == right.pageJump.has_value()
  106. && (!left.pageJump.has_value()
  107. || left.pageJump->targetPageId == right.pageJump->targetPageId);
  108. return left.id == right.id
  109. && left.type == right.type
  110. && left.bounds.x == right.bounds.x
  111. && left.bounds.y == right.bounds.y
  112. && left.bounds.width == right.bounds.width
  113. && left.bounds.height == right.bounds.height
  114. && left.text == right.text
  115. && left.binding == right.binding
  116. && left.properties == right.properties
  117. && left.buttonOperation == right.buttonOperation
  118. && page_jump_equal
  119. && progress_equal;
  120. }
  121. HmiEditorResult HmiEditorService::historyFailure(const std::string &message)
  122. {
  123. return {false, HmiEditorError::InvalidOperation, message, {}};
  124. }
  125. // 遍历工程所有 HmiPage,根据页面 ID 查找页面,找不到返回空指针
  126. const HmiPage *HmiEditorService::findPage(const std::string &page_id) const
  127. {
  128. const Project &project = project_service_.project();
  129. const auto page = std::find_if(
  130. project.hmiPages.cbegin(),
  131. project.hmiPages.cend(),
  132. [&page_id](const HmiPage &candidate)
  133. {
  134. return candidate.id == page_id;
  135. });
  136. return page == project.hmiPages.cend() ? nullptr : &*page;
  137. }
  138. // 先找到页面 → 在页面内查找指定控件;页面不存在 / 控件不存在都返回 nullptr
  139. const HmiControl *HmiEditorService::findControl(
  140. const std::string &page_id, const std::string &control_id) const
  141. {
  142. const HmiPage *page = findPage(page_id);
  143. if (page == nullptr)
  144. {
  145. return nullptr;
  146. }
  147. const auto control = std::find_if(
  148. page->controls.cbegin(),
  149. page->controls.cend(),
  150. [&control_id](const HmiControl &candidate)
  151. {
  152. return candidate.id == control_id;
  153. });
  154. return control == page->controls.cend() ? nullptr : &*control;
  155. }
  156. // 获取第一个页面 ID;没有页面返回空字符串
  157. std::string HmiEditorService::firstPageId() const
  158. {
  159. const Project &project = project_service_.project();
  160. return project.hmiPages.empty() ? std::string{} : project.hmiPages.front().id;
  161. }
  162. // 保证工程至少存在一个 HMI 页面
  163. HmiEditorResult HmiEditorService::ensureDefaultPage()
  164. {
  165. if (!project_service_.project().hmiPages.empty())
  166. {
  167. if (project_service_.project().initialHmiPageId.empty())
  168. {
  169. Project &project = project_service_.editProject();
  170. project.initialHmiPageId = project.hmiPages.front().id;
  171. }
  172. return {true, HmiEditorError::None, {}, firstPageId()};
  173. }
  174. // 仅在首个控件操作前创建默认页面,空工程仍可正常保存
  175. HistoryState before = captureState();
  176. HmiPage page;
  177. page.id = "page-1";
  178. page.name = "主操作页面";
  179. Project &project = project_service_.editProject();
  180. project.hmiPages.push_back(std::move(page));
  181. project.initialHmiPageId = project.hmiPages.back().id;
  182. recordHistory(std::move(before));
  183. return {true, HmiEditorError::None, {}, project.hmiPages.back().id};
  184. }
  185. HmiEditorResult HmiEditorService::addPage(const std::string &name)
  186. {
  187. if (isBlank(name))
  188. {
  189. return failure(HmiEditorError::InvalidPage, "HMI 页面名称不能为空");
  190. }
  191. const Project &current = project_service_.project();
  192. if (current.hmiPages.size() >= ProjectLimits::kMaximumHmiPages)
  193. {
  194. return failure(HmiEditorError::InvalidPage, "单个工程最多包含 128 个 HMI 页面");
  195. }
  196. if (name.size() > ProjectLimits::kMaximumTextBytes)
  197. {
  198. return failure(HmiEditorError::InvalidPage, "HMI 页面名称不能超过 4096 个 UTF-8 字节");
  199. }
  200. const bool duplicate = std::any_of(
  201. current.hmiPages.cbegin(), current.hmiPages.cend(),
  202. [&name](const HmiPage &page) { return page.name == name; });
  203. if (duplicate)
  204. {
  205. return failure(HmiEditorError::DuplicateName, "HMI 页面名称必须唯一");
  206. }
  207. HmiPage page;
  208. page.id = makeUniquePageId(current);
  209. page.name = name;
  210. HistoryState before = captureState();
  211. Project &project = project_service_.editProject();
  212. project.hmiPages.push_back(std::move(page));
  213. if (project.initialHmiPageId.empty())
  214. {
  215. project.initialHmiPageId = project.hmiPages.back().id;
  216. }
  217. recordHistory(std::move(before));
  218. return {true, HmiEditorError::None, {}, project.hmiPages.back().id};
  219. }
  220. HmiEditorResult HmiEditorService::resizePage(
  221. const std::string &page_id, int width, int height)
  222. {
  223. const HmiPage *page = findPage(page_id);
  224. if (page == nullptr)
  225. {
  226. return failure(HmiEditorError::PageNotFound, "未找到 HMI 页面");
  227. }
  228. if (page->width == width && page->height == height)
  229. {
  230. return {true, HmiEditorError::None, {}, page_id};
  231. }
  232. // 先在副本上校验新页面边界,失败时不触碰当前工程
  233. HmiPage candidate = *page;
  234. candidate.width = width;
  235. candidate.height = height;
  236. std::string error;
  237. if (!candidate.validate(&error))
  238. {
  239. return failure(HmiEditorError::InvalidPage, error);
  240. }
  241. HistoryState before = captureState();
  242. Project &project = project_service_.editProject();
  243. auto editable = std::find_if(
  244. project.hmiPages.begin(), project.hmiPages.end(),
  245. [&page_id](const HmiPage &item) { return item.id == page_id; });
  246. editable->width = width;
  247. editable->height = height;
  248. recordHistory(std::move(before));
  249. return {true, HmiEditorError::None, {}, page_id};
  250. }
  251. HmiEditorResult HmiEditorService::renamePage(
  252. const std::string &page_id, const std::string &name)
  253. {
  254. if (isBlank(name))
  255. {
  256. return failure(HmiEditorError::InvalidPage, "HMI 页面名称不能为空");
  257. }
  258. if (name.size() > ProjectLimits::kMaximumTextBytes)
  259. {
  260. return failure(HmiEditorError::InvalidPage, "HMI 页面名称不能超过 4096 个 UTF-8 字节");
  261. }
  262. const Project &current = project_service_.project();
  263. const auto existing = std::find_if(
  264. current.hmiPages.cbegin(), current.hmiPages.cend(),
  265. [&page_id](const HmiPage &page) { return page.id == page_id; });
  266. if (existing == current.hmiPages.cend())
  267. {
  268. return failure(HmiEditorError::PageNotFound, "未找到 HMI 页面");
  269. }
  270. const bool duplicate = std::any_of(
  271. current.hmiPages.cbegin(), current.hmiPages.cend(),
  272. [&page_id, &name](const HmiPage &page)
  273. {
  274. return page.id != page_id && page.name == name;
  275. });
  276. if (duplicate)
  277. {
  278. return failure(HmiEditorError::DuplicateName, "HMI 页面名称必须唯一");
  279. }
  280. if (existing->name == name)
  281. {
  282. return {true, HmiEditorError::None, {}, page_id};
  283. }
  284. HistoryState before = captureState();
  285. Project &project = project_service_.editProject();
  286. auto page = std::find_if(
  287. project.hmiPages.begin(), project.hmiPages.end(),
  288. [&page_id](const HmiPage &candidate) { return candidate.id == page_id; });
  289. page->name = name;
  290. recordHistory(std::move(before));
  291. return {true, HmiEditorError::None, {}, page_id};
  292. }
  293. HmiEditorResult HmiEditorService::removePage(const std::string &page_id)
  294. {
  295. const Project &current = project_service_.project();
  296. const auto page = std::find_if(
  297. current.hmiPages.cbegin(), current.hmiPages.cend(),
  298. [&page_id](const HmiPage &candidate) { return candidate.id == page_id; });
  299. if (page == current.hmiPages.cend())
  300. {
  301. return failure(HmiEditorError::PageNotFound, "未找到 HMI 页面");
  302. }
  303. if (current.hmiPages.size() <= 1)
  304. {
  305. return failure(HmiEditorError::LastPageRequired, "工程至少需要保留一个 HMI 页面");
  306. }
  307. if (current.initialHmiPageId == page_id)
  308. {
  309. return failure(
  310. HmiEditorError::InitialPageCannotBeRemoved,
  311. "请先设置其他初始页面,再删除当前页面");
  312. }
  313. for (const HmiPage &source_page : current.hmiPages)
  314. {
  315. const auto reference = std::find_if(
  316. source_page.controls.cbegin(), source_page.controls.cend(),
  317. [&page_id](const HmiControl &control)
  318. {
  319. return control.type == HmiControlType::PageJump
  320. && control.pageJump.has_value()
  321. && control.pageJump->targetPageId == page_id;
  322. });
  323. if (reference != source_page.controls.cend())
  324. {
  325. return failure(
  326. HmiEditorError::PageReferenced,
  327. "页面仍被跳转控件 " + reference->id + " 引用");
  328. }
  329. }
  330. HistoryState before = captureState();
  331. Project &project = project_service_.editProject();
  332. project.hmiPages.erase(
  333. std::remove_if(
  334. project.hmiPages.begin(), project.hmiPages.end(),
  335. [&page_id](const HmiPage &candidate) { return candidate.id == page_id; }),
  336. project.hmiPages.end());
  337. recordHistory(std::move(before));
  338. return {true, HmiEditorError::None, {}, page_id};
  339. }
  340. HmiEditorResult HmiEditorService::movePage(const std::string &page_id, int offset)
  341. {
  342. if (offset != -1 && offset != 1)
  343. {
  344. return failure(HmiEditorError::InvalidOperation, "页面每次只能上移或下移一位");
  345. }
  346. const Project &current = project_service_.project();
  347. const auto page = std::find_if(
  348. current.hmiPages.cbegin(), current.hmiPages.cend(),
  349. [&page_id](const HmiPage &candidate) { return candidate.id == page_id; });
  350. if (page == current.hmiPages.cend())
  351. {
  352. return failure(HmiEditorError::PageNotFound, "未找到 HMI 页面");
  353. }
  354. const auto index = std::distance(current.hmiPages.cbegin(), page);
  355. const std::ptrdiff_t target_index = index + offset;
  356. if (target_index < 0
  357. || target_index >= static_cast<std::ptrdiff_t>(current.hmiPages.size()))
  358. {
  359. return failure(HmiEditorError::InvalidOperation, "HMI 页面已经位于目标边界");
  360. }
  361. HistoryState before = captureState();
  362. Project &project = project_service_.editProject();
  363. std::iter_swap(
  364. project.hmiPages.begin() + index,
  365. project.hmiPages.begin() + target_index);
  366. recordHistory(std::move(before));
  367. return {true, HmiEditorError::None, {}, page_id};
  368. }
  369. HmiEditorResult HmiEditorService::setInitialPage(const std::string &page_id)
  370. {
  371. if (findPage(page_id) == nullptr)
  372. {
  373. return failure(HmiEditorError::PageNotFound, "未找到 HMI 页面");
  374. }
  375. if (project_service_.project().initialHmiPageId == page_id)
  376. {
  377. return {true, HmiEditorError::None, {}, page_id};
  378. }
  379. HistoryState before = captureState();
  380. project_service_.editProject().initialHmiPageId = page_id;
  381. recordHistory(std::move(before));
  382. return {true, HmiEditorError::None, {}, page_id};
  383. }
  384. HmiEditorResult HmiEditorService::addControl(
  385. const std::string &page_id, HmiControlType type)
  386. {
  387. const HmiPage *page = findPage(page_id);
  388. if (page == nullptr)
  389. {
  390. return failure(HmiEditorError::PageNotFound, "未找到 HMI 页面");
  391. }
  392. if (page->controls.size() >= ProjectLimits::kMaximumHmiControlsPerPage)
  393. {
  394. return failure(HmiEditorError::InvalidControl, "单个 HMI 页面最多包含 512 个控件");
  395. }
  396. const HmiControlDescriptor *descriptor = findHmiControlDescriptor(type);
  397. if (descriptor == nullptr)
  398. {
  399. return failure(HmiEditorError::InvalidControl, "不支持的 HMI 控件类型");
  400. }
  401. // 新控件初始不绑定寄存器,避免自动分配地址造成误写风险
  402. HmiControl control = makeControl(*page, *descriptor);
  403. HistoryState before = captureState();
  404. Project &project = project_service_.editProject();
  405. auto target_page = std::find_if(
  406. project.hmiPages.begin(),
  407. project.hmiPages.end(),
  408. [&page_id](const HmiPage &candidate)
  409. {
  410. return candidate.id == page_id;
  411. });
  412. target_page->controls.push_back(std::move(control));
  413. recordHistory(std::move(before));
  414. return {true,
  415. HmiEditorError::None,
  416. {},
  417. target_page->controls.back().id};
  418. }
  419. HmiEditorResult HmiEditorService::removeControl(
  420. const std::string &page_id, const std::string &control_id)
  421. {
  422. return removeControls(page_id, {control_id});
  423. }
  424. HmiEditorResult HmiEditorService::removeControls(
  425. const std::string &page_id,
  426. const std::vector<std::string> &control_ids)
  427. {
  428. if (findPage(page_id) == nullptr)
  429. {
  430. return failure(HmiEditorError::PageNotFound, "未找到 HMI 页面");
  431. }
  432. if (control_ids.empty())
  433. {
  434. return failure(HmiEditorError::InvalidOperation, "请先选择要删除的 HMI 控件");
  435. }
  436. std::set<std::string> selected_ids;
  437. for (const std::string &control_id : control_ids)
  438. {
  439. if (!selected_ids.insert(control_id).second)
  440. {
  441. return failure(HmiEditorError::InvalidOperation, "删除列表中存在重复控件");
  442. }
  443. if (findControl(page_id, control_id) == nullptr)
  444. {
  445. return failure(HmiEditorError::ControlNotFound, "未找到 HMI 控件");
  446. }
  447. }
  448. HistoryState before = captureState();
  449. Project &project = project_service_.editProject();
  450. auto page = std::find_if(
  451. project.hmiPages.begin(),
  452. project.hmiPages.end(),
  453. [&page_id](const HmiPage &candidate)
  454. {
  455. return candidate.id == page_id;
  456. });
  457. page->controls.erase(
  458. std::remove_if(
  459. page->controls.begin(),
  460. page->controls.end(),
  461. [&selected_ids](const HmiControl &candidate)
  462. {
  463. return selected_ids.find(candidate.id) != selected_ids.end();
  464. }),
  465. page->controls.end());
  466. recordHistory(std::move(before));
  467. return {true, HmiEditorError::None, {}, control_ids.front()};
  468. }
  469. // 移动 / 缩放控件
  470. HmiEditorResult HmiEditorService::moveControl(
  471. const std::string &page_id,
  472. const std::string &control_id,
  473. const HmiRect &bounds)
  474. {
  475. const HmiPage *page = findPage(page_id);
  476. const HmiControl *control = findControl(page_id, control_id);
  477. if (page == nullptr)
  478. {
  479. return failure(HmiEditorError::PageNotFound, "未找到 HMI 页面");
  480. }
  481. if (control == nullptr)
  482. {
  483. return failure(HmiEditorError::ControlNotFound, "未找到 HMI 控件");
  484. }
  485. // 先校验候选位置,失败时不修改工程模型
  486. HmiControl candidate = *control;
  487. candidate.bounds = bounds;
  488. // 更新前保留原控件,只有全部编辑规则通过才覆盖原值
  489. std::string error;
  490. if (!validateEditableControl(*page, candidate, &error))
  491. {
  492. return failure(HmiEditorError::InvalidControl, error);
  493. }
  494. if (control->bounds.x == bounds.x
  495. && control->bounds.y == bounds.y
  496. && control->bounds.width == bounds.width
  497. && control->bounds.height == bounds.height)
  498. {
  499. return {true, HmiEditorError::None, {}, control_id};
  500. }
  501. HistoryState before = captureState();
  502. Project &project = project_service_.editProject();
  503. auto target = std::find_if(
  504. project.hmiPages.begin(), project.hmiPages.end(),
  505. [&page_id](const HmiPage &item) { return item.id == page_id; });
  506. auto editable = std::find_if(
  507. target->controls.begin(), target->controls.end(),
  508. [&control_id](const HmiControl &item) { return item.id == control_id; });
  509. editable->bounds = bounds;
  510. recordHistory(std::move(before));
  511. return {true, HmiEditorError::None, {}, control_id};
  512. }
  513. // 更新控件全部属性
  514. HmiEditorResult HmiEditorService::updateControl(
  515. const std::string &page_id,
  516. const std::string &control_id,
  517. const HmiControl &control)
  518. {
  519. const HmiPage *page = findPage(page_id);
  520. const HmiControl *existing = findControl(page_id, control_id);
  521. if (page == nullptr)
  522. {
  523. return failure(HmiEditorError::PageNotFound, "未找到 HMI 页面");
  524. }
  525. if (existing == nullptr)
  526. {
  527. return failure(HmiEditorError::ControlNotFound, "未找到 HMI 控件");
  528. }
  529. if (control.type != existing->type)
  530. {
  531. return failure(HmiEditorError::InvalidControl, "不能修改已创建 HMI 控件的类型");
  532. }
  533. std::string error;
  534. if (!validateEditableControl(*page, control, &error))
  535. {
  536. return failure(HmiEditorError::InvalidControl, error);
  537. }
  538. if (hasDuplicateControlId(*page, control_id, control.id))
  539. {
  540. return failure(HmiEditorError::DuplicateId, "同一 HMI 页面内的控件 ID 必须唯一");
  541. }
  542. if (controlsEqual(*existing, control))
  543. {
  544. return {true, HmiEditorError::None, {}, control.id};
  545. }
  546. HistoryState before = captureState();
  547. Project &project = project_service_.editProject();
  548. auto target = std::find_if(
  549. project.hmiPages.begin(), project.hmiPages.end(),
  550. [&page_id](const HmiPage &item) { return item.id == page_id; });
  551. auto editable = std::find_if(
  552. target->controls.begin(), target->controls.end(),
  553. [&control_id](const HmiControl &item) { return item.id == control_id; });
  554. *editable = control;
  555. recordHistory(std::move(before));
  556. return {true, HmiEditorError::None, {}, control.id};
  557. }
  558. bool HmiEditorService::canUndo() const
  559. {
  560. return history_.canUndo();
  561. }
  562. bool HmiEditorService::canRedo() const
  563. {
  564. return history_.canRedo();
  565. }
  566. HmiEditorResult HmiEditorService::undo()
  567. {
  568. const std::optional<HistoryState> target = history_.undo(captureState());
  569. if (!target.has_value())
  570. {
  571. return historyFailure("没有可撤销的 HMI 编辑操作");
  572. }
  573. Project &project = project_service_.editProject();
  574. project.hmiPages = target->pages;
  575. project.initialHmiPageId = target->initial_page_id;
  576. return {true, HmiEditorError::None, {}, {}};
  577. }
  578. HmiEditorResult HmiEditorService::redo()
  579. {
  580. const std::optional<HistoryState> target = history_.redo(captureState());
  581. if (!target.has_value())
  582. {
  583. return historyFailure("没有可重做的 HMI 编辑操作");
  584. }
  585. Project &project = project_service_.editProject();
  586. project.hmiPages = target->pages;
  587. project.initialHmiPageId = target->initial_page_id;
  588. return {true, HmiEditorError::None, {}, {}};
  589. }
  590. void HmiEditorService::clearHistory()
  591. {
  592. history_.clear();
  593. }
  594. bool HmiEditorService::validateEditableControl(
  595. const HmiPage &page, const HmiControl &control, std::string *error) const
  596. {
  597. if (!control.validate(error))
  598. {
  599. return false;
  600. }
  601. if (control.bounds.x < 0 || control.bounds.y < 0
  602. || control.bounds.width > page.width || control.bounds.height > page.height
  603. || control.bounds.x > page.width - control.bounds.width
  604. || control.bounds.y > page.height - control.bounds.height)
  605. {
  606. setError(error, "HMI 控件不能超出页面范围");
  607. return false;
  608. }
  609. if (control.type == HmiControlType::PageJump
  610. && !control.pageJump->targetPageId.empty()
  611. && findPage(control.pageJump->targetPageId) == nullptr)
  612. {
  613. setError(error, "页面跳转控件的目标页面不存在");
  614. return false;
  615. }
  616. return true;
  617. }
  618. bool HmiEditorService::hasDuplicateControlId(
  619. const HmiPage &page,
  620. const std::string &excluded_id,
  621. const std::string &candidate_id)
  622. {
  623. return std::any_of(
  624. page.controls.cbegin(), page.controls.cend(),
  625. [&excluded_id, &candidate_id](const HmiControl &item)
  626. {
  627. return item.id != excluded_id && item.id == candidate_id; // 如果这个控件不是正在编辑的原控件,并且它的 ID 等于准备使用的新 ID
  628. });
  629. }
  630. // 根据控件类型生成默认样式、文字、大小、初始坐标
  631. HmiControl HmiEditorService::makeControl(
  632. const HmiPage &page, const HmiControlDescriptor &descriptor)
  633. {
  634. HmiControl control;
  635. control.id = makeUniqueId(page, descriptor.idPrefix);
  636. control.type = descriptor.type;
  637. control.text = descriptor.defaultText;
  638. control.bounds.width = std::min(descriptor.defaultBounds.width, page.width);
  639. control.bounds.height = std::min(descriptor.defaultBounds.height, page.height);
  640. if (descriptor.type == HmiControlType::PageJump)
  641. {
  642. control.pageJump = HmiPageJumpConfig{};
  643. }
  644. if (descriptor.type == HmiControlType::ProgressBar)
  645. {
  646. control.progressBar = HmiProgressBarConfig{};
  647. }
  648. const int offset = static_cast<int>(page.controls.size()) * 16;
  649. control.bounds.x = std::min(20 + offset, page.width - control.bounds.width);
  650. control.bounds.y = std::min(20 + offset, page.height - control.bounds.height);
  651. return control;
  652. }
  653. // 生成页面内不重复控件 ID,前缀 + 自增数字
  654. std::string HmiEditorService::makeUniqueId(
  655. const HmiPage &page, const std::string &prefix)
  656. {
  657. // 同类控件从 1 递增命名,保证页面内标识稳定且唯一
  658. int suffix = 1;
  659. while (true)
  660. {
  661. const std::string candidate = prefix + '-' + std::to_string(suffix);
  662. const bool found = std::any_of(
  663. page.controls.cbegin(), page.controls.cend(),
  664. [&candidate](const HmiControl &item) { return item.id == candidate; });
  665. if (!found)
  666. {
  667. return candidate;
  668. }
  669. ++suffix;
  670. }
  671. }