feat(field): FIELDDISPLAY gray field background (screen only, profile setting)

Co-Authored-By: Claude Opus 5.5 <[email protected]>
This commit is contained in:
ramox81andClaude Opus 5.5 committed 2026-10-02 11:20:02 +03:00
1 parent 762cf1e7e4
commit f6f2b81441
6 files changed
+120

No files matched your search

+1
View File
@@ -668,6 +668,7 @@ inventory::submit!(crate::command::CommandRegistration {
"DGNFRAME",
"PDFOSNAP",
"UOSNAP",
"FIELDDISPLAY",
"PDFIMPORTMODE",
"PDFIMPORTFILTER",
"PDFIMPORTLAYERS",
+35
View File
@@ -1025,6 +1025,7 @@ impl OpenCADStudio {
| "DWFOSNAP"
| "DGNOSNAP"
| "UOSNAP"
| "FIELDDISPLAY"
| "PDFIMPORTMODE"
| "PDFIMPORTFILTER"
| "PDFIMPORTLAYERS"
@@ -1232,6 +1233,40 @@ impl OpenCADStudio {
}
return Some(self.finish_dispatch(cmd));
}
if name == "FIELDDISPLAY" {
let current = i16::from(crate::entities::field::display());
match value.as_deref().map(|v| v.trim().parse::<i16>().ok().filter(|v| (0..=1).contains(v))) {
Some(Some(mode)) => {
if current != mode {
crate::entities::field::set_display(mode == 1);
// A profile setting: redraw the fields of every open drawing.
for tab in &mut self.tabs {
let changes: Vec<_> = tab
.scene
.document
.entities()
.filter(|e| crate::entities::field::hosts_field(&tab.scene.document, e))
.map(|e| (e.common().handle, crate::scene::ChangeKind::Modified))
.collect();
if !changes.is_empty() {
tab.scene.bump_entities(&changes);
}
}
self.save_config();
}
}
Some(None) => {
self.command_line.push_error(crate::t!("Requires 0 or 1 only.").as_ref());
self.command_line.push_output(&format!("Enter new value for FIELDDISPLAY <{current}>:"));
self.pending_setvar = Some(name.clone());
}
None => {
self.command_line.push_output(&format!("Enter new value for FIELDDISPLAY <{current}>:"));
self.pending_setvar = Some(name.clone());
}
}
return Some(self.finish_dispatch(cmd));
}
if matches!(name.as_str(), "PDFOSNAP" | "DWFOSNAP" | "DGNOSNAP" | "UOSNAP") {
use crate::scene::model::pdf_vector as pv;
use codec::entities::UnderlayType;
+3
View File
@@ -371,6 +371,8 @@ pub struct UserSettings {
pub image_frame: i16,
/// The Attribute Definition dialog's "Specify on-screen" choice.
pub attdef_on_screen: bool,
/// FIELDDISPLAY: fields show on a gray background.
pub field_display: bool,
/// Nested-copy symbol handling: false inserts, true binds.
pub ncopy_bind: bool,
/// Last Options page; unknown saved names fall back without rejecting the config.
@@ -750,6 +752,7 @@ impl Default for UserSettings {
grip_object_limit: DEFAULT_GRIP_OBJECT_LIMIT,
image_frame: 1,
attdef_on_screen: true,
field_display: true,
ncopy_bind: false,
cursor_type: CursorType::Crosshair,
crosshair_color: None,
+2
View File
@@ -677,6 +677,7 @@ impl OpenCADStudio {
grip_object_limit: self.grip_object_limit,
image_frame: crate::scene::frame::profile_image_mode(),
attdef_on_screen: crate::modules::draw::draw::attdef::session().on_screen,
field_display: crate::entities::field::display(),
ncopy_bind: self.ncopy_bind,
cursor_type: self.cursor_type,
crosshair_color: self.crosshair_color,
@@ -771,6 +772,7 @@ impl OpenCADStudio {
self.grip_object_limit = s.grip_object_limit.clamp(0, 32767);
crate::scene::frame::set_profile_image_mode(s.image_frame);
crate::modules::draw::draw::attdef::session().on_screen = s.attdef_on_screen;
crate::entities::field::set_display(s.field_display);
self.ncopy_bind = s.ncopy_bind;
self.cursor_type = s.cursor_type;
self.crosshair_color = s.crosshair_color;
+26
View File
@@ -249,3 +249,29 @@ fn os_date_locale() -> codec::fields::DateLocale {
#[cfg(not(target_os = "windows"))]
codec::fields::DateLocale::default()
}
/// FIELDDISPLAY: whether fields show on a gray background (not plotted).
/// A profile setting, so one value for every drawing.
static FIELD_DISPLAY: std::sync::atomic::AtomicBool = std::sync::atomic::AtomicBool::new(true);
pub fn display() -> bool {
FIELD_DISPLAY.load(std::sync::atomic::Ordering::Relaxed)
}
pub fn set_display(on: bool) {
FIELD_DISPLAY.store(on, std::sync::atomic::Ordering::Relaxed);
}
/// The gray the reference draws behind a field.
pub const BACKGROUND: [f32; 4] = [203.0 / 255.0, 203.0 / 255.0, 203.0 / 255.0, 1.0];
/// Whether `entity` hosts a field: its extension dictionary has ACAD_FIELD.
pub fn hosts_field(document: &CadDocument, entity: &codec::entities::EntityType) -> bool {
let Some(xdict) = entity.common().xdictionary_handle else {
return false;
};
matches!(
document.objects.get(&xdict),
Some(codec::objects::ObjectType::Dictionary(d)) if d.get("ACAD_FIELD").is_some()
)
}
+53
View File
@@ -1408,6 +1408,59 @@ pub fn tessellate(
// also carries the glyph quads built above.
if bins.is_empty() {
let mut wires: Vec<WireModel> = Vec::new();
// FIELDDISPLAY: a field shows on a gray box behind its
// glyphs, on screen only.
if text_aabb != WireModel::UNBOUNDED_AABB
&& crate::entities::field::display()
&& crate::entities::field::hosts_field(document, entity)
{
let corner_groups = match entity {
EntityType::MText(m) => {
let rotation = stroke_groups
.iter()
.find_map(|group| group.run.as_ref().map(|run| run.rotation as f64))
.unwrap_or(m.rotation);
oriented_mtext_corner_groups(&sdf_verts, m, rotation, 0.0, anno)
}
EntityType::Text(t) => vec![oriented_text_corners(
&sdf_verts,
[t.insertion_point.x, t.insertion_point.y],
t.rotation,
0.0,
)],
EntityType::AttributeEntity(a) => vec![oriented_text_corners(
&sdf_verts,
[a.insertion_point.x, a.insertion_point.y],
a.rotation,
0.0,
)],
_ => Vec::new(),
};
let mut ft = Vec::with_capacity(6 * corner_groups.len());
let mut ftl = Vec::with_capacity(6 * corner_groups.len());
for corners in &corner_groups {
for &k in &[0usize, 1, 2, 0, 2, 3] {
let (h, lo) = split_ds_xyz(corners[k][0], corners[k][1], elev_v);
ft.push(h);
ftl.push(lo);
}
}
if !ft.is_empty() {
wires.push(WireModel {
display_visible: true,
plot_visible: false,
name: name.clone(),
color: crate::entities::field::BACKGROUND,
selected,
line_weight_px,
aabb: WireModel::UNBOUNDED_AABB,
plinegen: true,
fill_tris: ft,
fill_tris_low: ftl,
..Default::default()
});
}
}
// MTEXT background and frame follow the glyph bounds.
if text_aabb != WireModel::UNBOUNDED_AABB {
if let EntityType::MText(m) = entity {