//! Multileader Style Manager window — fills the entire OS window. use crate::app::Message; use iced::widget::{ button, checkbox, column, container, pick_list, row, scrollable, text, text_input, }; use iced::{Background, Border, Color, Element, Fill, Theme}; const BORDER: Color = Color { r: 0.35, g: 0.35, b: 0.35, a: 1.0, }; const TEXT: Color = Color { r: 0.88, g: 0.88, b: 0.88, a: 1.0, }; const DIM: Color = Color { r: 0.55, g: 0.55, b: 0.55, a: 1.0, }; const ACCENT: Color = Color { r: 0.25, g: 0.50, b: 0.85, a: 1.0, }; fn btn_s(accent: bool) -> impl Fn(&Theme, button::Status) -> button::Style { move |_: &Theme, st| button::Style { background: Some(Background::Color(match (accent, st) { (true, button::Status::Hovered | button::Status::Pressed) => Color { r: 0.20, g: 0.42, b: 0.72, a: 1.0, }, (false, button::Status::Hovered | button::Status::Pressed) => Color { r: 0.28, g: 0.28, b: 0.28, a: 1.0, }, (true, _) => ACCENT, _ => Color { r: 0.22, g: 0.22, b: 0.22, a: 1.0, }, })), text_color: TEXT, border: Border { color: BORDER, width: 1.0, radius: 4.0.into(), }, ..Default::default() } } /// View-model borrowed from the app for one render of the dialog. pub struct MLeaderStyleView<'a> { pub styles: Vec, pub selected: &'a str, pub style: Option<&'a acadrust::objects::MultiLeaderStyle>, pub current: String, pub landing_distance: &'a str, pub landing_gap: &'a str, pub arrowhead_size: &'a str, pub text_height: &'a str, pub scale_factor: &'a str, pub break_gap: &'a str, pub first_seg_angle: &'a str, pub second_seg_angle: &'a str, pub max_points: &'a str, pub default_text: &'a str, pub line_color: &'a str, pub text_color: &'a str, pub description: &'a str, pub line_weight: &'a str, pub align_space: &'a str, pub block_color: &'a str, pub block_rotation: &'a str, pub block_scale_x: &'a str, pub block_scale_y: &'a str, pub block_scale_z: &'a str, // Handle dropdown option lists + currently-selected record names. pub block_opts: Vec, pub lt_opts: Vec, pub textstyle_opts: Vec, pub line_type_name: String, pub arrowhead_name: String, pub text_style_name: String, pub block_content_name: String, /// Name of the style being renamed inline (double-clicked), if any. pub rename_active: Option<&'a str>, /// Edit buffer for the inline rename text input. pub rename_buf: &'a str, /// Colour field whose expanded palette is currently open. pub color_open: Option<&'static str>, } fn section<'a>(label: &'static str) -> Element<'a, Message> { text(label).size(11).color(ACCENT).into() } fn num_row<'a>( label: &'static str, placeholder: &'static str, value: &'a str, field: &'static str, ) -> Element<'a, Message> { row![ text(label).size(11).color(DIM).width(150), text_input(placeholder, value) .on_input(move |v| Message::MLeaderStyleEdit { field, value: v }) .size(11) .width(110), ] .spacing(8) .align_y(iced::Center) .into() } /// Shared colour selector row. Reuses MLeaderStyleEdit by sending the chosen /// colour as an ACI string; `open` shows the expanded palette. fn color_row<'a>( label: &'static str, value: &'a str, field: &'static str, open: bool, ) -> Element<'a, Message> { let cur = crate::ui::color_select::aci_string_to_color(value); let selector = crate::ui::color_select::color_selector( cur, open, crate::ui::color_select::ColorExtras { by_layer: true, by_block: true, }, move |c| Message::MLeaderStyleEdit { field, value: crate::ui::color_select::color_to_aci_string(c), }, Message::MLeaderColorMore(field), Message::OpenColorWindow(crate::app::ColorPickTarget::MLeader(field)), ); row![text(label).size(11).color(DIM).width(150), selector] .spacing(8) .align_y(iced::Center) .into() } fn enum_row<'a>( label: &'static str, options: Vec, selected: String, field: &'static str, ) -> Element<'a, Message> { row![ text(label).size(11).color(DIM).width(150), pick_list(options, Some(selected), move |value| { Message::MLeaderStyleSetEnum { field, value } }) .text_size(11) .width(190), ] .spacing(8) .align_y(iced::Center) .into() } /// The 11 horizontal text-attachment variants (debug names). const ATTACH_OPTS: [&str; 11] = [ "TopOfTopLine", "MiddleOfTopLine", "MiddleOfText", "MiddleOfBottomLine", "BottomOfBottomLine", "BottomLine", "BottomOfTopLineUnderlineBottomLine", "BottomOfTopLineUnderlineTopLine", "BottomOfTopLineUnderlineAll", "CenterOfText", "CenterOfTextOverline", ]; fn opts(list: &[&str]) -> Vec { list.iter().map(|s| s.to_string()).collect() } /// Dropdown for an Option field (linetype / arrowhead / text style / /// block content). Options are record names; "None" clears the handle. fn handle_row<'a>( label: &'static str, options: Vec, selected: String, field: &'static str, ) -> Element<'a, Message> { row![ text(label).size(11).color(DIM).width(150), pick_list(options, Some(selected), move |value| { Message::MLeaderStyleSetHandle { field, value } }) .text_size(11) .width(190), ] .spacing(8) .align_y(iced::Center) .into() } fn chk<'a>(label: &'static str, val: bool, field: &'static str) -> Element<'a, Message> { checkbox(val) .label(label) .on_toggle(move |_| Message::MLeaderStyleToggle(field)) .size(14) .text_size(11) .into() } pub fn view_window<'a>(v: MLeaderStyleView<'a>) -> Element<'a, Message> { // ── Right: Details panel ────────────────────────────────────────────── let details: Element<'_, Message> = if let Some(s) = v.style { scrollable( column![ row![ text("Name:").size(11).color(DIM).width(150), text(s.name.clone()).size(11), ] .spacing(8), num_row("Description:", "", v.description, "description"), // Leader Format section("Leader Format"), enum_row( "Path type:", opts(&["Invisible", "StraightLineSegments", "Spline"]), format!("{:?}", s.path_type), "path_type" ), color_row("Line color (ACI):", v.line_color, "line_color", v.color_open == Some("line_color")), num_row("Line weight:", "-2", v.line_weight, "line_weight"), handle_row( "Line type:", v.lt_opts.clone(), v.line_type_name.clone(), "line_type_handle" ), handle_row( "Arrowhead block:", v.block_opts.clone(), v.arrowhead_name.clone(), "arrowhead_handle" ), num_row( "Arrowhead size:", "0.18", v.arrowhead_size, "arrowhead_size" ), num_row("Break gap size:", "0.125", v.break_gap, "break_gap"), // Leader Structure section("Leader Structure"), chk("Enable landing", s.enable_landing, "enable_landing"), chk("Enable dogleg", s.enable_dogleg, "enable_dogleg"), num_row( "Landing distance:", "8.0", v.landing_distance, "landing_distance" ), num_row("Landing gap:", "0.09", v.landing_gap, "landing_gap"), num_row("Max leader points:", "2", v.max_points, "max_points"), num_row( "First seg. angle:", "0", v.first_seg_angle, "first_seg_angle" ), num_row( "Second seg. angle:", "0", v.second_seg_angle, "second_seg_angle" ), num_row("Scale factor:", "1.0", v.scale_factor, "scale_factor"), num_row("Align space:", "4.0", v.align_space, "align_space"), enum_row( "Leader draw order:", opts(&["LeaderHeadFirst", "LeaderTailFirst"]), format!("{:?}", s.leader_draw_order), "leader_draw_order" ), enum_row( "Multileader draw order:", opts(&["ContentFirst", "LeaderFirst"]), format!("{:?}", s.multileader_draw_order), "multileader_draw_order" ), chk("Annotative", s.is_annotative, "annotative"), // Content section("Content"), enum_row( "Content type:", opts(&["None", "Block", "MText", "Tolerance"]), format!("{:?}", s.content_type), "content_type" ), num_row("Default text:", "", v.default_text, "default_text"), handle_row( "Text style:", v.textstyle_opts.clone(), v.text_style_name.clone(), "text_style_handle" ), num_row("Text height:", "0.18", v.text_height, "text_height"), color_row("Text color (ACI):", v.text_color, "text_color", v.color_open == Some("text_color")), enum_row( "Text angle:", opts(&["ParallelToLastLeaderLine", "Horizontal", "Optimized"]), format!("{:?}", s.text_angle_type), "text_angle_type" ), enum_row( "Text alignment:", opts(&["Left", "Center", "Right"]), format!("{:?}", s.text_alignment), "text_alignment" ), enum_row( "Left attachment:", opts(&ATTACH_OPTS), format!("{:?}", s.text_left_attachment), "text_left_attachment" ), enum_row( "Right attachment:", opts(&ATTACH_OPTS), format!("{:?}", s.text_right_attachment), "text_right_attachment" ), enum_row( "Top attachment:", opts(&ATTACH_OPTS), format!("{:?}", s.text_top_attachment), "text_top_attachment" ), enum_row( "Bottom attachment:", opts(&ATTACH_OPTS), format!("{:?}", s.text_bottom_attachment), "text_bottom_attachment" ), enum_row( "Attachment direction:", opts(&["Horizontal", "Vertical"]), format!("{:?}", s.text_attachment_direction), "text_attachment_direction" ), chk("Text frame", s.text_frame, "text_frame"), chk("Text always left", s.text_always_left, "text_always_left"), // Block Content section("Block Content"), handle_row( "Block:", v.block_opts.clone(), v.block_content_name.clone(), "block_content_handle" ), color_row("Block color (ACI):", v.block_color, "block_color", v.color_open == Some("block_color")), enum_row( "Block connection:", opts(&["BlockExtents", "BasePoint"]), format!("{:?}", s.block_content_connection), "block_content_connection" ), num_row("Block rotation:", "0", v.block_rotation, "block_rotation"), num_row("Block scale X:", "1.0", v.block_scale_x, "block_scale_x"), num_row("Block scale Y:", "1.0", v.block_scale_y, "block_scale_y"), num_row("Block scale Z:", "1.0", v.block_scale_z, "block_scale_z"), chk( "Enable block scale", s.enable_block_scale, "enable_block_scale" ), chk( "Enable block rotation", s.enable_block_rotation, "enable_block_rotation" ), button(text("Apply").size(11)) .on_press(Message::MLeaderStyleApply) .style(btn_s(true)) .padding([4, 14]), ] .spacing(6) .padding([12, 12]), ) .width(Fill) .height(Fill) .into() } else { container(text("Select a style to view details.").size(11).color(DIM)) .padding([12, 12]) .into() }; let right_panel = container(details).width(Fill).height(Fill); crate::ui::style_manager::view(crate::ui::style_manager::Scaffold { kind: crate::app::StyleKind::MLeader, styles: &v.styles, selected: v.selected, current: Some(v.current.as_str()), rename_active: v.rename_active, rename_buf: v.rename_buf, on_new: Message::MLeaderStyleDialogNew, on_copy: Message::MLeaderStyleDialogCopy, on_delete: Message::MLeaderStyleDialogDelete, on_select: Message::MLeaderStyleDialogSelect, on_set_current: Message::MLeaderStyleDialogSetCurrent, on_apply: Message::MLeaderStyleApply, editor: right_panel.into(), }) }