ocs_doc_api: Phase 2b-a (Text/MText annotations)

- CreateText/CreateMText/SetTextContent ops + GetTextContent query (append-only).
- backend add_text/add_mtext/set_text_content/text_content (Text.value/MText.value).
- facade create_text/create_mtext + Text/MText handles with content()/set_content().
- Text/MText coarse bounds + transform (insertion point); ensure_transformable.
- spec Text/MText families; api_reference + binding_schema regenerated; mocks.
- can_modify backend hook (exists + not-locked) for SetTextContent validation.
- consistency baseline extended with the appended variants.
- Test phase2b_text_mtext_create_content_transform -> 605 host lib + 3 consistency green.
This commit is contained in:
Sebastian committed 2026-09-06 09:07:41 +02:00
1 parent 1925a83a25
commit fc0e2c2c9d
16 files changed
+449 -4

No files matched your search

+44
View File
@@ -267,6 +267,8 @@ handle!(Ellipse);
handle!(Spline);
handle!(Ray);
handle!(XLine);
handle!(Text);
handle!(MText);
impl Entity {
pub fn view(&self) -> ApiResult<EntityView> {
@@ -331,6 +333,27 @@ impl Polyline {
}
}
macro_rules! text_handle {
($name:ident) => {
impl $name {
/// The text content of this annotation.
pub fn content(&self) -> ApiResult<String> {
match self.session.one_query(Query::GetTextContent { id: self.id })? {
QueryResult::TextContent(s) => Ok(s),
_ => Err(ApiError::Transport("unexpected content result".into())),
}
}
/// Replace the text content (one undo step).
pub fn set_content(&self, value: &str) -> ApiResult<()> {
self.session.apply_op(Operation::SetTextContent { id: self.id, value: value.to_string() })?;
Ok(())
}
}
};
}
text_handle!(Text);
text_handle!(MText);
// ── Collections (construction / lookup) ─────────────────────────────────────
#[derive(Clone)]
@@ -442,6 +465,27 @@ impl CurveCollection {
let (s, id) = self.create_curve(Curve2Spec::XLine { origin, direction })?;
Ok(XLine::new(s, id))
}
/// Single-line TEXT annotation.
pub fn create_text(&self, value: &str, insertion_point: [f64; 3], height: f64, rotation: f64) -> ApiResult<Text> {
let receipt = self.session.apply_op(Operation::CreateText(crate::ops::TextSpec {
value: value.to_string(),
insertion_point,
height,
rotation,
}))?;
let id = receipt.outcome.and_then(|o| o.new_id()).ok_or_else(|| ApiError::Transport("create_text returned no id".into()))?;
Ok(Text::new(self.session.clone(), id))
}
/// Multi-line MTEXT annotation.
pub fn create_mtext(&self, value: &str, insertion_point: [f64; 3], height: f64) -> ApiResult<MText> {
let receipt = self.session.apply_op(Operation::CreateMText(crate::ops::MTextSpec {
value: value.to_string(),
insertion_point,
height,
}))?;
let id = receipt.outcome.and_then(|o| o.new_id()).ok_or_else(|| ApiError::Transport("create_mtext returned no id".into()))?;
Ok(MText::new(self.session.clone(), id))
}
/// Bulk-create many points in ONE op (plan §5.3): all-or-nothing, one undo step.
pub fn create_points(&self, positions: &[[f64; 3]]) -> ApiResult<Vec<Point>> {
let specs: Vec<crate::ops::EntitySpec> = positions