refactor(panels): split effects paint into property_panel_effects.rs

`property_panel_sections.rs` reached 862 lines after the effect
parameter-stepper work — over the 800-line repo file gate. Move
`paint_effects_section` + `paint_effect_row` + `paint_effect_param_row`
into a new `property_panel_effects.rs` (128 lines) and re-export
`paint_effects_section` from `property_panel_sections` so callers
keep using `sections::*`.

`property_panel_sections.rs` is now 755 lines. No behaviour change;
op-editor-ui 140 / op-host-desktop 64 tests green.

🤖 Generated with [Claude Code](https://claude.com/claude-code)
This commit is contained in:
Kayshen-X 2026-05-17 14:45:43 +08:00
parent 722445b017
commit 448b39ec6c
3 changed files with 136 additions and 114 deletions

View file

@ -42,6 +42,7 @@ mod layer_panel_tests;
mod layer_panel_walkers;
pub mod property_panel;
pub mod property_panel_code;
pub mod property_panel_effects;
pub mod property_panel_fill;
pub mod property_panel_inputs;
pub mod property_panel_layout;

View file

@ -0,0 +1,128 @@
//! Effects-section paint helpers for [`crate::widgets::PropertyPanel`].
//! Split out of `property_panel_sections.rs` to honor the 800-line
//! file ceiling. Each effect block paints a type row plus one
//! parameter row per editable scalar field.
use crate::theme::Theme;
use crate::widgets::icons::{draw_icon, Icon};
use crate::widgets::property_panel::EffectSummary;
use crate::widgets::property_panel_inputs::{
paint_section_divider, paint_section_label_with_add, to_jian_color, INPUT_HEIGHT, PAD_X,
SECTION_GAP,
};
use crate::widgets::property_panel_layout::{
effect_param_fields, EFFECT_PARAM_ROW_HEIGHT, EFFECT_ROW_HEIGHT,
};
use crate::widgets::property_panel_sections::PropertyLabels;
use crate::widgets::PaintCx;
use crate::{Point2D, Rect, TextLayout};
// ── Effects section ───────────────────────────────────────────────
pub fn paint_effects_section(
cx: &mut PaintCx<'_>,
theme: &Theme,
labels: &PropertyLabels,
effects: &[EffectSummary],
x: f32,
y: f32,
width: f32,
) -> f32 {
let mut row_y = paint_section_label_with_add(cx, theme, labels.effects, x, y, width);
if effects.is_empty() {
row_y += 8.0;
} else {
for eff in effects {
paint_effect_row(cx, theme, eff, x, row_y, width);
row_y += EFFECT_ROW_HEIGHT;
for &(field, label) in effect_param_fields(eff.kind) {
paint_effect_param_row(cx, theme, label, eff.param_value(field), x, row_y, width);
row_y += EFFECT_PARAM_ROW_HEIGHT;
}
}
}
paint_section_divider(cx, theme, x, row_y, width);
row_y + SECTION_GAP
}
/// Paint one effect-parameter row: `<label> <value> [−] [+]`. The
/// "−"/"+" stepper rects must match `action_button_rects`'s
/// `AdjustEffectParam` rects exactly so paint + hit-test agree.
fn paint_effect_param_row(
cx: &mut PaintCx<'_>,
theme: &Theme,
label: &str,
value: f32,
x: f32,
y: f32,
width: f32,
) {
let label_layout = TextLayout::single_run(
label,
"system-ui",
11.0,
to_jian_color(theme.muted_foreground),
Point2D::new(0.0, 0.0),
);
cx.backend
.draw_text(&label_layout, Point2D::new(x + PAD_X + 4.0, y + 15.0));
let value_text = format!("{value:.0}");
let value_layout = TextLayout::single_run(
&value_text,
"system-ui",
12.0,
to_jian_color(theme.foreground),
Point2D::new(0.0, 0.0),
);
cx.backend.draw_text(
&value_layout,
Point2D::new(x + width - PAD_X - 78.0, y + 15.0),
);
// "−" then "+" — geometry mirrors the `AdjustEffectParam` rects.
for (icon, off) in [(Icon::Minus, 48.0_f32), (Icon::Plus, 22.0_f32)] {
let r = Rect {
origin: Point2D::new(x + width - PAD_X - off, y + 3.0),
size: Point2D::new(22.0, INPUT_HEIGHT - 6.0),
};
cx.backend.fill_round_rect(r, 6.0, theme.muted);
draw_icon(
cx.backend,
icon,
Point2D::new(r.origin.x + 5.0, r.origin.y + (r.size.y - 12.0) / 2.0),
12.0,
theme.foreground,
1.4,
);
}
}
/// Paint one effect row — the effect-type label on the left + a
/// right-aligned "✕" remove glyph. The "✕" hit rect is emitted by
/// `action_button_rects` as `RemoveEffect(index)`, so the glyph
/// position here must match that rect.
fn paint_effect_row(
cx: &mut PaintCx<'_>,
theme: &Theme,
eff: &EffectSummary,
x: f32,
y: f32,
width: f32,
) {
let label = TextLayout::single_run(
eff.kind.label(),
"system-ui",
12.0,
to_jian_color(theme.foreground),
Point2D::new(0.0, 0.0),
);
cx.backend
.draw_text(&label, Point2D::new(x + PAD_X, y + 15.0));
draw_icon(
cx.backend,
Icon::Close,
Point2D::new(x + width - PAD_X - 14.0, y + 3.0),
14.0,
theme.muted_foreground,
1.4,
);
}

View file

@ -5,15 +5,12 @@
use crate::theme::Theme;
use crate::widgets::icons::{draw_icon, Icon};
use crate::widgets::property_panel::{EffectSummary, NodeSnapshot};
use crate::widgets::property_panel::NodeSnapshot;
use crate::widgets::property_panel_inputs::{
format_color_hex, paint_dropdown, paint_input_with_icon_focused,
paint_input_with_prefix_focused, paint_input_with_suffix_focused, paint_section_divider,
paint_section_label, paint_section_label_with_add, to_jian_color, HEADER_HEIGHT, INPUT_HEIGHT,
INPUT_RADIUS, PAD_X, SECTION_GAP, TAB_HEIGHT,
};
use crate::widgets::property_panel_layout::{
effect_param_fields, EFFECT_PARAM_ROW_HEIGHT, EFFECT_ROW_HEIGHT,
paint_section_label, to_jian_color, HEADER_HEIGHT, INPUT_HEIGHT, INPUT_RADIUS, PAD_X,
SECTION_GAP, TAB_HEIGHT,
};
use crate::widgets::PaintCx;
use crate::{Color, Point2D, Rect, TextLayout};
@ -21,6 +18,7 @@ use op_editor_core::PropertyFocus;
// Re-exports — fill paint moved to `property_panel_fill.rs`,
// layout walkers + visibility flags to `property_panel_layout.rs`.
pub use crate::widgets::property_panel_effects::paint_effects_section;
pub use crate::widgets::property_panel_fill::{
fill_type_label, paint_fill_section, paint_fill_type_picker,
};
@ -695,114 +693,9 @@ pub fn paint_stroke_section(
}
// ── Effects section ───────────────────────────────────────────────
pub fn paint_effects_section(
cx: &mut PaintCx<'_>,
theme: &Theme,
labels: &PropertyLabels,
effects: &[EffectSummary],
x: f32,
y: f32,
width: f32,
) -> f32 {
let mut row_y = paint_section_label_with_add(cx, theme, labels.effects, x, y, width);
if effects.is_empty() {
row_y += 8.0;
} else {
for eff in effects {
paint_effect_row(cx, theme, eff, x, row_y, width);
row_y += EFFECT_ROW_HEIGHT;
for &(field, label) in effect_param_fields(eff.kind) {
paint_effect_param_row(cx, theme, label, eff.param_value(field), x, row_y, width);
row_y += EFFECT_PARAM_ROW_HEIGHT;
}
}
}
paint_section_divider(cx, theme, x, row_y, width);
row_y + SECTION_GAP
}
/// Paint one effect-parameter row: `<label> <value> [−] [+]`. The
/// "−"/"+" stepper rects must match `action_button_rects`'s
/// `AdjustEffectParam` rects exactly so paint + hit-test agree.
fn paint_effect_param_row(
cx: &mut PaintCx<'_>,
theme: &Theme,
label: &str,
value: f32,
x: f32,
y: f32,
width: f32,
) {
let label_layout = TextLayout::single_run(
label,
"system-ui",
11.0,
to_jian_color(theme.muted_foreground),
Point2D::new(0.0, 0.0),
);
cx.backend
.draw_text(&label_layout, Point2D::new(x + PAD_X + 4.0, y + 15.0));
let value_text = format!("{value:.0}");
let value_layout = TextLayout::single_run(
&value_text,
"system-ui",
12.0,
to_jian_color(theme.foreground),
Point2D::new(0.0, 0.0),
);
cx.backend.draw_text(
&value_layout,
Point2D::new(x + width - PAD_X - 78.0, y + 15.0),
);
// "−" then "+" — geometry mirrors the `AdjustEffectParam` rects.
for (icon, off) in [(Icon::Minus, 48.0_f32), (Icon::Plus, 22.0_f32)] {
let r = Rect {
origin: Point2D::new(x + width - PAD_X - off, y + 3.0),
size: Point2D::new(22.0, INPUT_HEIGHT - 6.0),
};
cx.backend.fill_round_rect(r, 6.0, theme.muted);
draw_icon(
cx.backend,
icon,
Point2D::new(r.origin.x + 5.0, r.origin.y + (r.size.y - 12.0) / 2.0),
12.0,
theme.foreground,
1.4,
);
}
}
/// Paint one effect row — the effect-type label on the left + a
/// right-aligned "✕" remove glyph. The "✕" hit rect is emitted by
/// `action_button_rects` as `RemoveEffect(index)`, so the glyph
/// position here must match that rect.
fn paint_effect_row(
cx: &mut PaintCx<'_>,
theme: &Theme,
eff: &EffectSummary,
x: f32,
y: f32,
width: f32,
) {
let label = TextLayout::single_run(
eff.kind.label(),
"system-ui",
12.0,
to_jian_color(theme.foreground),
Point2D::new(0.0, 0.0),
);
cx.backend
.draw_text(&label, Point2D::new(x + PAD_X, y + 15.0));
draw_icon(
cx.backend,
Icon::Close,
Point2D::new(x + width - PAD_X - 14.0, y + 3.0),
14.0,
theme.muted_foreground,
1.4,
);
}
// `paint_effects_section` + its row helpers live in
// `property_panel_effects.rs` (split out at the 800-line cap);
// re-exported below so callers keep using `sections::*`.
// ── Export section ────────────────────────────────────────────────