feat(properties): make inherited properties editable, align dropdowns, and switch transparency to EditChoice

Enable in-place editing of inherited properties in the Properties panel:
- Transparency: Switch to PropValue::EditChoice with ByLayer and ByBlock presets to allow direct typing of any numeric percentage value (0..90%) without combo_box autocomplete interference.
- Select-all-on-click: Integrate EditChoice text inputs into build_field_key_map with dedicated widget IDs, preserving click-to-select-all across editable fields.
- Material: Provide bounded preset choices (ByLayer, ByBlock, Global, and active custom material) to avoid scanning large drawing dictionaries.
- Plot style: Implement mode-aware behavior with interactive Choice in STB mode and ReadOnlyWithTooltip in CTB mode.
- Read-only tooltips: Introduce PropValue::ReadOnlyWithTooltip to provide contextual explanations for locked fields while preserving mouse text selection and clipboard copying.
- Dropdown alignment & unified borders: Fix EditChoice internal input/caret background clipping so the container's 1px neutral/active border surrounds the entire control identically to Lineweight and text inputs.
- Document defaults & aggregation: Synchronize document header defaults when editing with No selection, and aggregate heterogeneous multi-selections (*VARIES*).
This commit is contained in:
Karim Jerbi committed 2026-08-23 23:20:34 +01:00
1 parent 7794736271
commit 7215faae44
11 files changed
+461 -144

No files matched your search

+25 -10
View File
@@ -10,15 +10,15 @@ pub fn general_section(entity: &EntityType) -> PropSection {
} else {
common.linetype.clone()
};
// Alpha 0 is the ByLayer default (Transparency::BY_LAYER); show it by name
// and fall back to a rounded percentage only for an explicit value.
let transp_display = if common.transparency.alpha() == 0 {
"ByLayer".to_string()
} else {
format!(
// Alpha 0 is ByLayer, Alpha 1 is ByBlock; show them by name and fall back
// to a rounded percentage for explicit values.
let transp_display = match common.transparency.alpha() {
0 => "ByLayer".to_string(),
1 => "ByBlock".to_string(),
alpha => format!(
"{}",
(common.transparency.alpha() as f64 / 255.0 * 100.0).round() as u32
)
(alpha as f64 / 255.0 * 100.0).round() as u32
),
};
// Hyperlink is stored in XDATA under the "PE_URL" application.
@@ -81,7 +81,10 @@ pub fn general_section(entity: &EntityType) -> PropSection {
Property {
label: t!("Transparency").into_owned(),
field: "transparency",
value: PropValue::EditText(transp_display),
value: PropValue::EditChoice {
value: transp_display,
options: vec!["ByLayer".to_string(), "ByBlock".to_string()],
},
},
Property {
label: t!("Hyperlink").into_owned(),
@@ -136,14 +139,26 @@ pub fn visualization_section(entity: &EntityType) -> Option<PropSection> {
let material = match common.material_flags {
0 => "ByLayer",
1 => "ByBlock",
2 => "Global",
_ => "Custom",
};
let mut options = vec![
"ByLayer".to_string(),
"ByBlock".to_string(),
"Global".to_string(),
];
if !options.iter().any(|o| o == material) && !material.is_empty() {
options.push(material.to_string());
}
Some(PropSection {
title: t!("3D Visualization").into_owned(),
props: vec![Property {
label: t!("Material").into_owned(),
field: "material",
value: PropValue::ReadOnly(material.into()),
value: PropValue::Choice {
selected: material.to_string(),
options,
},
}],
})
}
+2
View File
@@ -8,6 +8,8 @@ use glam::DVec3;
pub enum PropValue {
/// Read-only display text.
ReadOnly(String),
/// Read-only display text with a tooltip explaining why it cannot be edited.
ReadOnlyWithTooltip { value: String, tooltip: String },
/// Editable numeric field.
EditText(String),
/// Editable text that must not be expression-evaluated.
+2
View File
@@ -463,6 +463,7 @@ impl Scene {
match prop.value {
PropValue::PlainText(_) => QSelectValueEditor::Text,
PropValue::ReadOnly(ref value)
| PropValue::ReadOnlyWithTooltip { ref value, .. }
| PropValue::EditText(ref value) => {
let field = prop.field.to_ascii_lowercase();
let textual = [
@@ -660,6 +661,7 @@ impl Scene {
.find(|p| p.field == field)?;
Some(match prop.value {
PropValue::ReadOnly(s)
| PropValue::ReadOnlyWithTooltip { value: s, .. }
| PropValue::EditText(s)
| PropValue::PlainText(s) => s,
PropValue::LayerChoice(s) => s,
+49 -5
View File
@@ -135,11 +135,55 @@ pub fn apply_common_prop(entity: &mut EntityType, field: &str, value: &str) {
}
}
"transparency" => {
if let Ok(pct) = value.trim().parse::<f64>() {
let alpha = (pct.clamp(0.0, 100.0) / 100.0 * 255.0).round() as u8;
entity
.as_entity_mut()
.set_transparency(Transparency::new(alpha));
let s = value.trim();
if s.eq_ignore_ascii_case("ByLayer") {
entity.as_entity_mut().set_transparency(Transparency::new(0));
} else if s.eq_ignore_ascii_case("ByBlock") {
entity.as_entity_mut().set_transparency(Transparency::new(1));
} else {
let clean = s.trim_end_matches('%').trim();
if let Ok(pct) = clean.parse::<f64>() {
let alpha = (pct.clamp(0.0, 90.0) / 100.0 * 255.0).round() as u8;
entity
.as_entity_mut()
.set_transparency(Transparency::new(alpha));
}
}
}
"material" => {
let common = entity.common_mut();
match value.trim() {
"ByLayer" => {
common.material_flags = 0;
common.material_handle = None;
}
"ByBlock" => {
common.material_flags = 1;
common.material_handle = None;
}
"Global" => {
common.material_flags = 2;
common.material_handle = None;
}
_ => {}
}
}
"plot_style" => {
let common = entity.common_mut();
match value.trim() {
"ByLayer" => {
common.plotstyle_flags = 0;
common.plotstyle_handle = None;
}
"ByBlock" => {
common.plotstyle_flags = 1;
common.plotstyle_handle = None;
}
"Normal" => {
common.plotstyle_flags = 2;
common.plotstyle_handle = None;
}
_ => {}
}
}
"thickness" => {