载入中...
搜索中...
未找到
EditorUiSkinPreview.cpp
浏览该文件的文档.
2
3#include <algorithm>
4#include <cmath>
5
6namespace eve::editor {
7namespace {
8double aligned(double start, double available, double extent, const std::string& alignment) {
9 if (alignment == "center") return start + (available - extent) * 0.5;
10 if (alignment == "end") return start + available - extent;
11 return start;
12}
13
14void intersect(double& x, double& y, double& width, double& height,
15 double otherX, double otherY, double otherWidth, double otherHeight) {
16 const double right = std::min(x + width, otherX + otherWidth);
17 const double bottom = std::min(y + height, otherY + otherHeight);
18 x = std::max(x, otherX); y = std::max(y, otherY);
19 width = std::max(0.0, right - x); height = std::max(0.0, bottom - y);
20}
21} // namespace
22
24 const UiPreviewSnapshot& preview,
25 const IUiSkinAssetResolver& assets) const {
26 UiSkinDrawPlan result;
27 const auto error = [&](const char* rule, std::string message) {
29 DiagnosticCode::PreconditionViolation, RuleId(rule), DiagnosticSeverity::Error,
30 std::move(message)));
31 };
32 result.documentRevision = document.revision();
33 if (preview.status != EditorStatus::Applied || preview.documentRevision != document.revision()) {
34 result.status = EditorStatus::Conflict;
35 error("editor.ui.skin-stale-preview", "UI skin planning requires a current layout preview");
36 return result;
37 }
38 std::map<ObjectId, UiPreviewWidget> rectangles;
39 for (const auto& rectangle : preview.widgets) rectangles.emplace(rectangle.id, rectangle);
40 for (const auto& rectangle : preview.widgets) {
41 if (!rectangle.visible) continue;
42 auto widgetResult = document.widget(rectangle.id);
43 if (!widgetResult.ok()) { result.status = EditorStatus::Conflict; return result; }
44 const UiWidgetSnapshot& widget = widgetResult.value();
45 double contentX = rectangle.x + widget.style.paddingLeft;
46 double contentY = rectangle.y + widget.style.paddingTop;
47 double contentWidth = std::max(0.0, rectangle.width - widget.style.paddingLeft - widget.style.paddingRight);
48 double contentHeight = std::max(0.0, rectangle.height - widget.style.paddingTop - widget.style.paddingBottom);
49 double clipX = contentX, clipY = contentY, clipWidth = contentWidth, clipHeight = contentHeight;
50 ObjectId ancestor = widget.parent;
51 while (widget.content.clip && !ancestor.empty()) {
52 auto parentWidget = document.widget(ancestor);
53 auto parentRect = rectangles.find(ancestor);
54 if (!parentWidget.ok() || parentRect == rectangles.end()) break;
55 intersect(clipX, clipY, clipWidth, clipHeight, parentRect->second.x, parentRect->second.y,
56 parentRect->second.width, parentRect->second.height);
57 ancestor = parentWidget.value().parent;
58 }
59 if (!widget.content.textureAsset.empty()) {
60 auto metadata = assets.texture(widget.content.textureAsset);
61 if (!metadata.ok() || metadata.value().width <= 0.0 || metadata.value().height <= 0.0) {
62 error("editor.ui.texture-unresolved",
63 "Could not resolve UI texture: " + widget.content.textureAsset);
64 } else {
65 UiSkinDrawCommand command; command.kind = UiSkinDrawKind::Image; command.widget = widget.id;
66 command.asset = widget.content.textureAsset; command.x = contentX; command.y = contentY;
67 command.width = contentWidth; command.height = contentHeight;
68 command.clipX = clipX; command.clipY = clipY; command.clipWidth = clipWidth; command.clipHeight = clipHeight;
69 const double sourceAspect = metadata.value().width / metadata.value().height;
70 const double targetAspect = contentHeight > 0.0 ? contentWidth / contentHeight : sourceAspect;
71 if (widget.content.imageFit == "contain") {
72 if (sourceAspect > targetAspect) command.height = contentWidth / sourceAspect;
73 else command.width = contentHeight * sourceAspect;
74 command.x = aligned(contentX, contentWidth, command.width, widget.content.horizontalAlign);
75 command.y = aligned(contentY, contentHeight, command.height, widget.content.verticalAlign);
76 } else if (widget.content.imageFit == "cover") {
77 if (sourceAspect > targetAspect) {
78 const double visible = targetAspect / sourceAspect;
79 command.u0 = (1.0 - visible) * 0.5; command.u1 = 1.0 - command.u0;
80 } else {
81 const double visible = sourceAspect / targetAspect;
82 command.v0 = (1.0 - visible) * 0.5; command.v1 = 1.0 - command.v0;
83 }
84 }
85 result.commands.push_back(std::move(command));
86 }
87 }
88 if (!widget.text.empty()) {
89 auto font = assets.font(widget.content.fontAsset);
90 if (widget.content.fontAsset.empty() || !font.ok()) {
91 error("editor.ui.font-unresolved", "Could not resolve UI font: " + widget.content.fontAsset);
92 } else {
93 UiSkinDrawCommand command; command.kind = UiSkinDrawKind::Text; command.widget = widget.id;
94 command.asset = widget.content.fontAsset; command.text = widget.text;
95 command.x = contentX; command.y = aligned(contentY, contentHeight, widget.content.fontSize,
96 widget.content.verticalAlign);
97 command.width = contentWidth; command.height = widget.content.fontSize;
98 command.clipX = clipX; command.clipY = clipY; command.clipWidth = clipWidth; command.clipHeight = clipHeight;
99 command.r = widget.content.textR; command.g = widget.content.textG;
100 command.b = widget.content.textB; command.a = widget.content.textA;
101 command.fontSize = widget.content.fontSize;
102 result.commands.push_back(std::move(command));
103 }
104 }
105 }
106 result.status = std::any_of(result.diagnostics.begin(), result.diagnostics.end(),
107 [](const EditorDiagnostic& diagnostic) {
108 return diagnostic.severity() == DiagnosticSeverity::Error;
109 })
110 ? EditorStatus::Rejected : EditorStatus::Applied;
111 return result;
112}
113
114} // namespace eve::editor
Duration start
float y
Definition AnimClip.cpp:738
float x
Definition AnimClip.cpp:738
std::string widget
std::string message
HexVec3 right
std::uint32_t height
std::uint32_t width
std::string error
Definition Package.cpp:60
bool visible
float bottom
A structured explanation of a failed, degraded, or noteworthy result.
Definition Diagnostic.h:94
UUID-backed identifier adapter for legacy textual boundaries.
Definition Identity.h:314
const std::string & value() const noexcept
Returns the exact legacy or canonical compatibility spelling.
Definition Identity.h:362
bool empty() const noexcept
True when no compatibility spelling has been assigned.
Definition Identity.h:374
std::uint64_t revision() const override
Return the current revision.
Asset metadata boundary used by renderer-neutral UI preview planning.
virtual Result< void > font(const std::string &asset) const =0
Report whether a font asset can be resolved.
virtual Result< UiTextureMetadata > texture(const std::string &asset) const =0
Resolve texture dimensions, returning NotFound for missing assets.
UiSkinDrawPlan build(const UiDocumentTarget &document, const UiPreviewSnapshot &preview, const IUiSkinAssetResolver &assets) const
Resolve assets and fit/clamp widget content against a layout preview.
Serializable UI authoring document with hierarchy and inspector capabilities.
Definition UiDocument.h:149
Result< UiWidgetSnapshot > widget(const ObjectId &id) const override
Widget.
Diagnostic ruleDiagnostic(eve::DiagnosticCode code, RuleId rule, DiagnosticSeverity severity, std::string message)
Build a common diagnostic carrying an open editing rule identity.
eve::editing::RuleId RuleId
Definition EditorIds.h:32
One deterministic text or image command, including effective clipping.
Revision-bound content plan suitable for graphics or UI-host presenters.
std::vector< UiSkinDrawCommand > commands
std::vector< EditorDiagnostic > diagnostics
Deterministic UI layout preview used by canvas rendering and picking.
Definition UiDocument.h:246
std::vector< UiPreviewWidget > widgets
Definition UiDocument.h:250
Immutable widget snapshot used across editor/runtime boundaries.
Definition UiDocument.h:94