feat(text): MTEXT background fill / mask + text frame, editable in Properties

Render the MTEXT background (never drawn before — the flags were only shown
read-only in the panel) and make it editable.

- Text arm emits an opaque fill_tris rect behind the glyphs: flag 0x01 uses
  the entity background colour, 0x02 uses the canvas colour (a wipeout-style
  mask). Box = glyph bounds padded by (background_scale - 1) × text height.
- Text frame (flag 0x10) draws a closed rectangle in the text colour around
  the same box.
- Threaded `bg_color` into `tessellate()` (+ all callers and the
  MultiLeaderTess trait) so the mask can use the drawing-window colour.
- Properties: "Background mask" is a Off / Fill / Mask dropdown, "Background
  color" is its own colour picker (independent of the entity colour — new
  PropBgColor* messages; picking a colour switches to Fill mode), and "Text
  frame" is a checkbox toggle. Edits re-tessellate so the change shows live.

Renders via the face3d fill path, so the fill shows with FILLMODE on (same as
MLEADER background fill); the frame is a stroke and always shows.

Co-Authored-By: Claude Opus 4.8 <[email protected]>
This commit is contained in:
Hakan SevenandClaude Opus 4.8 committed 2026-07-07 17:41:16 +03:00
1 parent 110683f419
commit cd7306f530
10 files changed
+223 -14

No files matched your search

+28 -2
View File
@@ -145,6 +145,10 @@ pub struct PropertiesPanel {
pub color_picker_open: bool,
/// Whether the full 16×16 ACI palette is expanded inside the color picker.
pub color_palette_open: bool,
/// Whether the MTEXT background-colour picker dropdown is open. Separate
/// from `color_picker_open` so the entity colour and the background colour
/// pickers are independent.
pub bg_color_picker_open: bool,
}
impl Default for PropertiesPanel {
@@ -165,6 +169,7 @@ impl Default for PropertiesPanel {
source_handles: vec![],
color_picker_open: false,
color_palette_open: false,
bg_color_picker_open: false,
}
}
}
@@ -320,7 +325,7 @@ impl PropertiesPanel {
for prop in &section.props {
match &prop.value {
PropValue::ColorChoice(color) => {
col = col.push(self.render_color_row(&prop.label, *color));
col = col.push(self.render_color_row(&prop.label, prop.field, *color));
}
PropValue::ColorVaries => {
col = col.push(self.render_color_varies_row(&prop.label));
@@ -396,7 +401,28 @@ impl PropertiesPanel {
// ── Color row (custom picker) ─────────────────────────────────────────
fn render_color_row<'a>(&'a self, label: &'a str, color: AcadColor) -> Element<'a, Message> {
fn render_color_row<'a>(
&'a self,
label: &'a str,
field: &'static str,
color: AcadColor,
) -> Element<'a, Message> {
// MTEXT background colour uses its own picker state + messages so it
// routes to `background_color`, not the entity's main colour.
if field == "background_color" {
let selector = crate::ui::color_select::color_selector(
color,
self.bg_color_picker_open,
crate::ui::color_select::ColorExtras {
by_layer: true,
by_block: true,
},
Message::PropBgColorChanged,
Message::PropBgColorPickerToggle,
Message::PropBgColorPickerToggle,
);
return prop_row_widget(label, selector);
}
let selector = crate::ui::color_select::color_selector(
color,
self.color_picker_open,