forked from 1b-agent/opencadstudio-upstream
fix(text): grip-drag no longer hides text (#316)
Dragging text by a grip hid it until release. The dragged entity is hidden from the base wire set and shown as a preview overlay, but its SDF glyph quads (text_verts) were never uploaded for the preview, so the text vanished mid-drag. Add a per-frame preview_text GPU buffer (sibling to preview_wires). A rigid whole-entity move of pure text (TEXT/MTEXT, Square insertion grip) slides the drag-start glyphs by the drag delta instead of re-shaping the run every move; reshape grips (MTEXT width), dimensions and wire entities re-tessellate and feed the same buffer. Also fold preview_text into render_signature: a pure-text slide leaves preview_wires empty, so without it the scene-render cache froze the dragged text at its first frame (grip moved but text stayed, then jumped to place on release). Co-Authored-By: Claude Opus 4.8 <[email protected]>
This commit is contained in:
1 parent
88450f0cd1
commit
67fe363775
9 files changed
+176
-5
No files matched your search
@@ -377,6 +377,15 @@ pub(super) struct OpenCADStudio {
|
||||
/// the document live (so grips / properties track), so cancel reverts from
|
||||
/// this backup. Dropped (kept) on a normal commit.
|
||||
grip_original: Option<acadrust::EntityType>,
|
||||
/// Drag-start snapshot of the dragged entity's SDF glyph quads. A whole-
|
||||
/// entity text move slides these each frame (translating the already-shaped
|
||||
/// glyphs) instead of re-tessellating the run every cursor move (issue #316).
|
||||
grip_text_verts: Vec<crate::scene::pipeline::text_gpu::TextVertex>,
|
||||
/// True when the current grip drag is a rigid whole-entity move of pure text
|
||||
/// (TEXT / MTEXT, no wire geometry, insertion grip) — the case where the
|
||||
/// glyphs can just be slid. A reshape grip (e.g. an MTEXT width handle) or an
|
||||
/// entity with wires re-tessellates instead so the preview stays exact.
|
||||
grip_text_slide: bool,
|
||||
/// Open Quick Select panel state. `None` = panel closed. Filters are
|
||||
/// applied via `Message::QSelectApply`; the panel is dismissed on
|
||||
/// Apply / Cancel / Esc / outside-click.
|
||||
@@ -2101,6 +2110,8 @@ impl OpenCADStudio {
|
||||
grip_preview_handle: None,
|
||||
hover_dwell: None,
|
||||
grip_original: None,
|
||||
grip_text_verts: Vec::new(),
|
||||
grip_text_slide: false,
|
||||
qselect: None,
|
||||
show_ucs_icon: true,
|
||||
ucs_icon_at_origin: true,
|
||||
|
||||
@@ -493,6 +493,8 @@ pub(super) fn on_tab_close(&mut self, idx: usize) -> Task<Message> {
|
||||
// once, and drop the preview.
|
||||
if let Some(h) = self.grip_preview_handle.take() {
|
||||
let i = self.active_tab;
|
||||
self.grip_text_verts = Vec::new();
|
||||
self.grip_text_slide = false;
|
||||
if let Some(orig) = self.grip_original.take() {
|
||||
if let Some(e) = self.tabs[i].scene.document.get_entity_mut(h) {
|
||||
*e = orig;
|
||||
|
||||
@@ -721,6 +721,25 @@ pub(super) fn on_tick(&mut self, t: Instant) -> Task<Message> {
|
||||
// block cache so the hide doesn't re-tessellate blocks.
|
||||
self.tabs[i].scene.bump_geometry_no_blocks();
|
||||
self.grip_preview_handle = Some(grip.handle);
|
||||
// Snapshot the entity's glyph quads once so each move can
|
||||
// slide the already-shaped text rather than re-shaping it
|
||||
// (issue #316). The fast slide path only runs for a rigid
|
||||
// whole-entity move of pure text: no wire geometry (so a
|
||||
// dimension re-tessellates) and a Square insertion grip (so
|
||||
// an MTEXT width handle, a Triangle, still re-tessellates so
|
||||
// the re-wrap is exact).
|
||||
let snap = self.tabs[i].scene.wire_models_for(&[grip.handle]);
|
||||
self.grip_text_verts =
|
||||
snap.iter().flat_map(|w| w.text_verts.iter().copied()).collect();
|
||||
let square_grip = self.tabs[i]
|
||||
.selected_grips
|
||||
.iter()
|
||||
.find(|g| g.id == grip.grip_id)
|
||||
.map(|g| g.shape == crate::scene::model::object::GripShape::Square)
|
||||
.unwrap_or(false);
|
||||
self.grip_text_slide = !self.grip_text_verts.is_empty()
|
||||
&& snap.iter().all(|w| w.points.is_empty())
|
||||
&& square_grip;
|
||||
}
|
||||
|
||||
// The edited entity is hidden, so it's already absent from
|
||||
@@ -778,10 +797,29 @@ pub(super) fn on_tick(&mut self, t: Instant) -> Task<Message> {
|
||||
.apply_grip(grip.handle, grip.grip_id, apply);
|
||||
self.tabs[i].dirty = true;
|
||||
self.tabs[i].active_grip.as_mut().unwrap().last_world = snapped;
|
||||
// Overlay the moved entity (hidden from the base) — no base
|
||||
// re-tessellation, just a one-entity preview tessellation.
|
||||
let preview = self.tabs[i].scene.wire_models_for(&[grip.handle]);
|
||||
self.tabs[i].scene.set_preview_wires(preview);
|
||||
// Overlay the moved entity (hidden from the base). Pure text
|
||||
// moved as a whole slides its drag-start glyphs — no re-shaping
|
||||
// and no wire re-tess. Anything else (wire geometry, or a point
|
||||
// grip that reshapes text) is re-tessellated for an exact
|
||||
// preview; either way the glyphs ride the preview-text buffer
|
||||
// so dragged text never vanishes mid-drag (issue #316).
|
||||
if self.grip_text_slide {
|
||||
let d = snapped - grip.origin_world;
|
||||
let slid = crate::scene::pipeline::text_gpu::translate_verts(
|
||||
&self.grip_text_verts,
|
||||
[d.x, d.y, d.z],
|
||||
);
|
||||
self.tabs[i].scene.set_preview_text(slid);
|
||||
self.tabs[i].scene.set_preview_wires(Vec::new());
|
||||
} else {
|
||||
let preview = self.tabs[i].scene.wire_models_for(&[grip.handle]);
|
||||
let ptext = preview
|
||||
.iter()
|
||||
.flat_map(|w| w.text_verts.iter().copied())
|
||||
.collect();
|
||||
self.tabs[i].scene.set_preview_text(ptext);
|
||||
self.tabs[i].scene.set_preview_wires(preview);
|
||||
}
|
||||
self.refresh_selected_grips();
|
||||
self.refresh_properties();
|
||||
return Task::none();
|
||||
@@ -1729,6 +1767,8 @@ pub(super) fn on_tick(&mut self, t: Instant) -> Task<Message> {
|
||||
// re-tessellate the base once, dropping the overlay preview.
|
||||
if let Some(h) = self.grip_preview_handle.take() {
|
||||
self.grip_original = None;
|
||||
self.grip_text_verts = Vec::new();
|
||||
self.grip_text_slide = false;
|
||||
self.tabs[i].scene.hidden.remove(&h);
|
||||
self.tabs[i].scene.clear_preview_wire();
|
||||
// Only the dragged entity changed — re-tessellate just it.
|
||||
|
||||
Reference in new issue
Block a user