cargo fmt
This commit is contained in:
parent
468cbde5e1
commit
ca07268c7c
204 changed files with 13988 additions and 5516 deletions
120
src/ui/about.rs
120
src/ui/about.rs
|
|
@ -2,12 +2,42 @@ use crate::app::Message;
|
|||
use iced::widget::{button, column, container, row, text, Space};
|
||||
use iced::{Background, Border, Color, Element, Fill, Theme};
|
||||
|
||||
const BG: Color = Color { r: 0.15, g: 0.15, b: 0.15, a: 1.0 };
|
||||
const PANEL: Color = Color { r: 0.12, g: 0.12, b: 0.12, a: 1.0 };
|
||||
const BORDER: Color = Color { r: 0.30, g: 0.30, b: 0.30, 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 };
|
||||
const WHITE: Color = Color { r: 0.92, g: 0.92, b: 0.92, a: 1.0 };
|
||||
const BG: Color = Color {
|
||||
r: 0.15,
|
||||
g: 0.15,
|
||||
b: 0.15,
|
||||
a: 1.0,
|
||||
};
|
||||
const PANEL: Color = Color {
|
||||
r: 0.12,
|
||||
g: 0.12,
|
||||
b: 0.12,
|
||||
a: 1.0,
|
||||
};
|
||||
const BORDER: Color = Color {
|
||||
r: 0.30,
|
||||
g: 0.30,
|
||||
b: 0.30,
|
||||
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,
|
||||
};
|
||||
const WHITE: Color = Color {
|
||||
r: 0.92,
|
||||
g: 0.92,
|
||||
b: 0.92,
|
||||
a: 1.0,
|
||||
};
|
||||
|
||||
fn info_row<'a>(label: &'static str, value: String) -> Element<'a, Message> {
|
||||
row![
|
||||
|
|
@ -21,28 +51,34 @@ fn info_row<'a>(label: &'static str, value: String) -> Element<'a, Message> {
|
|||
}
|
||||
|
||||
pub fn view_window<'a>() -> Element<'a, Message> {
|
||||
let version = env!("CARGO_PKG_VERSION");
|
||||
let os = std::env::consts::OS;
|
||||
let arch = std::env::consts::ARCH;
|
||||
let version = env!("CARGO_PKG_VERSION");
|
||||
let os = std::env::consts::OS;
|
||||
let arch = std::env::consts::ARCH;
|
||||
|
||||
let logo = container(
|
||||
column![
|
||||
text("H7CAD").size(32).color(ACCENT),
|
||||
text("CAD application for Architecture & Engineering")
|
||||
.size(11).color(DIM),
|
||||
.size(11)
|
||||
.color(DIM),
|
||||
]
|
||||
.spacing(4)
|
||||
.align_x(iced::Center),
|
||||
)
|
||||
.width(Fill)
|
||||
.padding(iced::Padding { top: 20.0, right: 0.0, bottom: 16.0, left: 0.0 })
|
||||
.padding(iced::Padding {
|
||||
top: 20.0,
|
||||
right: 0.0,
|
||||
bottom: 16.0,
|
||||
left: 0.0,
|
||||
})
|
||||
.align_x(iced::Center);
|
||||
|
||||
let info_block = container(
|
||||
column![
|
||||
info_row("Version", format!("v{}", version)),
|
||||
info_row("Platform", os.to_string()),
|
||||
info_row("Arch", arch.to_string()),
|
||||
info_row("Version", format!("v{}", version)),
|
||||
info_row("Platform", os.to_string()),
|
||||
info_row("Arch", arch.to_string()),
|
||||
]
|
||||
.spacing(2)
|
||||
.padding([12, 16]),
|
||||
|
|
@ -58,33 +94,45 @@ pub fn view_window<'a>() -> Element<'a, Message> {
|
|||
..Default::default()
|
||||
});
|
||||
|
||||
let copy_btn = button(
|
||||
text("Copy Info").size(11),
|
||||
)
|
||||
.on_press(Message::AboutCopyInfo)
|
||||
.style(|_: &Theme, st| button::Style {
|
||||
background: Some(Background::Color(match st {
|
||||
button::Status::Hovered | button::Status::Pressed =>
|
||||
Color { r: 0.20, g: 0.42, b: 0.72, a: 1.0 },
|
||||
_ => ACCENT,
|
||||
})),
|
||||
text_color: WHITE,
|
||||
border: Border { radius: 4.0.into(), ..Default::default() },
|
||||
..Default::default()
|
||||
})
|
||||
.padding([6, 16]);
|
||||
let copy_btn = button(text("Copy Info").size(11))
|
||||
.on_press(Message::AboutCopyInfo)
|
||||
.style(|_: &Theme, st| button::Style {
|
||||
background: Some(Background::Color(match st {
|
||||
button::Status::Hovered | button::Status::Pressed => Color {
|
||||
r: 0.20,
|
||||
g: 0.42,
|
||||
b: 0.72,
|
||||
a: 1.0,
|
||||
},
|
||||
_ => ACCENT,
|
||||
})),
|
||||
text_color: WHITE,
|
||||
border: Border {
|
||||
radius: 4.0.into(),
|
||||
..Default::default()
|
||||
},
|
||||
..Default::default()
|
||||
})
|
||||
.padding([6, 16]);
|
||||
|
||||
let footer = row![
|
||||
Space::new().width(Fill),
|
||||
copy_btn,
|
||||
]
|
||||
.align_y(iced::Center)
|
||||
.padding(iced::Padding { top: 12.0, right: 0.0, bottom: 0.0, left: 0.0 });
|
||||
let footer = row![Space::new().width(Fill), copy_btn,]
|
||||
.align_y(iced::Center)
|
||||
.padding(iced::Padding {
|
||||
top: 12.0,
|
||||
right: 0.0,
|
||||
bottom: 0.0,
|
||||
left: 0.0,
|
||||
});
|
||||
|
||||
container(
|
||||
column![logo, info_block, footer]
|
||||
.spacing(0)
|
||||
.padding(iced::Padding { top: 0.0, right: 20.0, bottom: 20.0, left: 20.0 }),
|
||||
.padding(iced::Padding {
|
||||
top: 0.0,
|
||||
right: 20.0,
|
||||
bottom: 20.0,
|
||||
left: 20.0,
|
||||
}),
|
||||
)
|
||||
.style(|_: &Theme| container::Style {
|
||||
background: Some(Background::Color(BG)),
|
||||
|
|
|
|||
|
|
@ -4,42 +4,122 @@ use crate::app::{DsField, Message};
|
|||
use iced::widget::{button, checkbox, column, container, row, scrollable, text, text_input, Space};
|
||||
use iced::{Background, Border, Color, Element, Fill, Theme};
|
||||
|
||||
const TB: Color = Color { r: 0.13, g: 0.13, b: 0.13, a: 1.0 };
|
||||
const BG: Color = Color { r: 0.15, g: 0.15, b: 0.15, a: 1.0 };
|
||||
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 };
|
||||
const ACTIVE: Color = Color { r: 0.20, g: 0.40, b: 0.70, a: 1.0 };
|
||||
const FIELD: Color = Color { r: 0.10, g: 0.10, b: 0.10, a: 1.0 };
|
||||
const LIST: Color = Color { r: 0.12, g: 0.12, b: 0.12, a: 1.0 };
|
||||
const TB: Color = Color {
|
||||
r: 0.13,
|
||||
g: 0.13,
|
||||
b: 0.13,
|
||||
a: 1.0,
|
||||
};
|
||||
const BG: Color = Color {
|
||||
r: 0.15,
|
||||
g: 0.15,
|
||||
b: 0.15,
|
||||
a: 1.0,
|
||||
};
|
||||
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,
|
||||
};
|
||||
const ACTIVE: Color = Color {
|
||||
r: 0.20,
|
||||
g: 0.40,
|
||||
b: 0.70,
|
||||
a: 1.0,
|
||||
};
|
||||
const FIELD: Color = Color {
|
||||
r: 0.10,
|
||||
g: 0.10,
|
||||
b: 0.10,
|
||||
a: 1.0,
|
||||
};
|
||||
const LIST: Color = Color {
|
||||
r: 0.12,
|
||||
g: 0.12,
|
||||
b: 0.12,
|
||||
a: 1.0,
|
||||
};
|
||||
|
||||
/// All DimStyle field values needed by the view.
|
||||
pub struct DimStyleValues<'a> {
|
||||
pub dimdle: &'a str, pub dimdli: &'a str, pub dimgap: &'a str,
|
||||
pub dimexe: &'a str, pub dimexo: &'a str,
|
||||
pub dimsd1: bool, pub dimsd2: bool,
|
||||
pub dimse1: bool, pub dimse2: bool,
|
||||
pub dimasz: &'a str, pub dimcen: &'a str, pub dimtsz: &'a str,
|
||||
pub dimtxt: &'a str, pub dimtxsty: &'a str, pub dimtad: &'a str,
|
||||
pub dimtih: bool, pub dimtoh: bool,
|
||||
pub dimscale: &'a str, pub dimlfac: &'a str,
|
||||
pub dimlunit: &'a str, pub dimdec: &'a str, pub dimpost: &'a str,
|
||||
pub dimtol: bool, pub dimlim: bool,
|
||||
pub dimtp: &'a str, pub dimtm: &'a str,
|
||||
pub dimtdec: &'a str, pub dimtfac: &'a str,
|
||||
pub dimdle: &'a str,
|
||||
pub dimdli: &'a str,
|
||||
pub dimgap: &'a str,
|
||||
pub dimexe: &'a str,
|
||||
pub dimexo: &'a str,
|
||||
pub dimsd1: bool,
|
||||
pub dimsd2: bool,
|
||||
pub dimse1: bool,
|
||||
pub dimse2: bool,
|
||||
pub dimasz: &'a str,
|
||||
pub dimcen: &'a str,
|
||||
pub dimtsz: &'a str,
|
||||
pub dimtxt: &'a str,
|
||||
pub dimtxsty: &'a str,
|
||||
pub dimtad: &'a str,
|
||||
pub dimtih: bool,
|
||||
pub dimtoh: bool,
|
||||
pub dimscale: &'a str,
|
||||
pub dimlfac: &'a str,
|
||||
pub dimlunit: &'a str,
|
||||
pub dimdec: &'a str,
|
||||
pub dimpost: &'a str,
|
||||
pub dimtol: bool,
|
||||
pub dimlim: bool,
|
||||
pub dimtp: &'a str,
|
||||
pub dimtm: &'a str,
|
||||
pub dimtdec: &'a str,
|
||||
pub dimtfac: &'a str,
|
||||
}
|
||||
|
||||
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 },
|
||||
(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() },
|
||||
border: Border {
|
||||
color: BORDER,
|
||||
width: 1.0,
|
||||
radius: 4.0.into(),
|
||||
},
|
||||
..Default::default()
|
||||
}
|
||||
}
|
||||
|
|
@ -47,12 +127,26 @@ fn btn_s(accent: bool) -> impl Fn(&Theme, button::Status) -> button::Style {
|
|||
fn tab_btn_style(active: bool) -> impl Fn(&Theme, button::Status) -> button::Style {
|
||||
move |_: &Theme, st| button::Style {
|
||||
background: Some(Background::Color(match (active, st) {
|
||||
(true, _) => ACTIVE,
|
||||
(false, button::Status::Hovered | button::Status::Pressed) => Color { r: 0.28, g: 0.28, b: 0.28, a: 1.0 },
|
||||
_ => Color { r: 0.20, g: 0.20, b: 0.20, a: 1.0 },
|
||||
(true, _) => ACTIVE,
|
||||
(false, button::Status::Hovered | button::Status::Pressed) => Color {
|
||||
r: 0.28,
|
||||
g: 0.28,
|
||||
b: 0.28,
|
||||
a: 1.0,
|
||||
},
|
||||
_ => Color {
|
||||
r: 0.20,
|
||||
g: 0.20,
|
||||
b: 0.20,
|
||||
a: 1.0,
|
||||
},
|
||||
})),
|
||||
text_color: TEXT,
|
||||
border: Border { color: BORDER, width: 1.0, radius: 3.0.into() },
|
||||
border: Border {
|
||||
color: BORDER,
|
||||
width: 1.0,
|
||||
radius: 3.0.into(),
|
||||
},
|
||||
..Default::default()
|
||||
}
|
||||
}
|
||||
|
|
@ -60,12 +154,26 @@ fn tab_btn_style(active: bool) -> impl Fn(&Theme, button::Status) -> button::Sty
|
|||
fn list_item_style(active: bool) -> impl Fn(&Theme, button::Status) -> button::Style {
|
||||
move |_: &Theme, st| button::Style {
|
||||
background: Some(Background::Color(match (active, st) {
|
||||
(true, _) => ACTIVE,
|
||||
(false, button::Status::Hovered | button::Status::Pressed) => Color { r: 0.28, g: 0.28, b: 0.28, a: 1.0 },
|
||||
_ => Color { r: 0.18, g: 0.18, b: 0.18, a: 1.0 },
|
||||
(true, _) => ACTIVE,
|
||||
(false, button::Status::Hovered | button::Status::Pressed) => Color {
|
||||
r: 0.28,
|
||||
g: 0.28,
|
||||
b: 0.28,
|
||||
a: 1.0,
|
||||
},
|
||||
_ => Color {
|
||||
r: 0.18,
|
||||
g: 0.18,
|
||||
b: 0.18,
|
||||
a: 1.0,
|
||||
},
|
||||
})),
|
||||
text_color: TEXT,
|
||||
border: Border { color: BORDER, width: 0.0, radius: 3.0.into() },
|
||||
border: Border {
|
||||
color: BORDER,
|
||||
width: 0.0,
|
||||
radius: 3.0.into(),
|
||||
},
|
||||
..Default::default()
|
||||
}
|
||||
}
|
||||
|
|
@ -73,14 +181,22 @@ fn list_item_style(active: bool) -> impl Fn(&Theme, button::Status) -> button::S
|
|||
fn field_style(_: &Theme, _: text_input::Status) -> text_input::Style {
|
||||
text_input::Style {
|
||||
background: Background::Color(FIELD),
|
||||
border: Border { color: BORDER, width: 1.0, radius: 3.0.into() },
|
||||
icon: TEXT, placeholder: DIM, value: TEXT, selection: ACCENT,
|
||||
border: Border {
|
||||
color: BORDER,
|
||||
width: 1.0,
|
||||
radius: 3.0.into(),
|
||||
},
|
||||
icon: TEXT,
|
||||
placeholder: DIM,
|
||||
value: TEXT,
|
||||
selection: ACCENT,
|
||||
}
|
||||
}
|
||||
|
||||
fn hdivider<'a>() -> Element<'a, Message> {
|
||||
container(Space::new().width(Fill).height(1))
|
||||
.width(Fill).height(1)
|
||||
.width(Fill)
|
||||
.height(1)
|
||||
.style(|_: &Theme| container::Style {
|
||||
background: Some(Background::Color(BORDER)),
|
||||
..Default::default()
|
||||
|
|
@ -97,15 +213,30 @@ pub fn view_window<'a>(
|
|||
// ── Toolbar ───────────────────────────────────────────────────────────
|
||||
let toolbar = container(
|
||||
row![
|
||||
button(text("New").size(11)).on_press(Message::DimStyleDialogNew).style(btn_s(false)).padding([4, 10]),
|
||||
button(text("Delete").size(11)).on_press(Message::DimStyleDialogDelete).style(btn_s(false)).padding([4, 10]),
|
||||
button(text("Set Current").size(11)).on_press(Message::DimStyleDialogSetCurrent).style(btn_s(false)).padding([4, 10]),
|
||||
button(text("New").size(11))
|
||||
.on_press(Message::DimStyleDialogNew)
|
||||
.style(btn_s(false))
|
||||
.padding([4, 10]),
|
||||
button(text("Delete").size(11))
|
||||
.on_press(Message::DimStyleDialogDelete)
|
||||
.style(btn_s(false))
|
||||
.padding([4, 10]),
|
||||
button(text("Set Current").size(11))
|
||||
.on_press(Message::DimStyleDialogSetCurrent)
|
||||
.style(btn_s(false))
|
||||
.padding([4, 10]),
|
||||
Space::new().width(Fill),
|
||||
button(text("Apply").size(11)).on_press(Message::DimStyleDialogApply).style(btn_s(true)).padding([4, 14]),
|
||||
button(text("Close").size(11)).on_press(Message::DimStyleDialogClose).style(btn_s(false)).padding([4, 10]),
|
||||
button(text("Apply").size(11))
|
||||
.on_press(Message::DimStyleDialogApply)
|
||||
.style(btn_s(true))
|
||||
.padding([4, 14]),
|
||||
button(text("Close").size(11))
|
||||
.on_press(Message::DimStyleDialogClose)
|
||||
.style(btn_s(false))
|
||||
.padding([4, 10]),
|
||||
]
|
||||
.spacing(4)
|
||||
.align_y(iced::Center)
|
||||
.align_y(iced::Center),
|
||||
)
|
||||
.style(|_: &Theme| container::Style {
|
||||
background: Some(Background::Color(TB)),
|
||||
|
|
@ -123,7 +254,7 @@ pub fn view_window<'a>(
|
|||
.on_press(Message::DimStyleDialogSelect(name.clone()))
|
||||
.style(list_item_style(active))
|
||||
.padding([4, 8])
|
||||
.width(Fill)
|
||||
.width(Fill),
|
||||
);
|
||||
}
|
||||
let style_list = container(
|
||||
|
|
@ -132,7 +263,11 @@ pub fn view_window<'a>(
|
|||
container(scrollable(list_col).height(Fill))
|
||||
.style(|_: &Theme| container::Style {
|
||||
background: Some(Background::Color(LIST)),
|
||||
border: Border { color: BORDER, width: 1.0, radius: 3.0.into() },
|
||||
border: Border {
|
||||
color: BORDER,
|
||||
width: 1.0,
|
||||
radius: 3.0.into()
|
||||
},
|
||||
..Default::default()
|
||||
})
|
||||
.width(180)
|
||||
|
|
@ -140,20 +275,41 @@ pub fn view_window<'a>(
|
|||
.padding(2),
|
||||
]
|
||||
.spacing(4)
|
||||
.height(Fill)
|
||||
.height(Fill),
|
||||
)
|
||||
.width(180)
|
||||
.height(Fill)
|
||||
.padding(iced::Padding { top: 12.0, right: 8.0, bottom: 12.0, left: 12.0 });
|
||||
.padding(iced::Padding {
|
||||
top: 12.0,
|
||||
right: 8.0,
|
||||
bottom: 12.0,
|
||||
left: 12.0,
|
||||
});
|
||||
|
||||
// ── Tab bar ───────────────────────────────────────────────────────────
|
||||
let tabs = row![
|
||||
button(text("Lines").size(11)).on_press(Message::DimStyleDialogTab(0)).style(tab_btn_style(tab == 0)).padding([4, 10]),
|
||||
button(text("Arrows").size(11)).on_press(Message::DimStyleDialogTab(1)).style(tab_btn_style(tab == 1)).padding([4, 10]),
|
||||
button(text("Text").size(11)).on_press(Message::DimStyleDialogTab(2)).style(tab_btn_style(tab == 2)).padding([4, 10]),
|
||||
button(text("Scale/Units").size(11)).on_press(Message::DimStyleDialogTab(3)).style(tab_btn_style(tab == 3)).padding([4, 10]),
|
||||
button(text("Tolerances").size(11)).on_press(Message::DimStyleDialogTab(4)).style(tab_btn_style(tab == 4)).padding([4, 10]),
|
||||
].spacing(2);
|
||||
button(text("Lines").size(11))
|
||||
.on_press(Message::DimStyleDialogTab(0))
|
||||
.style(tab_btn_style(tab == 0))
|
||||
.padding([4, 10]),
|
||||
button(text("Arrows").size(11))
|
||||
.on_press(Message::DimStyleDialogTab(1))
|
||||
.style(tab_btn_style(tab == 1))
|
||||
.padding([4, 10]),
|
||||
button(text("Text").size(11))
|
||||
.on_press(Message::DimStyleDialogTab(2))
|
||||
.style(tab_btn_style(tab == 2))
|
||||
.padding([4, 10]),
|
||||
button(text("Scale/Units").size(11))
|
||||
.on_press(Message::DimStyleDialogTab(3))
|
||||
.style(tab_btn_style(tab == 3))
|
||||
.padding([4, 10]),
|
||||
button(text("Tolerances").size(11))
|
||||
.on_press(Message::DimStyleDialogTab(4))
|
||||
.style(tab_btn_style(tab == 4))
|
||||
.padding([4, 10]),
|
||||
]
|
||||
.spacing(2);
|
||||
|
||||
let lbl = |s: &'static str| text(s).size(11).color(DIM).width(180);
|
||||
|
||||
|
|
@ -178,49 +334,159 @@ pub fn view_window<'a>(
|
|||
let tab_content: Element<'_, Message> = match tab {
|
||||
0 => column![
|
||||
text("Dimension Line").size(11).color(ACCENT),
|
||||
row![lbl("Extension (DIMDLE)"), mk_field(DsField::Dimdle, vals.dimdle)].spacing(8).align_y(iced::Center),
|
||||
row![lbl("Spacing (DIMDLI)"), mk_field(DsField::Dimdli, vals.dimdli)].spacing(8).align_y(iced::Center),
|
||||
row![lbl("Text gap (DIMGAP)"), mk_field(DsField::Dimgap, vals.dimgap)].spacing(8).align_y(iced::Center),
|
||||
row![
|
||||
lbl("Extension (DIMDLE)"),
|
||||
mk_field(DsField::Dimdle, vals.dimdle)
|
||||
]
|
||||
.spacing(8)
|
||||
.align_y(iced::Center),
|
||||
row![
|
||||
lbl("Spacing (DIMDLI)"),
|
||||
mk_field(DsField::Dimdli, vals.dimdli)
|
||||
]
|
||||
.spacing(8)
|
||||
.align_y(iced::Center),
|
||||
row![
|
||||
lbl("Text gap (DIMGAP)"),
|
||||
mk_field(DsField::Dimgap, vals.dimgap)
|
||||
]
|
||||
.spacing(8)
|
||||
.align_y(iced::Center),
|
||||
chk("Suppress 1st line (DIMSD1)", vals.dimsd1, DsField::Dimsd1),
|
||||
chk("Suppress 2nd line (DIMSD2)", vals.dimsd2, DsField::Dimsd2),
|
||||
text("Extension Line").size(11).color(ACCENT),
|
||||
row![lbl("Extension (DIMEXE)"), mk_field(DsField::Dimexe, vals.dimexe)].spacing(8).align_y(iced::Center),
|
||||
row![lbl("Offset (DIMEXO)"), mk_field(DsField::Dimexo, vals.dimexo)].spacing(8).align_y(iced::Center),
|
||||
row![
|
||||
lbl("Extension (DIMEXE)"),
|
||||
mk_field(DsField::Dimexe, vals.dimexe)
|
||||
]
|
||||
.spacing(8)
|
||||
.align_y(iced::Center),
|
||||
row![
|
||||
lbl("Offset (DIMEXO)"),
|
||||
mk_field(DsField::Dimexo, vals.dimexo)
|
||||
]
|
||||
.spacing(8)
|
||||
.align_y(iced::Center),
|
||||
chk("Suppress 1st line (DIMSE1)", vals.dimse1, DsField::Dimse1),
|
||||
chk("Suppress 2nd line (DIMSE2)", vals.dimse2, DsField::Dimse2),
|
||||
].spacing(7).into(),
|
||||
]
|
||||
.spacing(7)
|
||||
.into(),
|
||||
1 => column![
|
||||
text("Arrows").size(11).color(ACCENT),
|
||||
row![lbl("Arrow size (DIMASZ)"), mk_field(DsField::Dimasz, vals.dimasz)].spacing(8).align_y(iced::Center),
|
||||
row![lbl("Center mark (DIMCEN)"), mk_field(DsField::Dimcen, vals.dimcen)].spacing(8).align_y(iced::Center),
|
||||
row![lbl("Tick size (DIMTSZ)"), mk_field(DsField::Dimtsz, vals.dimtsz)].spacing(8).align_y(iced::Center),
|
||||
].spacing(7).into(),
|
||||
row![
|
||||
lbl("Arrow size (DIMASZ)"),
|
||||
mk_field(DsField::Dimasz, vals.dimasz)
|
||||
]
|
||||
.spacing(8)
|
||||
.align_y(iced::Center),
|
||||
row![
|
||||
lbl("Center mark (DIMCEN)"),
|
||||
mk_field(DsField::Dimcen, vals.dimcen)
|
||||
]
|
||||
.spacing(8)
|
||||
.align_y(iced::Center),
|
||||
row![
|
||||
lbl("Tick size (DIMTSZ)"),
|
||||
mk_field(DsField::Dimtsz, vals.dimtsz)
|
||||
]
|
||||
.spacing(8)
|
||||
.align_y(iced::Center),
|
||||
]
|
||||
.spacing(7)
|
||||
.into(),
|
||||
2 => column![
|
||||
text("Text").size(11).color(ACCENT),
|
||||
row![lbl("Height (DIMTXT)"), mk_field(DsField::Dimtxt, vals.dimtxt)].spacing(8).align_y(iced::Center),
|
||||
row![lbl("Style (DIMTXSTY)"), mk_field(DsField::Dimtxsty, vals.dimtxsty)].spacing(8).align_y(iced::Center),
|
||||
row![lbl("Vertical pos (DIMTAD)"), mk_field(DsField::Dimtad, vals.dimtad)].spacing(8).align_y(iced::Center),
|
||||
row![
|
||||
lbl("Height (DIMTXT)"),
|
||||
mk_field(DsField::Dimtxt, vals.dimtxt)
|
||||
]
|
||||
.spacing(8)
|
||||
.align_y(iced::Center),
|
||||
row![
|
||||
lbl("Style (DIMTXSTY)"),
|
||||
mk_field(DsField::Dimtxsty, vals.dimtxsty)
|
||||
]
|
||||
.spacing(8)
|
||||
.align_y(iced::Center),
|
||||
row![
|
||||
lbl("Vertical pos (DIMTAD)"),
|
||||
mk_field(DsField::Dimtad, vals.dimtad)
|
||||
]
|
||||
.spacing(8)
|
||||
.align_y(iced::Center),
|
||||
chk("Horizontal inside (DIMTIH)", vals.dimtih, DsField::Dimtih),
|
||||
chk("Horizontal outside (DIMTOH)", vals.dimtoh, DsField::Dimtoh),
|
||||
].spacing(7).into(),
|
||||
]
|
||||
.spacing(7)
|
||||
.into(),
|
||||
3 => column![
|
||||
text("Scale").size(11).color(ACCENT),
|
||||
row![lbl("Overall scale (DIMSCALE)"), mk_field(DsField::Dimscale, vals.dimscale)].spacing(8).align_y(iced::Center),
|
||||
row![lbl("Linear factor (DIMLFAC)"), mk_field(DsField::Dimlfac, vals.dimlfac)].spacing(8).align_y(iced::Center),
|
||||
row![
|
||||
lbl("Overall scale (DIMSCALE)"),
|
||||
mk_field(DsField::Dimscale, vals.dimscale)
|
||||
]
|
||||
.spacing(8)
|
||||
.align_y(iced::Center),
|
||||
row![
|
||||
lbl("Linear factor (DIMLFAC)"),
|
||||
mk_field(DsField::Dimlfac, vals.dimlfac)
|
||||
]
|
||||
.spacing(8)
|
||||
.align_y(iced::Center),
|
||||
text("Units").size(11).color(ACCENT),
|
||||
row![lbl("Format (DIMLUNIT)"), mk_field(DsField::Dimlunit, vals.dimlunit)].spacing(8).align_y(iced::Center),
|
||||
row![lbl("Decimals (DIMDEC)"), mk_field(DsField::Dimdec, vals.dimdec)].spacing(8).align_y(iced::Center),
|
||||
row![lbl("Suffix (DIMPOST)"), mk_field(DsField::Dimpost, vals.dimpost)].spacing(8).align_y(iced::Center),
|
||||
].spacing(7).into(),
|
||||
row![
|
||||
lbl("Format (DIMLUNIT)"),
|
||||
mk_field(DsField::Dimlunit, vals.dimlunit)
|
||||
]
|
||||
.spacing(8)
|
||||
.align_y(iced::Center),
|
||||
row![
|
||||
lbl("Decimals (DIMDEC)"),
|
||||
mk_field(DsField::Dimdec, vals.dimdec)
|
||||
]
|
||||
.spacing(8)
|
||||
.align_y(iced::Center),
|
||||
row![
|
||||
lbl("Suffix (DIMPOST)"),
|
||||
mk_field(DsField::Dimpost, vals.dimpost)
|
||||
]
|
||||
.spacing(8)
|
||||
.align_y(iced::Center),
|
||||
]
|
||||
.spacing(7)
|
||||
.into(),
|
||||
_ => column![
|
||||
text("Tolerances").size(11).color(ACCENT),
|
||||
chk("Generate tolerances (DIMTOL)", vals.dimtol, DsField::Dimtol),
|
||||
chk("Limits generation (DIMLIM)", vals.dimlim, DsField::Dimlim),
|
||||
row![lbl("Plus tolerance (DIMTP)"), mk_field(DsField::Dimtp, vals.dimtp)].spacing(8).align_y(iced::Center),
|
||||
row![lbl("Minus tolerance (DIMTM)"), mk_field(DsField::Dimtm, vals.dimtm)].spacing(8).align_y(iced::Center),
|
||||
row![lbl("Tol. decimals (DIMTDEC)"), mk_field(DsField::Dimtdec, vals.dimtdec)].spacing(8).align_y(iced::Center),
|
||||
row![lbl("Tol. scale (DIMTFAC)"), mk_field(DsField::Dimtfac, vals.dimtfac)].spacing(8).align_y(iced::Center),
|
||||
].spacing(7).into(),
|
||||
chk("Limits generation (DIMLIM)", vals.dimlim, DsField::Dimlim),
|
||||
row![
|
||||
lbl("Plus tolerance (DIMTP)"),
|
||||
mk_field(DsField::Dimtp, vals.dimtp)
|
||||
]
|
||||
.spacing(8)
|
||||
.align_y(iced::Center),
|
||||
row![
|
||||
lbl("Minus tolerance (DIMTM)"),
|
||||
mk_field(DsField::Dimtm, vals.dimtm)
|
||||
]
|
||||
.spacing(8)
|
||||
.align_y(iced::Center),
|
||||
row![
|
||||
lbl("Tol. decimals (DIMTDEC)"),
|
||||
mk_field(DsField::Dimtdec, vals.dimtdec)
|
||||
]
|
||||
.spacing(8)
|
||||
.align_y(iced::Center),
|
||||
row![
|
||||
lbl("Tol. scale (DIMTFAC)"),
|
||||
mk_field(DsField::Dimtfac, vals.dimtfac)
|
||||
]
|
||||
.spacing(8)
|
||||
.align_y(iced::Center),
|
||||
]
|
||||
.spacing(7)
|
||||
.into(),
|
||||
};
|
||||
|
||||
// ── Right panel: tabs + scrollable content ────────────────────────────
|
||||
|
|
@ -229,20 +495,24 @@ pub fn view_window<'a>(
|
|||
text(format!("Editing: {selected}")).size(11).color(DIM),
|
||||
tabs,
|
||||
hdivider(),
|
||||
scrollable(
|
||||
container(tab_content).padding([12, 12])
|
||||
).height(Fill),
|
||||
scrollable(container(tab_content).padding([12, 12])).height(Fill),
|
||||
]
|
||||
.spacing(6)
|
||||
.height(Fill)
|
||||
.height(Fill),
|
||||
)
|
||||
.height(Fill)
|
||||
.width(Fill)
|
||||
.padding(iced::Padding { top: 12.0, right: 12.0, bottom: 12.0, left: 0.0 });
|
||||
.padding(iced::Padding {
|
||||
top: 12.0,
|
||||
right: 12.0,
|
||||
bottom: 12.0,
|
||||
left: 0.0,
|
||||
});
|
||||
|
||||
// ── Vertical separator ────────────────────────────────────────────────
|
||||
let vsep = container(Space::new().width(1).height(Fill))
|
||||
.width(1).height(Fill)
|
||||
.width(1)
|
||||
.height(Fill)
|
||||
.style(|_: &Theme| container::Style {
|
||||
background: Some(Background::Color(BORDER)),
|
||||
..Default::default()
|
||||
|
|
@ -250,14 +520,12 @@ pub fn view_window<'a>(
|
|||
|
||||
let body = row![style_list, vsep, right_panel].height(Fill);
|
||||
|
||||
container(
|
||||
column![toolbar, hdivider(), body].spacing(0)
|
||||
)
|
||||
.style(|_: &Theme| container::Style {
|
||||
background: Some(Background::Color(BG)),
|
||||
..Default::default()
|
||||
})
|
||||
.width(Fill)
|
||||
.height(Fill)
|
||||
.into()
|
||||
container(column![toolbar, hdivider(), body].spacing(0))
|
||||
.style(|_: &Theme| container::Style {
|
||||
background: Some(Background::Color(BG)),
|
||||
..Default::default()
|
||||
})
|
||||
.width(Fill)
|
||||
.height(Fill)
|
||||
.into()
|
||||
}
|
||||
|
|
|
|||
462
src/ui/layers.rs
462
src/ui/layers.rs
|
|
@ -1,7 +1,8 @@
|
|||
//! Layer Manager — floating window.
|
||||
|
||||
use crate::ui::properties::{color_picker_dropdown, LinetypeItem, LwItem, lw_options};
|
||||
use crate::app::Message;
|
||||
use crate::ui::properties::{color_picker_dropdown, lw_options, LinetypeItem, LwItem};
|
||||
use crate::ui::ROW_H;
|
||||
use acadrust::tables::layer::Layer as DocLayer;
|
||||
use acadrust::tables::Table;
|
||||
use acadrust::types::aci_table::aci_to_rgb;
|
||||
|
|
@ -10,9 +11,8 @@ use acadrust::Handle;
|
|||
use iced::widget::{
|
||||
button, column, combo_box, container, mouse_area, row, scrollable, text, text_input,
|
||||
};
|
||||
use iced::{Background, Border, Color, Element, Fill, Length, Theme};
|
||||
use iced::Padding;
|
||||
use crate::ui::ROW_H;
|
||||
use iced::{Background, Border, Color, Element, Fill, Length, Theme};
|
||||
|
||||
// ── Per-viewport column descriptor ───────────────────────────────────────────
|
||||
|
||||
|
|
@ -24,9 +24,9 @@ pub struct VpCol {
|
|||
|
||||
// ── Row-height-derived constants ─────────────────────────────────────────
|
||||
/// SVG icon size inside a layer-table cell.
|
||||
const ICON_SZ: f32 = ROW_H * 0.62; // ≈16 px at ROW_H=26
|
||||
const ICON_SZ: f32 = ROW_H * 0.62; // ≈16 px at ROW_H=26
|
||||
/// Font size for cell text.
|
||||
const FONT_SZ: f32 = ROW_H * 0.42; // ≈11 px at ROW_H=26
|
||||
const FONT_SZ: f32 = ROW_H * 0.42; // ≈11 px at ROW_H=26
|
||||
/// Vertical padding for combo_box / text_input so their total height = ROW_H.
|
||||
const COMBO_PAD_V: f32 = (ROW_H - FONT_SZ * 1.3 - 2.0) / 2.0;
|
||||
|
||||
|
|
@ -91,12 +91,16 @@ impl Default for LayerPanel {
|
|||
editing: None,
|
||||
edit_buf: String::new(),
|
||||
current_layer: "0".to_string(),
|
||||
linetype_items: vec![LinetypeItem { name: "Continuous".into(), art: String::new() }],
|
||||
linetype_items: vec![LinetypeItem {
|
||||
name: "Continuous".into(),
|
||||
art: String::new(),
|
||||
}],
|
||||
color_picker_row: None,
|
||||
color_full_palette: false,
|
||||
linetype_combo: combo_box::State::new(vec![
|
||||
LinetypeItem { name: "Continuous".into(), art: String::new() }
|
||||
]),
|
||||
linetype_combo: combo_box::State::new(vec![LinetypeItem {
|
||||
name: "Continuous".into(),
|
||||
art: String::new(),
|
||||
}]),
|
||||
lw_combo: combo_box::State::new(lw_options()),
|
||||
vp_cols: vec![],
|
||||
}
|
||||
|
|
@ -113,7 +117,10 @@ impl LayerPanel {
|
|||
) {
|
||||
self.vp_cols = vp_info
|
||||
.iter()
|
||||
.map(|(h, label, _)| VpCol { handle: *h, label: label.clone() })
|
||||
.map(|(h, label, _)| VpCol {
|
||||
handle: *h,
|
||||
label: label.clone(),
|
||||
})
|
||||
.collect();
|
||||
|
||||
self.layers = doc_layers
|
||||
|
|
@ -179,14 +186,38 @@ impl LayerPanel {
|
|||
// ── Column header ─────────────────────────────────────────────────
|
||||
let mut header_row = row![
|
||||
text("Status").size(10).color(DIM).width(50),
|
||||
text("Name").size(10).color(DIM).width(Length::Fixed(COL_NAME)),
|
||||
text("On").size(10).color(DIM).width(Length::Fixed(COL_ICON)),
|
||||
text("Freeze").size(10).color(DIM).width(Length::Fixed(COL_ICON)),
|
||||
text("Lock").size(10).color(DIM).width(Length::Fixed(COL_ICON)),
|
||||
text("Color").size(10).color(DIM).width(Length::Fixed(COL_COLOR)),
|
||||
text("Linetype").size(10).color(DIM).width(Length::Fixed(COL_LT)),
|
||||
text("Lineweight").size(10).color(DIM).width(Length::Fixed(COL_LW)),
|
||||
text("Transparency").size(10).color(DIM).width(Length::Fixed(COL_TRANS)),
|
||||
text("Name")
|
||||
.size(10)
|
||||
.color(DIM)
|
||||
.width(Length::Fixed(COL_NAME)),
|
||||
text("On")
|
||||
.size(10)
|
||||
.color(DIM)
|
||||
.width(Length::Fixed(COL_ICON)),
|
||||
text("Freeze")
|
||||
.size(10)
|
||||
.color(DIM)
|
||||
.width(Length::Fixed(COL_ICON)),
|
||||
text("Lock")
|
||||
.size(10)
|
||||
.color(DIM)
|
||||
.width(Length::Fixed(COL_ICON)),
|
||||
text("Color")
|
||||
.size(10)
|
||||
.color(DIM)
|
||||
.width(Length::Fixed(COL_COLOR)),
|
||||
text("Linetype")
|
||||
.size(10)
|
||||
.color(DIM)
|
||||
.width(Length::Fixed(COL_LT)),
|
||||
text("Lineweight")
|
||||
.size(10)
|
||||
.color(DIM)
|
||||
.width(Length::Fixed(COL_LW)),
|
||||
text("Transparency")
|
||||
.size(10)
|
||||
.color(DIM)
|
||||
.width(Length::Fixed(COL_TRANS)),
|
||||
]
|
||||
.spacing(4)
|
||||
.align_y(iced::Center);
|
||||
|
|
@ -203,7 +234,11 @@ impl LayerPanel {
|
|||
let col_header = container(header_row)
|
||||
.style(|_: &Theme| container::Style {
|
||||
background: Some(Background::Color(COL_HEADER_BG)),
|
||||
border: Border { color: BORDER_COLOR, width: 1.0, radius: 0.0.into() },
|
||||
border: Border {
|
||||
color: BORDER_COLOR,
|
||||
width: 1.0,
|
||||
radius: 0.0.into(),
|
||||
},
|
||||
..Default::default()
|
||||
})
|
||||
.padding([4, 8])
|
||||
|
|
@ -224,7 +259,15 @@ impl LayerPanel {
|
|||
};
|
||||
|
||||
rows_col = rows_col.push(layer_row(
|
||||
i, layer, is_sel, is_current, is_editing, &self.edit_buf, color_open, ltc, lwc,
|
||||
i,
|
||||
layer,
|
||||
is_sel,
|
||||
is_current,
|
||||
is_editing,
|
||||
&self.edit_buf,
|
||||
color_open,
|
||||
ltc,
|
||||
lwc,
|
||||
&self.vp_cols,
|
||||
));
|
||||
|
||||
|
|
@ -255,11 +298,30 @@ fn toolbar_btn(label: &str, msg: Message) -> Element<'_, Message> {
|
|||
.on_press(msg)
|
||||
.style(|_: &Theme, status| button::Style {
|
||||
background: Some(Background::Color(match status {
|
||||
button::Status::Hovered => Color { r: 0.32, g: 0.32, b: 0.32, a: 1.0 },
|
||||
button::Status::Pressed => Color { r: 0.25, g: 0.25, b: 0.25, a: 1.0 },
|
||||
_ => Color { r: 0.26, g: 0.26, b: 0.26, a: 1.0 },
|
||||
button::Status::Hovered => Color {
|
||||
r: 0.32,
|
||||
g: 0.32,
|
||||
b: 0.32,
|
||||
a: 1.0,
|
||||
},
|
||||
button::Status::Pressed => Color {
|
||||
r: 0.25,
|
||||
g: 0.25,
|
||||
b: 0.25,
|
||||
a: 1.0,
|
||||
},
|
||||
_ => Color {
|
||||
r: 0.26,
|
||||
g: 0.26,
|
||||
b: 0.26,
|
||||
a: 1.0,
|
||||
},
|
||||
})),
|
||||
border: Border { radius: 3.0.into(), color: BORDER_COLOR, width: 1.0 },
|
||||
border: Border {
|
||||
radius: 3.0.into(),
|
||||
color: BORDER_COLOR,
|
||||
width: 1.0,
|
||||
},
|
||||
text_color: Color::WHITE,
|
||||
..Default::default()
|
||||
})
|
||||
|
|
@ -271,14 +333,33 @@ fn toolbar_btn_cond(label: &str, msg: Message, enabled: bool) -> Element<'_, Mes
|
|||
let mut b = button(text(label).size(11).color(if enabled {
|
||||
Color::WHITE
|
||||
} else {
|
||||
Color { r: 0.45, g: 0.45, b: 0.45, a: 1.0 }
|
||||
Color {
|
||||
r: 0.45,
|
||||
g: 0.45,
|
||||
b: 0.45,
|
||||
a: 1.0,
|
||||
}
|
||||
}))
|
||||
.style(|_: &Theme, status| button::Style {
|
||||
background: Some(Background::Color(match status {
|
||||
button::Status::Hovered => Color { r: 0.32, g: 0.32, b: 0.32, a: 1.0 },
|
||||
_ => Color { r: 0.26, g: 0.26, b: 0.26, a: 1.0 },
|
||||
button::Status::Hovered => Color {
|
||||
r: 0.32,
|
||||
g: 0.32,
|
||||
b: 0.32,
|
||||
a: 1.0,
|
||||
},
|
||||
_ => Color {
|
||||
r: 0.26,
|
||||
g: 0.26,
|
||||
b: 0.26,
|
||||
a: 1.0,
|
||||
},
|
||||
})),
|
||||
border: Border { radius: 3.0.into(), color: BORDER_COLOR, width: 1.0 },
|
||||
border: Border {
|
||||
radius: 3.0.into(),
|
||||
color: BORDER_COLOR,
|
||||
width: 1.0,
|
||||
},
|
||||
text_color: Color::WHITE,
|
||||
..Default::default()
|
||||
})
|
||||
|
|
@ -313,12 +394,22 @@ fn layer_row<'a>(
|
|||
.on_press(on_press)
|
||||
.style(|_: &Theme, status| button::Style {
|
||||
background: Some(Background::Color(match status {
|
||||
button::Status::Hovered => Color { r: 0.35, g: 0.35, b: 0.35, a: 1.0 },
|
||||
button::Status::Hovered => Color {
|
||||
r: 0.35,
|
||||
g: 0.35,
|
||||
b: 0.35,
|
||||
a: 1.0,
|
||||
},
|
||||
_ => Color::TRANSPARENT,
|
||||
})),
|
||||
..Default::default()
|
||||
})
|
||||
.padding(Padding { top: COMBO_PAD_V, bottom: COMBO_PAD_V, left: 4.0, right: 4.0 })
|
||||
.padding(Padding {
|
||||
top: COMBO_PAD_V,
|
||||
bottom: COMBO_PAD_V,
|
||||
left: 4.0,
|
||||
right: 4.0,
|
||||
})
|
||||
.height(Length::Fixed(ROW_H))
|
||||
.into()
|
||||
};
|
||||
|
|
@ -340,9 +431,25 @@ fn layer_row<'a>(
|
|||
};
|
||||
|
||||
let status_dot: Element<'_, Message> = if is_current {
|
||||
text("✓").size(13).color(Color { r: 0.25, g: 0.85, b: 0.45, a: 1.0 }).into()
|
||||
text("✓")
|
||||
.size(13)
|
||||
.color(Color {
|
||||
r: 0.25,
|
||||
g: 0.85,
|
||||
b: 0.45,
|
||||
a: 1.0,
|
||||
})
|
||||
.into()
|
||||
} else {
|
||||
text("▣").size(11).color(Color { r: 0.55, g: 0.55, b: 0.55, a: 1.0 }).into()
|
||||
text("▣")
|
||||
.size(11)
|
||||
.color(Color {
|
||||
r: 0.55,
|
||||
g: 0.55,
|
||||
b: 0.55,
|
||||
a: 1.0,
|
||||
})
|
||||
.into()
|
||||
};
|
||||
|
||||
// Name cell
|
||||
|
|
@ -351,18 +458,43 @@ fn layer_row<'a>(
|
|||
.on_input(Message::LayerRenameEdit)
|
||||
.on_submit(Message::LayerRenameCommit)
|
||||
.size(FONT_SZ)
|
||||
.padding(Padding { top: COMBO_PAD_V, bottom: COMBO_PAD_V, left: 4.0, right: 4.0 })
|
||||
.padding(Padding {
|
||||
top: COMBO_PAD_V,
|
||||
bottom: COMBO_PAD_V,
|
||||
left: 4.0,
|
||||
right: 4.0,
|
||||
})
|
||||
.style(|_: &Theme, _| iced::widget::text_input::Style {
|
||||
background: iced::Background::Color(Color { r: 0.12, g: 0.12, b: 0.12, a: 1.0 }),
|
||||
background: iced::Background::Color(Color {
|
||||
r: 0.12,
|
||||
g: 0.12,
|
||||
b: 0.12,
|
||||
a: 1.0,
|
||||
}),
|
||||
border: Border {
|
||||
radius: 2.0.into(),
|
||||
width: 1.0,
|
||||
color: Color { r: 0.45, g: 0.65, b: 0.90, a: 1.0 },
|
||||
color: Color {
|
||||
r: 0.45,
|
||||
g: 0.65,
|
||||
b: 0.90,
|
||||
a: 1.0,
|
||||
},
|
||||
},
|
||||
icon: Color::WHITE,
|
||||
placeholder: Color { r: 0.4, g: 0.4, b: 0.4, a: 1.0 },
|
||||
placeholder: Color {
|
||||
r: 0.4,
|
||||
g: 0.4,
|
||||
b: 0.4,
|
||||
a: 1.0,
|
||||
},
|
||||
value: Color::WHITE,
|
||||
selection: Color { r: 0.25, g: 0.45, b: 0.75, a: 0.5 },
|
||||
selection: Color {
|
||||
r: 0.25,
|
||||
g: 0.45,
|
||||
b: 0.75,
|
||||
a: 0.5,
|
||||
},
|
||||
})
|
||||
.width(Length::Fixed(COL_NAME))
|
||||
.into()
|
||||
|
|
@ -371,12 +503,22 @@ fn layer_row<'a>(
|
|||
.on_press(Message::LayerRenameStart(index))
|
||||
.style(|_: &Theme, status| button::Style {
|
||||
background: Some(Background::Color(match status {
|
||||
button::Status::Hovered => Color { r: 0.30, g: 0.30, b: 0.30, a: 1.0 },
|
||||
button::Status::Hovered => Color {
|
||||
r: 0.30,
|
||||
g: 0.30,
|
||||
b: 0.30,
|
||||
a: 1.0,
|
||||
},
|
||||
_ => Color::TRANSPARENT,
|
||||
})),
|
||||
..Default::default()
|
||||
})
|
||||
.padding(Padding { top: COMBO_PAD_V, bottom: COMBO_PAD_V, left: 4.0, right: 4.0 })
|
||||
.padding(Padding {
|
||||
top: COMBO_PAD_V,
|
||||
bottom: COMBO_PAD_V,
|
||||
left: 4.0,
|
||||
right: 4.0,
|
||||
})
|
||||
.height(Length::Fixed(ROW_H))
|
||||
.width(Length::Fixed(COL_NAME))
|
||||
.into()
|
||||
|
|
@ -391,7 +533,12 @@ fn layer_row<'a>(
|
|||
.style(move |_: &Theme| container::Style {
|
||||
background: Some(Background::Color(layer_color)),
|
||||
border: Border {
|
||||
color: Color { r: 0.0, g: 0.0, b: 0.0, a: 0.5 },
|
||||
color: Color {
|
||||
r: 0.0,
|
||||
g: 0.0,
|
||||
b: 0.0,
|
||||
a: 0.5,
|
||||
},
|
||||
width: 1.0,
|
||||
radius: 1.0.into(),
|
||||
},
|
||||
|
|
@ -415,21 +562,41 @@ fn layer_row<'a>(
|
|||
.on_press(Message::LayerColorPickerToggle(index))
|
||||
.style(move |_: &Theme, status| button::Style {
|
||||
background: Some(Background::Color(match status {
|
||||
button::Status::Hovered => Color { r: 0.20, g: 0.20, b: 0.20, a: 1.0 },
|
||||
_ => Color { r: 0.13, g: 0.13, b: 0.13, a: 1.0 },
|
||||
button::Status::Hovered => Color {
|
||||
r: 0.20,
|
||||
g: 0.20,
|
||||
b: 0.20,
|
||||
a: 1.0,
|
||||
},
|
||||
_ => Color {
|
||||
r: 0.13,
|
||||
g: 0.13,
|
||||
b: 0.13,
|
||||
a: 1.0,
|
||||
},
|
||||
})),
|
||||
border: Border {
|
||||
radius: 2.0.into(),
|
||||
width: 1.0,
|
||||
color: if color_picker_open {
|
||||
Color { r: 0.45, g: 0.65, b: 0.90, a: 1.0 }
|
||||
Color {
|
||||
r: 0.45,
|
||||
g: 0.65,
|
||||
b: 0.90,
|
||||
a: 1.0,
|
||||
}
|
||||
} else {
|
||||
BORDER_COLOR
|
||||
},
|
||||
},
|
||||
..Default::default()
|
||||
})
|
||||
.padding(Padding { top: COMBO_PAD_V, bottom: COMBO_PAD_V, left: 4.0, right: 4.0 })
|
||||
.padding(Padding {
|
||||
top: COMBO_PAD_V,
|
||||
bottom: COMBO_PAD_V,
|
||||
left: 4.0,
|
||||
right: 4.0,
|
||||
})
|
||||
.height(Length::Fixed(ROW_H))
|
||||
.width(Length::Fixed(COL_COLOR))
|
||||
.into();
|
||||
|
|
@ -447,12 +614,21 @@ fn layer_row<'a>(
|
|||
|item: LinetypeItem| Message::LayerLinetypeSet(item.name),
|
||||
)
|
||||
.size(FONT_SZ)
|
||||
.padding(Padding { top: COMBO_PAD_V, bottom: COMBO_PAD_V, left: 4.0, right: 4.0 })
|
||||
.padding(Padding {
|
||||
top: COMBO_PAD_V,
|
||||
bottom: COMBO_PAD_V,
|
||||
left: 4.0,
|
||||
right: 4.0,
|
||||
})
|
||||
.width(Length::Fixed(COL_LT))
|
||||
.input_style(combo_input_style)
|
||||
.into()
|
||||
} else {
|
||||
text(layer.linetype.as_str()).size(FONT_SZ).color(DIM).width(Length::Fixed(COL_LT)).into()
|
||||
text(layer.linetype.as_str())
|
||||
.size(FONT_SZ)
|
||||
.color(DIM)
|
||||
.width(Length::Fixed(COL_LT))
|
||||
.into()
|
||||
};
|
||||
|
||||
// Lineweight cell
|
||||
|
|
@ -462,12 +638,21 @@ fn layer_row<'a>(
|
|||
Message::LayerLineweightSet(item.0)
|
||||
})
|
||||
.size(FONT_SZ)
|
||||
.padding(Padding { top: COMBO_PAD_V, bottom: COMBO_PAD_V, left: 4.0, right: 4.0 })
|
||||
.padding(Padding {
|
||||
top: COMBO_PAD_V,
|
||||
bottom: COMBO_PAD_V,
|
||||
left: 4.0,
|
||||
right: 4.0,
|
||||
})
|
||||
.width(Length::Fixed(COL_LW))
|
||||
.input_style(combo_input_style)
|
||||
.into()
|
||||
} else {
|
||||
text(cur_lw_item.to_string()).size(FONT_SZ).color(DIM).width(Length::Fixed(COL_LW)).into()
|
||||
text(cur_lw_item.to_string())
|
||||
.size(FONT_SZ)
|
||||
.color(DIM)
|
||||
.width(Length::Fixed(COL_LW))
|
||||
.into()
|
||||
};
|
||||
|
||||
// Transparency cell
|
||||
|
|
@ -475,21 +660,44 @@ fn layer_row<'a>(
|
|||
let trans_cell = text_input("0", &trans_str)
|
||||
.on_input(move |s| Message::LayerTransparencyEdit(index, s))
|
||||
.size(FONT_SZ)
|
||||
.padding(Padding { top: COMBO_PAD_V, bottom: COMBO_PAD_V, left: 4.0, right: 4.0 })
|
||||
.padding(Padding {
|
||||
top: COMBO_PAD_V,
|
||||
bottom: COMBO_PAD_V,
|
||||
left: 4.0,
|
||||
right: 4.0,
|
||||
})
|
||||
.style(|_: &Theme, _| iced::widget::text_input::Style {
|
||||
background: iced::Background::Color(Color::TRANSPARENT),
|
||||
border: Border { radius: 2.0.into(), width: 1.0, color: BORDER_COLOR },
|
||||
border: Border {
|
||||
radius: 2.0.into(),
|
||||
width: 1.0,
|
||||
color: BORDER_COLOR,
|
||||
},
|
||||
icon: Color::WHITE,
|
||||
placeholder: DIM,
|
||||
value: ROW_TEXT,
|
||||
selection: Color { r: 0.25, g: 0.45, b: 0.75, a: 0.5 },
|
||||
selection: Color {
|
||||
r: 0.25,
|
||||
g: 0.45,
|
||||
b: 0.75,
|
||||
a: 0.5,
|
||||
},
|
||||
})
|
||||
.width(Length::Fixed(COL_TRANS));
|
||||
|
||||
let bg = if is_selected { ROW_SEL } else if index % 2 == 0 { ROW_EVEN } else { ROW_ODD };
|
||||
let bg = if is_selected {
|
||||
ROW_SEL
|
||||
} else if index % 2 == 0 {
|
||||
ROW_EVEN
|
||||
} else {
|
||||
ROW_ODD
|
||||
};
|
||||
|
||||
let mut row_content = row![
|
||||
container(status_dot).width(50).align_x(iced::Center).align_y(iced::Center),
|
||||
container(status_dot)
|
||||
.width(50)
|
||||
.align_x(iced::Center)
|
||||
.align_y(iced::Center),
|
||||
name_cell,
|
||||
container(svg_btn(vis_svg, Message::LayerToggleVisible(index)))
|
||||
.width(Length::Fixed(COL_ICON))
|
||||
|
|
@ -517,9 +725,12 @@ fn layer_row<'a>(
|
|||
include_bytes!("../../assets/icons/layers/laythw.svg")
|
||||
};
|
||||
row_content = row_content.push(
|
||||
container(svg_btn(vp_frz_svg, Message::LayerToggleVpFreeze(index, vp_idx)))
|
||||
.width(Length::Fixed(COL_ICON))
|
||||
.align_x(iced::Center),
|
||||
container(svg_btn(
|
||||
vp_frz_svg,
|
||||
Message::LayerToggleVpFreeze(index, vp_idx),
|
||||
))
|
||||
.width(Length::Fixed(COL_ICON))
|
||||
.align_x(iced::Center),
|
||||
);
|
||||
}
|
||||
|
||||
|
|
@ -529,7 +740,12 @@ fn layer_row<'a>(
|
|||
background: Some(Background::Color(bg)),
|
||||
..Default::default()
|
||||
})
|
||||
.padding(Padding { top: 0.0, bottom: 0.0, left: 8.0, right: 8.0 })
|
||||
.padding(Padding {
|
||||
top: 0.0,
|
||||
bottom: 0.0,
|
||||
left: 8.0,
|
||||
right: 8.0,
|
||||
})
|
||||
.height(Length::Fixed(ROW_H))
|
||||
.width(Fill),
|
||||
)
|
||||
|
|
@ -539,16 +755,31 @@ fn layer_row<'a>(
|
|||
|
||||
// ── Combo style ────────────────────────────────────────────────────────────
|
||||
|
||||
fn combo_input_style(_theme: &Theme, _status: iced::widget::text_input::Status)
|
||||
-> iced::widget::text_input::Style
|
||||
{
|
||||
fn combo_input_style(
|
||||
_theme: &Theme,
|
||||
_status: iced::widget::text_input::Status,
|
||||
) -> iced::widget::text_input::Style {
|
||||
iced::widget::text_input::Style {
|
||||
background: iced::Background::Color(Color { r: 0.13, g: 0.13, b: 0.13, a: 1.0 }),
|
||||
border: Border { radius: 2.0.into(), width: 1.0, color: BORDER_COLOR },
|
||||
background: iced::Background::Color(Color {
|
||||
r: 0.13,
|
||||
g: 0.13,
|
||||
b: 0.13,
|
||||
a: 1.0,
|
||||
}),
|
||||
border: Border {
|
||||
radius: 2.0.into(),
|
||||
width: 1.0,
|
||||
color: BORDER_COLOR,
|
||||
},
|
||||
icon: Color::WHITE,
|
||||
placeholder: DIM,
|
||||
value: Color::WHITE,
|
||||
selection: Color { r: 0.25, g: 0.45, b: 0.75, a: 0.5 },
|
||||
selection: Color {
|
||||
r: 0.25,
|
||||
g: 0.45,
|
||||
b: 0.75,
|
||||
a: 0.5,
|
||||
},
|
||||
}
|
||||
}
|
||||
|
||||
|
|
@ -556,32 +787,26 @@ fn combo_input_style(_theme: &Theme, _status: iced::widget::text_input::Status)
|
|||
|
||||
fn color_picker_palette<'a>(full: bool) -> Element<'a, Message> {
|
||||
// Horizontal offset to align dropdown under the Color column.
|
||||
const LEFT_OFFSET: f32 = 20.0 + 4.0 + COL_NAME + 4.0 + COL_ICON + 4.0
|
||||
+ COL_ICON + 4.0 + COL_ICON + 4.0 + 8.0;
|
||||
const LEFT_OFFSET: f32 =
|
||||
20.0 + 4.0 + COL_NAME + 4.0 + COL_ICON + 4.0 + COL_ICON + 4.0 + COL_ICON + 4.0 + 8.0;
|
||||
|
||||
// Use the shared color_picker_dropdown from properties — no ByLayer/ByBlock for layers.
|
||||
let dropdown = color_picker_dropdown(
|
||||
full,
|
||||
Message::LayerColorMorePalette,
|
||||
None,
|
||||
None,
|
||||
|aci| Message::LayerColorSet(aci),
|
||||
);
|
||||
let dropdown = color_picker_dropdown(full, Message::LayerColorMorePalette, None, None, |aci| {
|
||||
Message::LayerColorSet(aci)
|
||||
});
|
||||
|
||||
row![
|
||||
iced::widget::Space::new().width(LEFT_OFFSET),
|
||||
dropdown,
|
||||
]
|
||||
.into()
|
||||
row![iced::widget::Space::new().width(LEFT_OFFSET), dropdown,].into()
|
||||
}
|
||||
|
||||
|
||||
// ── Display helpers ───────────────────────────────────────────────────────
|
||||
|
||||
#[allow(dead_code)]
|
||||
fn aci_color_display(i: u8) -> (Color, &'static str) {
|
||||
let (r, g, b) = aci_to_rgb(i).unwrap_or((200, 200, 200));
|
||||
(Color::from_rgb(r as f32 / 255.0, g as f32 / 255.0, b as f32 / 255.0), "")
|
||||
(
|
||||
Color::from_rgb(r as f32 / 255.0, g as f32 / 255.0, b as f32 / 255.0),
|
||||
"",
|
||||
)
|
||||
}
|
||||
|
||||
fn iced_to_aci(c: Color) -> u8 {
|
||||
|
|
@ -629,7 +854,6 @@ fn color_name_to_aci(name: &str) -> u8 {
|
|||
}
|
||||
}
|
||||
|
||||
|
||||
pub fn iced_color_from_acad(c: &AcadColor) -> Color {
|
||||
match c {
|
||||
AcadColor::Index(i) => {
|
||||
|
|
@ -645,23 +869,73 @@ pub fn iced_color_from_acad(c: &AcadColor) -> Color {
|
|||
|
||||
// ── Column widths ─────────────────────────────────────────────────────────
|
||||
|
||||
const COL_NAME: f32 = 130.0;
|
||||
const COL_ICON: f32 = 44.0;
|
||||
const COL_NAME: f32 = 130.0;
|
||||
const COL_ICON: f32 = 44.0;
|
||||
const COL_COLOR: f32 = 90.0;
|
||||
const COL_LT: f32 = 110.0;
|
||||
const COL_LW: f32 = 90.0;
|
||||
const COL_LT: f32 = 110.0;
|
||||
const COL_LW: f32 = 90.0;
|
||||
const COL_TRANS: f32 = 80.0;
|
||||
|
||||
// ── Colors ────────────────────────────────────────────────────────────────
|
||||
|
||||
const PANEL_BG: Color = Color { r: 0.18, g: 0.18, b: 0.18, a: 1.0 };
|
||||
const TOOLBAR_BG: Color = Color { r: 0.20, g: 0.20, b: 0.20, a: 1.0 };
|
||||
const COL_HEADER_BG: Color = Color { r: 0.21, g: 0.21, b: 0.21, a: 1.0 };
|
||||
const ROW_EVEN: Color = Color { r: 0.18, g: 0.18, b: 0.18, a: 1.0 };
|
||||
const ROW_ODD: Color = Color { r: 0.21, g: 0.21, b: 0.21, a: 1.0 };
|
||||
const ROW_SEL: Color = Color { r: 0.18, g: 0.32, b: 0.52, a: 1.0 };
|
||||
const ROW_TEXT: Color = Color { r: 0.85, g: 0.85, b: 0.85, a: 1.0 };
|
||||
const DIM: Color = Color { r: 0.50, g: 0.50, b: 0.50, a: 1.0 };
|
||||
const BORDER_COLOR: Color = Color { r: 0.30, g: 0.30, b: 0.30, a: 1.0 };
|
||||
const PANEL_BG: Color = Color {
|
||||
r: 0.18,
|
||||
g: 0.18,
|
||||
b: 0.18,
|
||||
a: 1.0,
|
||||
};
|
||||
const TOOLBAR_BG: Color = Color {
|
||||
r: 0.20,
|
||||
g: 0.20,
|
||||
b: 0.20,
|
||||
a: 1.0,
|
||||
};
|
||||
const COL_HEADER_BG: Color = Color {
|
||||
r: 0.21,
|
||||
g: 0.21,
|
||||
b: 0.21,
|
||||
a: 1.0,
|
||||
};
|
||||
const ROW_EVEN: Color = Color {
|
||||
r: 0.18,
|
||||
g: 0.18,
|
||||
b: 0.18,
|
||||
a: 1.0,
|
||||
};
|
||||
const ROW_ODD: Color = Color {
|
||||
r: 0.21,
|
||||
g: 0.21,
|
||||
b: 0.21,
|
||||
a: 1.0,
|
||||
};
|
||||
const ROW_SEL: Color = Color {
|
||||
r: 0.18,
|
||||
g: 0.32,
|
||||
b: 0.52,
|
||||
a: 1.0,
|
||||
};
|
||||
const ROW_TEXT: Color = Color {
|
||||
r: 0.85,
|
||||
g: 0.85,
|
||||
b: 0.85,
|
||||
a: 1.0,
|
||||
};
|
||||
const DIM: Color = Color {
|
||||
r: 0.50,
|
||||
g: 0.50,
|
||||
b: 0.50,
|
||||
a: 1.0,
|
||||
};
|
||||
const BORDER_COLOR: Color = Color {
|
||||
r: 0.30,
|
||||
g: 0.30,
|
||||
b: 0.30,
|
||||
a: 1.0,
|
||||
};
|
||||
#[allow(dead_code)]
|
||||
const ICON_COLOR: Color = Color { r: 0.80, g: 0.80, b: 0.80, a: 1.0 };
|
||||
const ICON_COLOR: Color = Color {
|
||||
r: 0.80,
|
||||
g: 0.80,
|
||||
b: 0.80,
|
||||
a: 1.0,
|
||||
};
|
||||
|
|
|
|||
|
|
@ -4,27 +4,96 @@ use crate::app::Message;
|
|||
use iced::widget::{button, column, container, row, scrollable, text, text_input, Space};
|
||||
use iced::{Background, Border, Color, Element, Fill, Theme};
|
||||
|
||||
const TB: Color = Color { r: 0.13, g: 0.13, b: 0.13, a: 1.0 };
|
||||
const BG: Color = Color { r: 0.15, g: 0.15, b: 0.15, a: 1.0 };
|
||||
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 };
|
||||
const ACTIVE: Color = Color { r: 0.20, g: 0.40, b: 0.70, a: 1.0 };
|
||||
const FIELD: Color = Color { r: 0.10, g: 0.10, b: 0.10, a: 1.0 };
|
||||
const LIST: Color = Color { r: 0.12, g: 0.12, b: 0.12, a: 1.0 };
|
||||
const WARN: Color = Color { r: 0.80, g: 0.35, b: 0.25, a: 1.0 };
|
||||
const TB: Color = Color {
|
||||
r: 0.13,
|
||||
g: 0.13,
|
||||
b: 0.13,
|
||||
a: 1.0,
|
||||
};
|
||||
const BG: Color = Color {
|
||||
r: 0.15,
|
||||
g: 0.15,
|
||||
b: 0.15,
|
||||
a: 1.0,
|
||||
};
|
||||
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,
|
||||
};
|
||||
const ACTIVE: Color = Color {
|
||||
r: 0.20,
|
||||
g: 0.40,
|
||||
b: 0.70,
|
||||
a: 1.0,
|
||||
};
|
||||
const FIELD: Color = Color {
|
||||
r: 0.10,
|
||||
g: 0.10,
|
||||
b: 0.10,
|
||||
a: 1.0,
|
||||
};
|
||||
const LIST: Color = Color {
|
||||
r: 0.12,
|
||||
g: 0.12,
|
||||
b: 0.12,
|
||||
a: 1.0,
|
||||
};
|
||||
const WARN: Color = Color {
|
||||
r: 0.80,
|
||||
g: 0.35,
|
||||
b: 0.25,
|
||||
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 },
|
||||
(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() },
|
||||
border: Border {
|
||||
color: BORDER,
|
||||
width: 1.0,
|
||||
radius: 4.0.into(),
|
||||
},
|
||||
..Default::default()
|
||||
}
|
||||
}
|
||||
|
|
@ -32,8 +101,13 @@ fn btn_s(accent: bool) -> impl Fn(&Theme, button::Status) -> button::Style {
|
|||
fn list_item(active: bool) -> impl Fn(&Theme, button::Status) -> button::Style {
|
||||
move |_: &Theme, st| button::Style {
|
||||
background: Some(Background::Color(match (active, st) {
|
||||
(true, _) => ACTIVE,
|
||||
(false, button::Status::Hovered | button::Status::Pressed) => Color { r: 0.26, g: 0.26, b: 0.26, a: 1.0 },
|
||||
(true, _) => ACTIVE,
|
||||
(false, button::Status::Hovered | button::Status::Pressed) => Color {
|
||||
r: 0.26,
|
||||
g: 0.26,
|
||||
b: 0.26,
|
||||
a: 1.0,
|
||||
},
|
||||
_ => Color::TRANSPARENT,
|
||||
})),
|
||||
text_color: TEXT,
|
||||
|
|
@ -44,14 +118,22 @@ fn list_item(active: bool) -> impl Fn(&Theme, button::Status) -> button::Style {
|
|||
fn field_style(_: &Theme, _: text_input::Status) -> text_input::Style {
|
||||
text_input::Style {
|
||||
background: Background::Color(FIELD),
|
||||
border: Border { color: BORDER, width: 1.0, radius: 3.0.into() },
|
||||
icon: TEXT, placeholder: DIM, value: TEXT, selection: ACCENT,
|
||||
border: Border {
|
||||
color: BORDER,
|
||||
width: 1.0,
|
||||
radius: 3.0.into(),
|
||||
},
|
||||
icon: TEXT,
|
||||
placeholder: DIM,
|
||||
value: TEXT,
|
||||
selection: ACCENT,
|
||||
}
|
||||
}
|
||||
|
||||
fn hdivider<'a>() -> Element<'a, Message> {
|
||||
container(Space::new().width(Fill).height(1))
|
||||
.width(Fill).height(1)
|
||||
.width(Fill)
|
||||
.height(1)
|
||||
.style(|_: &Theme| container::Style {
|
||||
background: Some(Background::Color(BORDER)),
|
||||
..Default::default()
|
||||
|
|
@ -61,7 +143,8 @@ fn hdivider<'a>() -> Element<'a, Message> {
|
|||
|
||||
fn vsep<'a>() -> Element<'a, Message> {
|
||||
container(Space::new().width(1).height(Fill))
|
||||
.width(1).height(Fill)
|
||||
.width(1)
|
||||
.height(Fill)
|
||||
.style(|_: &Theme| container::Style {
|
||||
background: Some(Background::Color(BORDER)),
|
||||
..Default::default()
|
||||
|
|
@ -80,26 +163,52 @@ pub fn view_window<'a>(
|
|||
// ── Toolbar ───────────────────────────────────────────────────────────
|
||||
let toolbar = container(
|
||||
row![
|
||||
button(text("New Layout").size(11)).on_press(Message::LayoutManagerNew).style(btn_s(false)).padding([4, 10]),
|
||||
button(text("New Layout").size(11))
|
||||
.on_press(Message::LayoutManagerNew)
|
||||
.style(btn_s(false))
|
||||
.padding([4, 10]),
|
||||
button(text("Delete").size(11))
|
||||
.on_press(Message::LayoutManagerDelete)
|
||||
.style(move |_: &Theme, st| button::Style {
|
||||
background: Some(Background::Color(match st {
|
||||
button::Status::Hovered | button::Status::Pressed => Color { r: 0.60, g: 0.20, b: 0.18, a: 1.0 },
|
||||
_ => Color { r: 0.22, g: 0.22, b: 0.22, a: 1.0 },
|
||||
button::Status::Hovered | button::Status::Pressed => Color {
|
||||
r: 0.60,
|
||||
g: 0.20,
|
||||
b: 0.18,
|
||||
a: 1.0
|
||||
},
|
||||
_ => Color {
|
||||
r: 0.22,
|
||||
g: 0.22,
|
||||
b: 0.22,
|
||||
a: 1.0
|
||||
},
|
||||
})),
|
||||
text_color: if is_model { DIM } else { WARN },
|
||||
border: Border { color: BORDER, width: 1.0, radius: 4.0.into() },
|
||||
border: Border {
|
||||
color: BORDER,
|
||||
width: 1.0,
|
||||
radius: 4.0.into()
|
||||
},
|
||||
..Default::default()
|
||||
})
|
||||
.padding([4, 10]),
|
||||
Space::new().width(Fill),
|
||||
button(text("◀ Move Left").size(11)).on_press(Message::LayoutManagerMoveLeft).style(btn_s(false)).padding([4, 8]),
|
||||
button(text("Move Right ▶").size(11)).on_press(Message::LayoutManagerMoveRight).style(btn_s(false)).padding([4, 8]),
|
||||
button(text("Set Current").size(11)).on_press(Message::LayoutManagerSetCurrent).style(btn_s(true)).padding([4, 10]),
|
||||
button(text("◀ Move Left").size(11))
|
||||
.on_press(Message::LayoutManagerMoveLeft)
|
||||
.style(btn_s(false))
|
||||
.padding([4, 8]),
|
||||
button(text("Move Right ▶").size(11))
|
||||
.on_press(Message::LayoutManagerMoveRight)
|
||||
.style(btn_s(false))
|
||||
.padding([4, 8]),
|
||||
button(text("Set Current").size(11))
|
||||
.on_press(Message::LayoutManagerSetCurrent)
|
||||
.style(btn_s(true))
|
||||
.padding([4, 10]),
|
||||
]
|
||||
.spacing(4)
|
||||
.align_y(iced::Center)
|
||||
.align_y(iced::Center),
|
||||
)
|
||||
.style(|_: &Theme| container::Style {
|
||||
background: Some(Background::Color(TB)),
|
||||
|
|
@ -109,17 +218,24 @@ pub fn view_window<'a>(
|
|||
.padding([5, 8]);
|
||||
|
||||
// ── Left: Layout list ─────────────────────────────────────────────────
|
||||
let list_items: Vec<Element<'_, Message>> = layouts.iter().map(|name| {
|
||||
let is_sel = name.as_str() == selected;
|
||||
let is_cur = name.as_str() == current.as_str();
|
||||
let label = if is_cur { format!("{name} ◀") } else { name.clone() };
|
||||
button(text(label).size(12))
|
||||
.on_press(Message::LayoutManagerSelect(name.clone()))
|
||||
.style(list_item(is_sel))
|
||||
.padding([5, 10])
|
||||
.width(Fill)
|
||||
.into()
|
||||
}).collect();
|
||||
let list_items: Vec<Element<'_, Message>> = layouts
|
||||
.iter()
|
||||
.map(|name| {
|
||||
let is_sel = name.as_str() == selected;
|
||||
let is_cur = name.as_str() == current.as_str();
|
||||
let label = if is_cur {
|
||||
format!("{name} ◀")
|
||||
} else {
|
||||
name.clone()
|
||||
};
|
||||
button(text(label).size(12))
|
||||
.on_press(Message::LayoutManagerSelect(name.clone()))
|
||||
.style(list_item(is_sel))
|
||||
.padding([5, 10])
|
||||
.width(Fill)
|
||||
.into()
|
||||
})
|
||||
.collect();
|
||||
|
||||
let layout_list = container(
|
||||
column![
|
||||
|
|
@ -127,7 +243,11 @@ pub fn view_window<'a>(
|
|||
container(scrollable(column(list_items).spacing(2)).height(Fill))
|
||||
.style(|_: &Theme| container::Style {
|
||||
background: Some(Background::Color(LIST)),
|
||||
border: Border { color: BORDER, width: 1.0, radius: 3.0.into() },
|
||||
border: Border {
|
||||
color: BORDER,
|
||||
width: 1.0,
|
||||
radius: 3.0.into()
|
||||
},
|
||||
..Default::default()
|
||||
})
|
||||
.width(Fill)
|
||||
|
|
@ -135,28 +255,50 @@ pub fn view_window<'a>(
|
|||
.padding(2),
|
||||
]
|
||||
.spacing(4)
|
||||
.height(Fill)
|
||||
.height(Fill),
|
||||
)
|
||||
.width(220)
|
||||
.height(Fill)
|
||||
.padding(iced::Padding { top: 12.0, right: 8.0, bottom: 12.0, left: 12.0 });
|
||||
.padding(iced::Padding {
|
||||
top: 12.0,
|
||||
right: 8.0,
|
||||
bottom: 12.0,
|
||||
left: 12.0,
|
||||
});
|
||||
|
||||
// ── Right: Details + rename ───────────────────────────────────────────
|
||||
let details = container(
|
||||
column![
|
||||
text(if is_model { "Model Space" } else { "Paper Space Layout" })
|
||||
.size(13).color(TEXT),
|
||||
text(if is_model {
|
||||
"Model Space"
|
||||
} else {
|
||||
"Paper Space Layout"
|
||||
})
|
||||
.size(13)
|
||||
.color(TEXT),
|
||||
Space::new().height(8),
|
||||
row![
|
||||
text("Name:").size(11).color(DIM).width(80),
|
||||
text(selected).size(11),
|
||||
].spacing(8).align_y(iced::Center),
|
||||
]
|
||||
.spacing(8)
|
||||
.align_y(iced::Center),
|
||||
row![
|
||||
text("Status:").size(11).color(DIM).width(80),
|
||||
text(if selected == current.as_str() { "Active ◀" } else { "Inactive" })
|
||||
.size(11)
|
||||
.color(if selected == current.as_str() { ACCENT } else { DIM }),
|
||||
].spacing(8).align_y(iced::Center),
|
||||
text(if selected == current.as_str() {
|
||||
"Active ◀"
|
||||
} else {
|
||||
"Inactive"
|
||||
})
|
||||
.size(11)
|
||||
.color(if selected == current.as_str() {
|
||||
ACCENT
|
||||
} else {
|
||||
DIM
|
||||
}),
|
||||
]
|
||||
.spacing(8)
|
||||
.align_y(iced::Center),
|
||||
Space::new().height(16),
|
||||
text("Rename").size(10).color(DIM),
|
||||
row![
|
||||
|
|
@ -170,23 +312,23 @@ pub fn view_window<'a>(
|
|||
.on_press(Message::LayoutManagerRenameCommit)
|
||||
.style(btn_s(true))
|
||||
.padding([4, 10]),
|
||||
].spacing(6).align_y(iced::Center),
|
||||
]
|
||||
.spacing(6)
|
||||
.align_y(iced::Center),
|
||||
]
|
||||
.spacing(8)
|
||||
.spacing(8),
|
||||
)
|
||||
.width(Fill)
|
||||
.padding([12, 12]);
|
||||
|
||||
let body = row![layout_list, vsep(), details].height(Fill);
|
||||
|
||||
container(
|
||||
column![toolbar, hdivider(), body].spacing(0)
|
||||
)
|
||||
.style(|_: &Theme| container::Style {
|
||||
background: Some(Background::Color(BG)),
|
||||
..Default::default()
|
||||
})
|
||||
.width(Fill)
|
||||
.height(Fill)
|
||||
.into()
|
||||
container(column![toolbar, hdivider(), body].spacing(0))
|
||||
.style(|_: &Theme| container::Style {
|
||||
background: Some(Background::Color(BG)),
|
||||
..Default::default()
|
||||
})
|
||||
.width(Fill)
|
||||
.height(Fill)
|
||||
.into()
|
||||
}
|
||||
|
|
|
|||
|
|
@ -4,25 +4,84 @@ use crate::app::Message;
|
|||
use iced::widget::{button, column, container, row, scrollable, text, Space};
|
||||
use iced::{Background, Border, Color, Element, Fill, Theme};
|
||||
|
||||
const TB: Color = Color { r: 0.13, g: 0.13, b: 0.13, a: 1.0 };
|
||||
const BG: Color = Color { r: 0.15, g: 0.15, b: 0.15, a: 1.0 };
|
||||
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 };
|
||||
const ACTIVE: Color = Color { r: 0.20, g: 0.40, b: 0.70, a: 1.0 };
|
||||
const LIST: Color = Color { r: 0.12, g: 0.12, b: 0.12, a: 1.0 };
|
||||
const TB: Color = Color {
|
||||
r: 0.13,
|
||||
g: 0.13,
|
||||
b: 0.13,
|
||||
a: 1.0,
|
||||
};
|
||||
const BG: Color = Color {
|
||||
r: 0.15,
|
||||
g: 0.15,
|
||||
b: 0.15,
|
||||
a: 1.0,
|
||||
};
|
||||
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,
|
||||
};
|
||||
const ACTIVE: Color = Color {
|
||||
r: 0.20,
|
||||
g: 0.40,
|
||||
b: 0.70,
|
||||
a: 1.0,
|
||||
};
|
||||
const LIST: Color = Color {
|
||||
r: 0.12,
|
||||
g: 0.12,
|
||||
b: 0.12,
|
||||
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 },
|
||||
(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() },
|
||||
border: Border {
|
||||
color: BORDER,
|
||||
width: 1.0,
|
||||
radius: 4.0.into(),
|
||||
},
|
||||
..Default::default()
|
||||
}
|
||||
}
|
||||
|
|
@ -30,8 +89,13 @@ fn btn_s(accent: bool) -> impl Fn(&Theme, button::Status) -> button::Style {
|
|||
fn list_item(active: bool) -> impl Fn(&Theme, button::Status) -> button::Style {
|
||||
move |_: &Theme, st| button::Style {
|
||||
background: Some(Background::Color(match (active, st) {
|
||||
(true, _) => ACTIVE,
|
||||
(false, button::Status::Hovered | button::Status::Pressed) => Color { r: 0.26, g: 0.26, b: 0.26, a: 1.0 },
|
||||
(true, _) => ACTIVE,
|
||||
(false, button::Status::Hovered | button::Status::Pressed) => Color {
|
||||
r: 0.26,
|
||||
g: 0.26,
|
||||
b: 0.26,
|
||||
a: 1.0,
|
||||
},
|
||||
_ => Color::TRANSPARENT,
|
||||
})),
|
||||
text_color: TEXT,
|
||||
|
|
@ -41,7 +105,8 @@ fn list_item(active: bool) -> impl Fn(&Theme, button::Status) -> button::Style {
|
|||
|
||||
fn hdivider<'a>() -> Element<'a, Message> {
|
||||
container(Space::new().width(Fill).height(1))
|
||||
.width(Fill).height(1)
|
||||
.width(Fill)
|
||||
.height(1)
|
||||
.style(|_: &Theme| container::Style {
|
||||
background: Some(Background::Color(BORDER)),
|
||||
..Default::default()
|
||||
|
|
@ -51,7 +116,8 @@ fn hdivider<'a>() -> Element<'a, Message> {
|
|||
|
||||
fn vsep<'a>() -> Element<'a, Message> {
|
||||
container(Space::new().width(1).height(Fill))
|
||||
.width(1).height(Fill)
|
||||
.width(1)
|
||||
.height(Fill)
|
||||
.style(|_: &Theme| container::Style {
|
||||
background: Some(Background::Color(BORDER)),
|
||||
..Default::default()
|
||||
|
|
@ -68,12 +134,21 @@ pub fn view_window<'a>(
|
|||
// ── Toolbar ───────────────────────────────────────────────────────────
|
||||
let toolbar = container(
|
||||
row![
|
||||
button(text("Set Current").size(11)).on_press(Message::MlStyleDialogSetCurrent).style(btn_s(true)).padding([4, 10]),
|
||||
button(text("New").size(11)).on_press(Message::MlStyleDialogNew).style(btn_s(false)).padding([4, 10]),
|
||||
button(text("Delete").size(11)).on_press(Message::MlStyleDialogDelete).style(btn_s(false)).padding([4, 10]),
|
||||
button(text("Set Current").size(11))
|
||||
.on_press(Message::MlStyleDialogSetCurrent)
|
||||
.style(btn_s(true))
|
||||
.padding([4, 10]),
|
||||
button(text("New").size(11))
|
||||
.on_press(Message::MlStyleDialogNew)
|
||||
.style(btn_s(false))
|
||||
.padding([4, 10]),
|
||||
button(text("Delete").size(11))
|
||||
.on_press(Message::MlStyleDialogDelete)
|
||||
.style(btn_s(false))
|
||||
.padding([4, 10]),
|
||||
]
|
||||
.spacing(4)
|
||||
.align_y(iced::Center)
|
||||
.align_y(iced::Center),
|
||||
)
|
||||
.style(|_: &Theme| container::Style {
|
||||
background: Some(Background::Color(TB)),
|
||||
|
|
@ -83,17 +158,24 @@ pub fn view_window<'a>(
|
|||
.padding([5, 8]);
|
||||
|
||||
// ── Left: Style list ──────────────────────────────────────────────────
|
||||
let style_items: Vec<Element<'_, Message>> = styles.iter().map(|name| {
|
||||
let is_sel = name.as_str() == selected;
|
||||
let is_cur = *name == current_style;
|
||||
let label = if is_cur { format!("{name} ◀") } else { name.clone() };
|
||||
button(text(label).size(11))
|
||||
.on_press(Message::MlStyleDialogSelect(name.clone()))
|
||||
.style(list_item(is_sel))
|
||||
.padding([4, 8])
|
||||
.width(Fill)
|
||||
.into()
|
||||
}).collect();
|
||||
let style_items: Vec<Element<'_, Message>> = styles
|
||||
.iter()
|
||||
.map(|name| {
|
||||
let is_sel = name.as_str() == selected;
|
||||
let is_cur = *name == current_style;
|
||||
let label = if is_cur {
|
||||
format!("{name} ◀")
|
||||
} else {
|
||||
name.clone()
|
||||
};
|
||||
button(text(label).size(11))
|
||||
.on_press(Message::MlStyleDialogSelect(name.clone()))
|
||||
.style(list_item(is_sel))
|
||||
.padding([4, 8])
|
||||
.width(Fill)
|
||||
.into()
|
||||
})
|
||||
.collect();
|
||||
|
||||
let style_list = container(
|
||||
column![
|
||||
|
|
@ -101,7 +183,11 @@ pub fn view_window<'a>(
|
|||
container(scrollable(column(style_items).spacing(2)).height(Fill))
|
||||
.style(|_: &Theme| container::Style {
|
||||
background: Some(Background::Color(LIST)),
|
||||
border: Border { color: BORDER, width: 1.0, radius: 3.0.into() },
|
||||
border: Border {
|
||||
color: BORDER,
|
||||
width: 1.0,
|
||||
radius: 3.0.into()
|
||||
},
|
||||
..Default::default()
|
||||
})
|
||||
.width(Fill)
|
||||
|
|
@ -109,62 +195,85 @@ pub fn view_window<'a>(
|
|||
.padding(2),
|
||||
]
|
||||
.spacing(4)
|
||||
.height(Fill)
|
||||
.height(Fill),
|
||||
)
|
||||
.width(200)
|
||||
.height(Fill)
|
||||
.padding(iced::Padding { top: 12.0, right: 8.0, bottom: 12.0, left: 12.0 });
|
||||
.padding(iced::Padding {
|
||||
top: 12.0,
|
||||
right: 8.0,
|
||||
bottom: 12.0,
|
||||
left: 12.0,
|
||||
});
|
||||
|
||||
// ── Right: Details panel ──────────────────────────────────────────────
|
||||
let info_row = |label: &'static str, val: String| -> Element<'_, Message> {
|
||||
row![
|
||||
text(label).size(11).color(DIM).width(120),
|
||||
text(val).size(11),
|
||||
].spacing(8).align_y(iced::Center).into()
|
||||
]
|
||||
.spacing(8)
|
||||
.align_y(iced::Center)
|
||||
.into()
|
||||
};
|
||||
|
||||
let details: Element<'_, Message> = if let Some(s) = selected_style {
|
||||
let elem_rows: Vec<Element<'_, Message>> = s.elements.iter().enumerate().map(|(idx, e)| {
|
||||
let color_str = match &e.color {
|
||||
acadrust::types::Color::ByLayer => "ByLayer".into(),
|
||||
acadrust::types::Color::ByBlock => "ByBlock".into(),
|
||||
acadrust::types::Color::Index(i) => format!("ACI {i}"),
|
||||
acadrust::types::Color::Rgb { r, g, b } => format!("#{r:02X}{g:02X}{b:02X}"),
|
||||
};
|
||||
let lt = if e.linetype.is_empty() { "ByLayer" } else { &e.linetype };
|
||||
row![
|
||||
text(format!(" {idx}:")).size(10).color(DIM).width(24),
|
||||
text(format!("{:+.3}", e.offset)).size(10).width(70),
|
||||
text(color_str).size(10).width(90),
|
||||
text(lt).size(10),
|
||||
].spacing(4).align_y(iced::Center).into()
|
||||
}).collect();
|
||||
let elem_rows: Vec<Element<'_, Message>> = s
|
||||
.elements
|
||||
.iter()
|
||||
.enumerate()
|
||||
.map(|(idx, e)| {
|
||||
let color_str = match &e.color {
|
||||
acadrust::types::Color::ByLayer => "ByLayer".into(),
|
||||
acadrust::types::Color::ByBlock => "ByBlock".into(),
|
||||
acadrust::types::Color::Index(i) => format!("ACI {i}"),
|
||||
acadrust::types::Color::Rgb { r, g, b } => format!("#{r:02X}{g:02X}{b:02X}"),
|
||||
};
|
||||
let lt = if e.linetype.is_empty() {
|
||||
"ByLayer"
|
||||
} else {
|
||||
&e.linetype
|
||||
};
|
||||
row![
|
||||
text(format!(" {idx}:")).size(10).color(DIM).width(24),
|
||||
text(format!("{:+.3}", e.offset)).size(10).width(70),
|
||||
text(color_str).size(10).width(90),
|
||||
text(lt).size(10),
|
||||
]
|
||||
.spacing(4)
|
||||
.align_y(iced::Center)
|
||||
.into()
|
||||
})
|
||||
.collect();
|
||||
|
||||
let mut col_items: Vec<Element<'_, Message>> = vec![
|
||||
info_row("Name:", s.name.clone()),
|
||||
info_row("Elements:", s.elements.len().to_string()),
|
||||
text(" Off Color Ltype").size(10).color(DIM).into(),
|
||||
text(" Off Color Ltype")
|
||||
.size(10)
|
||||
.color(DIM)
|
||||
.into(),
|
||||
];
|
||||
col_items.extend(elem_rows);
|
||||
scrollable(column(col_items).spacing(6).padding([12, 12])).height(Fill).into()
|
||||
scrollable(column(col_items).spacing(6).padding([12, 12]))
|
||||
.height(Fill)
|
||||
.into()
|
||||
} else {
|
||||
container(text("Select a style to view details.").size(11).color(DIM)).padding([12, 12]).into()
|
||||
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);
|
||||
let right_panel = container(details).width(Fill).height(Fill);
|
||||
|
||||
let body = row![style_list, vsep(), right_panel].height(Fill);
|
||||
|
||||
container(
|
||||
column![toolbar, hdivider(), body].spacing(0)
|
||||
)
|
||||
.style(|_: &Theme| container::Style {
|
||||
background: Some(Background::Color(BG)),
|
||||
..Default::default()
|
||||
})
|
||||
.width(Fill)
|
||||
.height(Fill)
|
||||
.into()
|
||||
container(column![toolbar, hdivider(), body].spacing(0))
|
||||
.style(|_: &Theme| container::Style {
|
||||
background: Some(Background::Color(BG)),
|
||||
..Default::default()
|
||||
})
|
||||
.width(Fill)
|
||||
.height(Fill)
|
||||
.into()
|
||||
}
|
||||
|
|
|
|||
|
|
@ -14,8 +14,8 @@ pub mod page_setup;
|
|||
pub mod plotstyle;
|
||||
pub mod properties;
|
||||
pub mod ribbon;
|
||||
pub mod shortcuts;
|
||||
pub mod scale_popup;
|
||||
pub mod shortcuts;
|
||||
pub mod snap_popup;
|
||||
pub mod statusbar;
|
||||
pub mod tablestyle;
|
||||
|
|
|
|||
|
|
@ -7,9 +7,9 @@ use iced::mouse;
|
|||
use iced::widget::{button, canvas, column, container, row, text};
|
||||
use iced::{Background, Border, Color, Element, Length, Point, Size, Theme};
|
||||
|
||||
use crate::app::Message;
|
||||
use crate::scene::object::GripShape;
|
||||
use crate::scene::SelectionState;
|
||||
use crate::app::Message;
|
||||
|
||||
/// Half-size of the crosshair center square in screen pixels (square = SQ*2 × SQ*2).
|
||||
pub const CROSSHAIR_SQ: f32 = 7.5;
|
||||
|
|
@ -246,8 +246,10 @@ impl canvas::Program<Message> for SelectionCanvas {
|
|||
use crate::scene::{VIEWCUBE_DRAW_PX, VIEWCUBE_PAD};
|
||||
let vc_x = bounds.width - VIEWCUBE_DRAW_PX - VIEWCUBE_PAD;
|
||||
let vc_y = VIEWCUBE_PAD;
|
||||
if pos.x >= vc_x && pos.x <= vc_x + VIEWCUBE_DRAW_PX
|
||||
&& pos.y >= vc_y && pos.y <= vc_y + VIEWCUBE_DRAW_PX
|
||||
if pos.x >= vc_x
|
||||
&& pos.x <= vc_x + VIEWCUBE_DRAW_PX
|
||||
&& pos.y >= vc_y
|
||||
&& pos.y <= vc_y + VIEWCUBE_DRAW_PX
|
||||
{
|
||||
return mouse::Interaction::None;
|
||||
}
|
||||
|
|
@ -526,56 +528,58 @@ impl canvas::Program<Message> for SelectionCanvas {
|
|||
cursor.position_in(bounds).map_or(false, |pos| {
|
||||
let vc_x = bounds.width - VIEWCUBE_DRAW_PX - VIEWCUBE_PAD;
|
||||
let vc_y = VIEWCUBE_PAD;
|
||||
pos.x >= vc_x && pos.x <= vc_x + VIEWCUBE_DRAW_PX
|
||||
&& pos.y >= vc_y && pos.y <= vc_y + VIEWCUBE_DRAW_PX
|
||||
pos.x >= vc_x
|
||||
&& pos.x <= vc_x + VIEWCUBE_DRAW_PX
|
||||
&& pos.y >= vc_y
|
||||
&& pos.y <= vc_y + VIEWCUBE_DRAW_PX
|
||||
})
|
||||
};
|
||||
if !over_viewcube {
|
||||
if let Some(cp) = self.selection.last_move_pos {
|
||||
let color = Color {
|
||||
r: 0.85,
|
||||
g: 0.85,
|
||||
b: 0.85,
|
||||
a: 0.90,
|
||||
};
|
||||
let stroke = canvas::Stroke {
|
||||
width: 1.0,
|
||||
style: canvas::Style::Solid(color),
|
||||
..Default::default()
|
||||
};
|
||||
let sq = CROSSHAIR_SQ; // square half-size → 15×15
|
||||
let arm = CROSSHAIR_ARM; // crosshair arm length from center
|
||||
if let Some(cp) = self.selection.last_move_pos {
|
||||
let color = Color {
|
||||
r: 0.85,
|
||||
g: 0.85,
|
||||
b: 0.85,
|
||||
a: 0.90,
|
||||
};
|
||||
let stroke = canvas::Stroke {
|
||||
width: 1.0,
|
||||
style: canvas::Style::Solid(color),
|
||||
..Default::default()
|
||||
};
|
||||
let sq = CROSSHAIR_SQ; // square half-size → 15×15
|
||||
let arm = CROSSHAIR_ARM; // crosshair arm length from center
|
||||
|
||||
// Horizontal arms (start at square edge, end at arm length)
|
||||
let h_left = canvas::Path::new(|b| {
|
||||
b.move_to(Point::new(cp.x - sq, cp.y));
|
||||
b.line_to(Point::new(cp.x - arm, cp.y));
|
||||
});
|
||||
let h_right = canvas::Path::new(|b| {
|
||||
b.move_to(Point::new(cp.x + sq, cp.y));
|
||||
b.line_to(Point::new(cp.x + arm, cp.y));
|
||||
});
|
||||
// Vertical arms
|
||||
let v_top = canvas::Path::new(|b| {
|
||||
b.move_to(Point::new(cp.x, cp.y - sq));
|
||||
b.line_to(Point::new(cp.x, cp.y - arm));
|
||||
});
|
||||
let v_bot = canvas::Path::new(|b| {
|
||||
b.move_to(Point::new(cp.x, cp.y + sq));
|
||||
b.line_to(Point::new(cp.x, cp.y + arm));
|
||||
});
|
||||
// Center square
|
||||
let square = canvas::Path::rectangle(
|
||||
Point::new(cp.x - sq, cp.y - sq),
|
||||
Size::new(sq * 2.0, sq * 2.0),
|
||||
);
|
||||
// Horizontal arms (start at square edge, end at arm length)
|
||||
let h_left = canvas::Path::new(|b| {
|
||||
b.move_to(Point::new(cp.x - sq, cp.y));
|
||||
b.line_to(Point::new(cp.x - arm, cp.y));
|
||||
});
|
||||
let h_right = canvas::Path::new(|b| {
|
||||
b.move_to(Point::new(cp.x + sq, cp.y));
|
||||
b.line_to(Point::new(cp.x + arm, cp.y));
|
||||
});
|
||||
// Vertical arms
|
||||
let v_top = canvas::Path::new(|b| {
|
||||
b.move_to(Point::new(cp.x, cp.y - sq));
|
||||
b.line_to(Point::new(cp.x, cp.y - arm));
|
||||
});
|
||||
let v_bot = canvas::Path::new(|b| {
|
||||
b.move_to(Point::new(cp.x, cp.y + sq));
|
||||
b.line_to(Point::new(cp.x, cp.y + arm));
|
||||
});
|
||||
// Center square
|
||||
let square = canvas::Path::rectangle(
|
||||
Point::new(cp.x - sq, cp.y - sq),
|
||||
Size::new(sq * 2.0, sq * 2.0),
|
||||
);
|
||||
|
||||
frame.stroke(&h_left, stroke.clone());
|
||||
frame.stroke(&h_right, stroke.clone());
|
||||
frame.stroke(&v_top, stroke.clone());
|
||||
frame.stroke(&v_bot, stroke.clone());
|
||||
frame.stroke(&square, stroke);
|
||||
}
|
||||
frame.stroke(&h_left, stroke.clone());
|
||||
frame.stroke(&h_right, stroke.clone());
|
||||
frame.stroke(&v_top, stroke.clone());
|
||||
frame.stroke(&v_bot, stroke.clone());
|
||||
frame.stroke(&square, stroke);
|
||||
}
|
||||
} // end !over_viewcube
|
||||
|
||||
// ── UCS icon ──────────────────────────────────────────────────────
|
||||
|
|
@ -588,7 +592,12 @@ impl canvas::Program<Message> for SelectionCanvas {
|
|||
let tp = ost.screen;
|
||||
let cx = self.cursor_screen.x;
|
||||
let cy = self.cursor_screen.y;
|
||||
let track_color = Color { r: 0.15, g: 0.85, b: 0.95, a: 0.7 };
|
||||
let track_color = Color {
|
||||
r: 0.15,
|
||||
g: 0.85,
|
||||
b: 0.95,
|
||||
a: 0.7,
|
||||
};
|
||||
let dash_stroke = canvas::Stroke::default()
|
||||
.with_color(track_color)
|
||||
.with_width(1.0);
|
||||
|
|
@ -606,12 +615,24 @@ impl canvas::Program<Message> for SelectionCanvas {
|
|||
// Small cross at the tracking point.
|
||||
let sz = 5.0_f32;
|
||||
let h = canvas::Path::line(
|
||||
Point { x: tp.x - sz, y: tp.y },
|
||||
Point { x: tp.x + sz, y: tp.y },
|
||||
Point {
|
||||
x: tp.x - sz,
|
||||
y: tp.y,
|
||||
},
|
||||
Point {
|
||||
x: tp.x + sz,
|
||||
y: tp.y,
|
||||
},
|
||||
);
|
||||
let v = canvas::Path::line(
|
||||
Point { x: tp.x, y: tp.y - sz },
|
||||
Point { x: tp.x, y: tp.y + sz },
|
||||
Point {
|
||||
x: tp.x,
|
||||
y: tp.y - sz,
|
||||
},
|
||||
Point {
|
||||
x: tp.x,
|
||||
y: tp.y + sz,
|
||||
},
|
||||
);
|
||||
frame.stroke(&h, dash_stroke.clone());
|
||||
frame.stroke(&v, dash_stroke);
|
||||
|
|
@ -789,8 +810,8 @@ fn draw_axes(frame: &mut canvas::Frame, vp: Mat4, bounds: iced::Rectangle, exten
|
|||
// drawn as outlined circles with reduced opacity.
|
||||
|
||||
const UCS_ICON_MARGIN: f32 = 50.0;
|
||||
const UCS_ICON_LEN: f32 = 38.0; // longest axis arm in screen pixels
|
||||
const UCS_ICON_TIP: f32 = 7.0; // arrowhead size in pixels
|
||||
const UCS_ICON_LEN: f32 = 38.0; // longest axis arm in screen pixels
|
||||
const UCS_ICON_TIP: f32 = 7.0; // arrowhead size in pixels
|
||||
|
||||
fn draw_ucs_icon(frame: &mut canvas::Frame, vp: Mat4, bounds: iced::Rectangle) {
|
||||
if bounds.width < 10.0 || bounds.height < 10.0 {
|
||||
|
|
@ -813,9 +834,9 @@ fn draw_ucs_icon(frame: &mut canvas::Frame, vp: Mat4, bounds: iced::Rectangle) {
|
|||
};
|
||||
|
||||
let Some(org) = w2ndc(Vec3::ZERO) else { return };
|
||||
let Some(xn) = w2ndc(Vec3::X) else { return };
|
||||
let Some(yn) = w2ndc(Vec3::Y) else { return };
|
||||
let Some(zn) = w2ndc(Vec3::Z) else { return };
|
||||
let Some(xn) = w2ndc(Vec3::X) else { return };
|
||||
let Some(yn) = w2ndc(Vec3::Y) else { return };
|
||||
let Some(zn) = w2ndc(Vec3::Z) else { return };
|
||||
|
||||
let org_s = ndc2s(org);
|
||||
let icon_origin = Point::new(
|
||||
|
|
@ -843,19 +864,61 @@ fn draw_ucs_icon(frame: &mut canvas::Frame, vp: Mat4, bounds: iced::Rectangle) {
|
|||
// depth > 0 → tip is farther from viewer than origin (axis going into screen).
|
||||
// depth < 0 → tip is closer (axis coming toward viewer).
|
||||
struct AxisInfo {
|
||||
dx: f32, dy: f32, sc_len: f32, depth: f32,
|
||||
r: f32, g: f32, b: f32, label: &'static str,
|
||||
dx: f32,
|
||||
dy: f32,
|
||||
sc_len: f32,
|
||||
depth: f32,
|
||||
r: f32,
|
||||
g: f32,
|
||||
b: f32,
|
||||
label: &'static str,
|
||||
}
|
||||
let mut axes = [
|
||||
AxisInfo { dx: xdx*sc, dy: xdy*sc, sc_len: xlen*sc, depth: xn.z - org.z, r: 0.90, g: 0.22, b: 0.22, label: "X" },
|
||||
AxisInfo { dx: ydx*sc, dy: ydy*sc, sc_len: ylen*sc, depth: yn.z - org.z, r: 0.22, g: 0.85, b: 0.22, label: "Y" },
|
||||
AxisInfo { dx: zdx*sc, dy: zdy*sc, sc_len: zlen*sc, depth: zn.z - org.z, r: 0.22, g: 0.45, b: 0.90, label: "Z" },
|
||||
AxisInfo {
|
||||
dx: xdx * sc,
|
||||
dy: xdy * sc,
|
||||
sc_len: xlen * sc,
|
||||
depth: xn.z - org.z,
|
||||
r: 0.90,
|
||||
g: 0.22,
|
||||
b: 0.22,
|
||||
label: "X",
|
||||
},
|
||||
AxisInfo {
|
||||
dx: ydx * sc,
|
||||
dy: ydy * sc,
|
||||
sc_len: ylen * sc,
|
||||
depth: yn.z - org.z,
|
||||
r: 0.22,
|
||||
g: 0.85,
|
||||
b: 0.22,
|
||||
label: "Y",
|
||||
},
|
||||
AxisInfo {
|
||||
dx: zdx * sc,
|
||||
dy: zdy * sc,
|
||||
sc_len: zlen * sc,
|
||||
depth: zn.z - org.z,
|
||||
r: 0.22,
|
||||
g: 0.45,
|
||||
b: 0.90,
|
||||
label: "Z",
|
||||
},
|
||||
];
|
||||
// Back-to-front: draw axis farthest from viewer first.
|
||||
axes.sort_by(|a, b| b.depth.partial_cmp(&a.depth).unwrap_or(std::cmp::Ordering::Equal));
|
||||
axes.sort_by(|a, b| {
|
||||
b.depth
|
||||
.partial_cmp(&a.depth)
|
||||
.unwrap_or(std::cmp::Ordering::Equal)
|
||||
});
|
||||
|
||||
for ax in &axes {
|
||||
let col = Color { r: ax.r, g: ax.g, b: ax.b, a: 1.0 };
|
||||
let col = Color {
|
||||
r: ax.r,
|
||||
g: ax.g,
|
||||
b: ax.b,
|
||||
a: 1.0,
|
||||
};
|
||||
let tip = Point::new(icon_origin.x + ax.dx, icon_origin.y + ax.dy);
|
||||
|
||||
// Shaft
|
||||
|
|
@ -922,7 +985,15 @@ fn draw_ucs_icon(frame: &mut canvas::Frame, vp: Mat4, bounds: iced::Rectangle) {
|
|||
|
||||
// Origin dot.
|
||||
let circle = canvas::Path::circle(icon_origin, 3.5);
|
||||
frame.fill(&circle, Color { r: 0.9, g: 0.9, b: 0.9, a: 0.95 });
|
||||
frame.fill(
|
||||
&circle,
|
||||
Color {
|
||||
r: 0.9,
|
||||
g: 0.9,
|
||||
b: 0.9,
|
||||
a: 0.95,
|
||||
},
|
||||
);
|
||||
}
|
||||
|
||||
// ── Dynamic Input overlay ─────────────────────────────────────────────────
|
||||
|
|
@ -931,14 +1002,14 @@ fn draw_ucs_icon(frame: &mut canvas::Frame, vp: Mat4, bounds: iced::Rectangle) {
|
|||
///
|
||||
/// `cursor_screen` — cursor position in viewport pixels.
|
||||
/// `label` — text to display (e.g. "X: 12.34 Y: 56.78").
|
||||
pub fn dynamic_input_overlay<'a>(
|
||||
cursor_screen: Point,
|
||||
label: String,
|
||||
) -> Element<'a, Message> {
|
||||
canvas(DynInputCanvas { cursor_screen, label })
|
||||
.width(Length::Fill)
|
||||
.height(Length::Fill)
|
||||
.into()
|
||||
pub fn dynamic_input_overlay<'a>(cursor_screen: Point, label: String) -> Element<'a, Message> {
|
||||
canvas(DynInputCanvas {
|
||||
cursor_screen,
|
||||
label,
|
||||
})
|
||||
.width(Length::Fill)
|
||||
.height(Length::Fill)
|
||||
.into()
|
||||
}
|
||||
|
||||
struct DynInputCanvas {
|
||||
|
|
@ -980,27 +1051,52 @@ impl canvas::Program<Message> for DynInputCanvas {
|
|||
let mut by = self.cursor_screen.y + OFFSET_Y;
|
||||
|
||||
// Keep box inside viewport.
|
||||
if bx + BOX_W > bounds.width { bx = self.cursor_screen.x - BOX_W - 4.0; }
|
||||
if by + BOX_H > bounds.height { by = self.cursor_screen.y - BOX_H - 4.0; }
|
||||
if bx + BOX_W > bounds.width {
|
||||
bx = self.cursor_screen.x - BOX_W - 4.0;
|
||||
}
|
||||
if by + BOX_H > bounds.height {
|
||||
by = self.cursor_screen.y - BOX_H - 4.0;
|
||||
}
|
||||
|
||||
let bg = canvas::Path::rectangle(
|
||||
Point { x: bx, y: by },
|
||||
Size { width: BOX_W, height: BOX_H },
|
||||
Size {
|
||||
width: BOX_W,
|
||||
height: BOX_H,
|
||||
},
|
||||
);
|
||||
frame.fill(
|
||||
&bg,
|
||||
Color { r: 0.05, g: 0.05, b: 0.12, a: 0.85 },
|
||||
Color {
|
||||
r: 0.05,
|
||||
g: 0.05,
|
||||
b: 0.12,
|
||||
a: 0.85,
|
||||
},
|
||||
);
|
||||
frame.stroke(
|
||||
&bg,
|
||||
canvas::Stroke::default()
|
||||
.with_color(Color { r: 0.35, g: 0.55, b: 0.90, a: 0.9 })
|
||||
.with_color(Color {
|
||||
r: 0.35,
|
||||
g: 0.55,
|
||||
b: 0.90,
|
||||
a: 0.9,
|
||||
})
|
||||
.with_width(1.0),
|
||||
);
|
||||
frame.fill_text(canvas::Text {
|
||||
content: self.label.clone(),
|
||||
position: Point { x: bx + PAD, y: by + PAD },
|
||||
color: Color { r: 0.90, g: 0.90, b: 0.90, a: 1.0 },
|
||||
position: Point {
|
||||
x: bx + PAD,
|
||||
y: by + PAD,
|
||||
},
|
||||
color: Color {
|
||||
r: 0.90,
|
||||
g: 0.90,
|
||||
b: 0.90,
|
||||
a: 1.0,
|
||||
},
|
||||
size: iced::Pixels(FONT_SIZE),
|
||||
..Default::default()
|
||||
});
|
||||
|
|
|
|||
|
|
@ -4,25 +4,84 @@ use crate::app::Message;
|
|||
use iced::widget::{button, column, container, row, scrollable, text, text_input, Space};
|
||||
use iced::{Background, Border, Color, Element, Fill, Theme};
|
||||
|
||||
const TB: Color = Color { r: 0.13, g: 0.13, b: 0.13, a: 1.0 };
|
||||
const BG: Color = Color { r: 0.15, g: 0.15, b: 0.15, a: 1.0 };
|
||||
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 };
|
||||
const ACTIVE: Color = Color { r: 0.20, g: 0.40, b: 0.70, a: 1.0 };
|
||||
const FIELD: Color = Color { r: 0.10, g: 0.10, b: 0.10, a: 1.0 };
|
||||
const TB: Color = Color {
|
||||
r: 0.13,
|
||||
g: 0.13,
|
||||
b: 0.13,
|
||||
a: 1.0,
|
||||
};
|
||||
const BG: Color = Color {
|
||||
r: 0.15,
|
||||
g: 0.15,
|
||||
b: 0.15,
|
||||
a: 1.0,
|
||||
};
|
||||
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,
|
||||
};
|
||||
const ACTIVE: Color = Color {
|
||||
r: 0.20,
|
||||
g: 0.40,
|
||||
b: 0.70,
|
||||
a: 1.0,
|
||||
};
|
||||
const FIELD: Color = Color {
|
||||
r: 0.10,
|
||||
g: 0.10,
|
||||
b: 0.10,
|
||||
a: 1.0,
|
||||
};
|
||||
|
||||
fn btn(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 },
|
||||
(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() },
|
||||
border: Border {
|
||||
color: BORDER,
|
||||
width: 1.0,
|
||||
radius: 4.0.into(),
|
||||
},
|
||||
shadow: iced::Shadow::default(),
|
||||
snap: false,
|
||||
}
|
||||
|
|
@ -31,12 +90,26 @@ fn btn(accent: bool) -> impl Fn(&Theme, button::Status) -> button::Style {
|
|||
fn pill(active: bool) -> impl Fn(&Theme, button::Status) -> button::Style {
|
||||
move |_: &Theme, st| button::Style {
|
||||
background: Some(Background::Color(match (active, st) {
|
||||
(true, _) => ACTIVE,
|
||||
(false, button::Status::Hovered | button::Status::Pressed) => Color { r: 0.28, g: 0.28, b: 0.28, a: 1.0 },
|
||||
_ => Color { r: 0.20, g: 0.20, b: 0.20, a: 1.0 },
|
||||
(true, _) => ACTIVE,
|
||||
(false, button::Status::Hovered | button::Status::Pressed) => Color {
|
||||
r: 0.28,
|
||||
g: 0.28,
|
||||
b: 0.28,
|
||||
a: 1.0,
|
||||
},
|
||||
_ => Color {
|
||||
r: 0.20,
|
||||
g: 0.20,
|
||||
b: 0.20,
|
||||
a: 1.0,
|
||||
},
|
||||
})),
|
||||
text_color: TEXT,
|
||||
border: Border { color: BORDER, width: 1.0, radius: 3.0.into() },
|
||||
border: Border {
|
||||
color: BORDER,
|
||||
width: 1.0,
|
||||
radius: 3.0.into(),
|
||||
},
|
||||
shadow: iced::Shadow::default(),
|
||||
snap: false,
|
||||
}
|
||||
|
|
@ -45,14 +118,22 @@ fn pill(active: bool) -> impl Fn(&Theme, button::Status) -> button::Style {
|
|||
fn field_style(_: &Theme, _: text_input::Status) -> text_input::Style {
|
||||
text_input::Style {
|
||||
background: Background::Color(FIELD),
|
||||
border: Border { color: BORDER, width: 1.0, radius: 3.0.into() },
|
||||
icon: TEXT, placeholder: DIM, value: TEXT, selection: ACCENT,
|
||||
border: Border {
|
||||
color: BORDER,
|
||||
width: 1.0,
|
||||
radius: 3.0.into(),
|
||||
},
|
||||
icon: TEXT,
|
||||
placeholder: DIM,
|
||||
value: TEXT,
|
||||
selection: ACCENT,
|
||||
}
|
||||
}
|
||||
|
||||
fn hdivider<'a>() -> Element<'a, Message> {
|
||||
container(Space::new().width(Fill).height(1))
|
||||
.width(Fill).height(1)
|
||||
.width(Fill)
|
||||
.height(1)
|
||||
.style(|_: &Theme| container::Style {
|
||||
background: Some(Background::Color(BORDER)),
|
||||
..Default::default()
|
||||
|
|
@ -87,7 +168,7 @@ pub fn view_window<'a>(
|
|||
.style(btn(true))
|
||||
.padding([4, 20]),
|
||||
]
|
||||
.align_y(iced::Center)
|
||||
.align_y(iced::Center),
|
||||
)
|
||||
.style(|_: &Theme| container::Style {
|
||||
background: Some(Background::Color(TB)),
|
||||
|
|
@ -100,42 +181,110 @@ pub fn view_window<'a>(
|
|||
|
||||
// ── Paper size presets ────────────────────────────────────────────────
|
||||
let presets1 = row![
|
||||
button(text("A4 P").size(10)).on_press(Message::PageSetupPreset("A4 Portrait".into())).style(pill(false)).padding([3, 6]),
|
||||
button(text("A4 L").size(10)).on_press(Message::PageSetupPreset("A4 Landscape".into())).style(pill(false)).padding([3, 6]),
|
||||
button(text("A3 P").size(10)).on_press(Message::PageSetupPreset("A3 Portrait".into())).style(pill(false)).padding([3, 6]),
|
||||
button(text("A3 L").size(10)).on_press(Message::PageSetupPreset("A3 Landscape".into())).style(pill(false)).padding([3, 6]),
|
||||
].spacing(4);
|
||||
button(text("A4 P").size(10))
|
||||
.on_press(Message::PageSetupPreset("A4 Portrait".into()))
|
||||
.style(pill(false))
|
||||
.padding([3, 6]),
|
||||
button(text("A4 L").size(10))
|
||||
.on_press(Message::PageSetupPreset("A4 Landscape".into()))
|
||||
.style(pill(false))
|
||||
.padding([3, 6]),
|
||||
button(text("A3 P").size(10))
|
||||
.on_press(Message::PageSetupPreset("A3 Portrait".into()))
|
||||
.style(pill(false))
|
||||
.padding([3, 6]),
|
||||
button(text("A3 L").size(10))
|
||||
.on_press(Message::PageSetupPreset("A3 Landscape".into()))
|
||||
.style(pill(false))
|
||||
.padding([3, 6]),
|
||||
]
|
||||
.spacing(4);
|
||||
|
||||
let presets2 = row![
|
||||
button(text("A2 L").size(10)).on_press(Message::PageSetupPreset("A2 Landscape".into())).style(pill(false)).padding([3, 6]),
|
||||
button(text("A1 L").size(10)).on_press(Message::PageSetupPreset("A1 Landscape".into())).style(pill(false)).padding([3, 6]),
|
||||
button(text("A0 L").size(10)).on_press(Message::PageSetupPreset("A0 Landscape".into())).style(pill(false)).padding([3, 6]),
|
||||
button(text("Letter").size(10)).on_press(Message::PageSetupPreset("Letter Landscape".into())).style(pill(false)).padding([3, 6]),
|
||||
].spacing(4);
|
||||
button(text("A2 L").size(10))
|
||||
.on_press(Message::PageSetupPreset("A2 Landscape".into()))
|
||||
.style(pill(false))
|
||||
.padding([3, 6]),
|
||||
button(text("A1 L").size(10))
|
||||
.on_press(Message::PageSetupPreset("A1 Landscape".into()))
|
||||
.style(pill(false))
|
||||
.padding([3, 6]),
|
||||
button(text("A0 L").size(10))
|
||||
.on_press(Message::PageSetupPreset("A0 Landscape".into()))
|
||||
.style(pill(false))
|
||||
.padding([3, 6]),
|
||||
button(text("Letter").size(10))
|
||||
.on_press(Message::PageSetupPreset("Letter Landscape".into()))
|
||||
.style(pill(false))
|
||||
.padding([3, 6]),
|
||||
]
|
||||
.spacing(4);
|
||||
|
||||
// ── Rotation buttons ──────────────────────────────────────────────────
|
||||
let rot_row = row![
|
||||
button(text("0°").size(10)).on_press(Message::PageSetupRotation("0".into())).style(pill(rotation == "0")).padding([3, 8]),
|
||||
button(text("90°").size(10)).on_press(Message::PageSetupRotation("90".into())).style(pill(rotation == "90")).padding([3, 8]),
|
||||
button(text("180°").size(10)).on_press(Message::PageSetupRotation("180".into())).style(pill(rotation == "180")).padding([3, 8]),
|
||||
button(text("270°").size(10)).on_press(Message::PageSetupRotation("270".into())).style(pill(rotation == "270")).padding([3, 8]),
|
||||
].spacing(4);
|
||||
button(text("0°").size(10))
|
||||
.on_press(Message::PageSetupRotation("0".into()))
|
||||
.style(pill(rotation == "0"))
|
||||
.padding([3, 8]),
|
||||
button(text("90°").size(10))
|
||||
.on_press(Message::PageSetupRotation("90".into()))
|
||||
.style(pill(rotation == "90"))
|
||||
.padding([3, 8]),
|
||||
button(text("180°").size(10))
|
||||
.on_press(Message::PageSetupRotation("180".into()))
|
||||
.style(pill(rotation == "180"))
|
||||
.padding([3, 8]),
|
||||
button(text("270°").size(10))
|
||||
.on_press(Message::PageSetupRotation("270".into()))
|
||||
.style(pill(rotation == "270"))
|
||||
.padding([3, 8]),
|
||||
]
|
||||
.spacing(4);
|
||||
|
||||
// ── Scale buttons ─────────────────────────────────────────────────────
|
||||
let scale_row1 = row![
|
||||
button(text("Fit").size(10)).on_press(Message::PageSetupScale("Fit".into())).style(pill(scale == "Fit")).padding([3, 8]),
|
||||
button(text("1:1").size(10)).on_press(Message::PageSetupScale("1:1".into())).style(pill(scale == "1:1")).padding([3, 8]),
|
||||
button(text("1:2").size(10)).on_press(Message::PageSetupScale("1:2".into())).style(pill(scale == "1:2")).padding([3, 8]),
|
||||
button(text("1:5").size(10)).on_press(Message::PageSetupScale("1:5".into())).style(pill(scale == "1:5")).padding([3, 8]),
|
||||
button(text("1:10").size(10)).on_press(Message::PageSetupScale("1:10".into())).style(pill(scale == "1:10")).padding([3, 8]),
|
||||
].spacing(4);
|
||||
button(text("Fit").size(10))
|
||||
.on_press(Message::PageSetupScale("Fit".into()))
|
||||
.style(pill(scale == "Fit"))
|
||||
.padding([3, 8]),
|
||||
button(text("1:1").size(10))
|
||||
.on_press(Message::PageSetupScale("1:1".into()))
|
||||
.style(pill(scale == "1:1"))
|
||||
.padding([3, 8]),
|
||||
button(text("1:2").size(10))
|
||||
.on_press(Message::PageSetupScale("1:2".into()))
|
||||
.style(pill(scale == "1:2"))
|
||||
.padding([3, 8]),
|
||||
button(text("1:5").size(10))
|
||||
.on_press(Message::PageSetupScale("1:5".into()))
|
||||
.style(pill(scale == "1:5"))
|
||||
.padding([3, 8]),
|
||||
button(text("1:10").size(10))
|
||||
.on_press(Message::PageSetupScale("1:10".into()))
|
||||
.style(pill(scale == "1:10"))
|
||||
.padding([3, 8]),
|
||||
]
|
||||
.spacing(4);
|
||||
|
||||
let scale_row2 = row![
|
||||
button(text("1:20").size(10)).on_press(Message::PageSetupScale("1:20".into())).style(pill(scale == "1:20")).padding([3, 8]),
|
||||
button(text("1:50").size(10)).on_press(Message::PageSetupScale("1:50".into())).style(pill(scale == "1:50")).padding([3, 8]),
|
||||
button(text("1:100").size(10)).on_press(Message::PageSetupScale("1:100".into())).style(pill(scale == "1:100")).padding([3, 8]),
|
||||
button(text("2:1").size(10)).on_press(Message::PageSetupScale("2:1".into())).style(pill(scale == "2:1")).padding([3, 8]),
|
||||
].spacing(4);
|
||||
button(text("1:20").size(10))
|
||||
.on_press(Message::PageSetupScale("1:20".into()))
|
||||
.style(pill(scale == "1:20"))
|
||||
.padding([3, 8]),
|
||||
button(text("1:50").size(10))
|
||||
.on_press(Message::PageSetupScale("1:50".into()))
|
||||
.style(pill(scale == "1:50"))
|
||||
.padding([3, 8]),
|
||||
button(text("1:100").size(10))
|
||||
.on_press(Message::PageSetupScale("1:100".into()))
|
||||
.style(pill(scale == "1:100"))
|
||||
.padding([3, 8]),
|
||||
button(text("2:1").size(10))
|
||||
.on_press(Message::PageSetupScale("2:1".into()))
|
||||
.style(pill(scale == "2:1"))
|
||||
.padding([3, 8]),
|
||||
]
|
||||
.spacing(4);
|
||||
|
||||
// ── Main scrollable form ──────────────────────────────────────────────
|
||||
let form = column![
|
||||
|
|
@ -144,34 +293,72 @@ pub fn view_window<'a>(
|
|||
presets2,
|
||||
row![
|
||||
lbl("Width (mm)"),
|
||||
text_input("297", w_buf).on_input(Message::PageSetupWidthEdit).on_submit(Message::PageSetupCommit).style(field_style).size(12).width(90),
|
||||
].spacing(8).align_y(iced::Center),
|
||||
text_input("297", w_buf)
|
||||
.on_input(Message::PageSetupWidthEdit)
|
||||
.on_submit(Message::PageSetupCommit)
|
||||
.style(field_style)
|
||||
.size(12)
|
||||
.width(90),
|
||||
]
|
||||
.spacing(8)
|
||||
.align_y(iced::Center),
|
||||
row![
|
||||
lbl("Height (mm)"),
|
||||
text_input("210", h_buf).on_input(Message::PageSetupHeightEdit).on_submit(Message::PageSetupCommit).style(field_style).size(12).width(90),
|
||||
].spacing(8).align_y(iced::Center),
|
||||
text_input("210", h_buf)
|
||||
.on_input(Message::PageSetupHeightEdit)
|
||||
.on_submit(Message::PageSetupCommit)
|
||||
.style(field_style)
|
||||
.size(12)
|
||||
.width(90),
|
||||
]
|
||||
.spacing(8)
|
||||
.align_y(iced::Center),
|
||||
hdivider(),
|
||||
section_label("Plot Area"),
|
||||
row![
|
||||
button(text("Layout").size(10)).on_press(Message::PageSetupPlotArea("Layout".into())).style(pill(plot_area == "Layout")).padding([3, 8]),
|
||||
button(text("Extents").size(10)).on_press(Message::PageSetupPlotArea("Extents".into())).style(pill(plot_area == "Extents")).padding([3, 8]),
|
||||
].spacing(6),
|
||||
button(text("Layout").size(10))
|
||||
.on_press(Message::PageSetupPlotArea("Layout".into()))
|
||||
.style(pill(plot_area == "Layout"))
|
||||
.padding([3, 8]),
|
||||
button(text("Extents").size(10))
|
||||
.on_press(Message::PageSetupPlotArea("Extents".into()))
|
||||
.style(pill(plot_area == "Extents"))
|
||||
.padding([3, 8]),
|
||||
]
|
||||
.spacing(6),
|
||||
hdivider(),
|
||||
section_label("Position"),
|
||||
button(
|
||||
text(if center { "✓ Center on page" } else { " Center on page" }).size(11)
|
||||
text(if center {
|
||||
"✓ Center on page"
|
||||
} else {
|
||||
" Center on page"
|
||||
})
|
||||
.size(11)
|
||||
)
|
||||
.on_press(Message::PageSetupCenterToggle)
|
||||
.style(pill(center))
|
||||
.padding([4, 10]),
|
||||
row![
|
||||
lbl("Offset X (mm)"),
|
||||
text_input("0", offset_x).on_input(Message::PageSetupOffsetXEdit).style(field_style).size(12).width(90),
|
||||
].spacing(8).align_y(iced::Center),
|
||||
text_input("0", offset_x)
|
||||
.on_input(Message::PageSetupOffsetXEdit)
|
||||
.style(field_style)
|
||||
.size(12)
|
||||
.width(90),
|
||||
]
|
||||
.spacing(8)
|
||||
.align_y(iced::Center),
|
||||
row![
|
||||
lbl("Offset Y (mm)"),
|
||||
text_input("0", offset_y).on_input(Message::PageSetupOffsetYEdit).style(field_style).size(12).width(90),
|
||||
].spacing(8).align_y(iced::Center),
|
||||
text_input("0", offset_y)
|
||||
.on_input(Message::PageSetupOffsetYEdit)
|
||||
.style(field_style)
|
||||
.size(12)
|
||||
.width(90),
|
||||
]
|
||||
.spacing(8)
|
||||
.align_y(iced::Center),
|
||||
hdivider(),
|
||||
section_label("Rotation"),
|
||||
rot_row,
|
||||
|
|
@ -186,14 +373,12 @@ pub fn view_window<'a>(
|
|||
|
||||
let content = scrollable(form).width(Fill).height(Fill);
|
||||
|
||||
container(
|
||||
column![toolbar, hdivider(), content].spacing(0)
|
||||
)
|
||||
.style(|_: &Theme| container::Style {
|
||||
background: Some(Background::Color(BG)),
|
||||
..Default::default()
|
||||
})
|
||||
.width(Fill)
|
||||
.height(Fill)
|
||||
.into()
|
||||
container(column![toolbar, hdivider(), content].spacing(0))
|
||||
.style(|_: &Theme| container::Style {
|
||||
background: Some(Background::Color(BG)),
|
||||
..Default::default()
|
||||
})
|
||||
.width(Fill)
|
||||
.height(Fill)
|
||||
.into()
|
||||
}
|
||||
|
|
|
|||
|
|
@ -4,26 +4,90 @@ use crate::app::Message;
|
|||
use iced::widget::{button, column, container, row, scrollable, text, text_input, Space};
|
||||
use iced::{Background, Border, Color, Element, Fill, Theme};
|
||||
|
||||
const TB: Color = Color { r: 0.13, g: 0.13, b: 0.13, a: 1.0 };
|
||||
const BG: Color = Color { r: 0.15, g: 0.15, b: 0.15, a: 1.0 };
|
||||
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 };
|
||||
const ACTIVE: Color = Color { r: 0.20, g: 0.40, b: 0.70, a: 1.0 };
|
||||
const FIELD: Color = Color { r: 0.10, g: 0.10, b: 0.10, a: 1.0 };
|
||||
const LIST: Color = Color { r: 0.12, g: 0.12, b: 0.12, a: 1.0 };
|
||||
const TB: Color = Color {
|
||||
r: 0.13,
|
||||
g: 0.13,
|
||||
b: 0.13,
|
||||
a: 1.0,
|
||||
};
|
||||
const BG: Color = Color {
|
||||
r: 0.15,
|
||||
g: 0.15,
|
||||
b: 0.15,
|
||||
a: 1.0,
|
||||
};
|
||||
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,
|
||||
};
|
||||
const ACTIVE: Color = Color {
|
||||
r: 0.20,
|
||||
g: 0.40,
|
||||
b: 0.70,
|
||||
a: 1.0,
|
||||
};
|
||||
const FIELD: Color = Color {
|
||||
r: 0.10,
|
||||
g: 0.10,
|
||||
b: 0.10,
|
||||
a: 1.0,
|
||||
};
|
||||
const LIST: Color = Color {
|
||||
r: 0.12,
|
||||
g: 0.12,
|
||||
b: 0.12,
|
||||
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 },
|
||||
(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() },
|
||||
border: Border {
|
||||
color: BORDER,
|
||||
width: 1.0,
|
||||
radius: 4.0.into(),
|
||||
},
|
||||
..Default::default()
|
||||
}
|
||||
}
|
||||
|
|
@ -31,14 +95,22 @@ fn btn_s(accent: bool) -> impl Fn(&Theme, button::Status) -> button::Style {
|
|||
fn field_style(_: &Theme, _: text_input::Status) -> text_input::Style {
|
||||
text_input::Style {
|
||||
background: Background::Color(FIELD),
|
||||
border: Border { color: BORDER, width: 1.0, radius: 3.0.into() },
|
||||
icon: TEXT, placeholder: DIM, value: TEXT, selection: ACCENT,
|
||||
border: Border {
|
||||
color: BORDER,
|
||||
width: 1.0,
|
||||
radius: 3.0.into(),
|
||||
},
|
||||
icon: TEXT,
|
||||
placeholder: DIM,
|
||||
value: TEXT,
|
||||
selection: ACCENT,
|
||||
}
|
||||
}
|
||||
|
||||
fn hdivider<'a>() -> Element<'a, Message> {
|
||||
container(Space::new().width(Fill).height(1))
|
||||
.width(Fill).height(1)
|
||||
.width(Fill)
|
||||
.height(1)
|
||||
.style(|_: &Theme| container::Style {
|
||||
background: Some(Background::Color(BORDER)),
|
||||
..Default::default()
|
||||
|
|
@ -48,7 +120,8 @@ fn hdivider<'a>() -> Element<'a, Message> {
|
|||
|
||||
fn vsep<'a>() -> Element<'a, Message> {
|
||||
container(Space::new().width(1).height(Fill))
|
||||
.width(1).height(Fill)
|
||||
.width(1)
|
||||
.height(Fill)
|
||||
.style(|_: &Theme| container::Style {
|
||||
background: Some(Background::Color(BORDER)),
|
||||
..Default::default()
|
||||
|
|
@ -63,19 +136,30 @@ pub fn view_window<'a>(
|
|||
lw_buf: &'a str,
|
||||
screen_buf: &'a str,
|
||||
) -> Element<'a, Message> {
|
||||
let table_name = table.map(|t| t.name.as_str()).unwrap_or("(no table loaded)");
|
||||
let table_name = table
|
||||
.map(|t| t.name.as_str())
|
||||
.unwrap_or("(no table loaded)");
|
||||
|
||||
// ── Toolbar ───────────────────────────────────────────────────────────
|
||||
let toolbar = container(
|
||||
row![
|
||||
button(text("Load CTB/STB").size(11)).on_press(Message::PlotStyleLoad).style(btn_s(false)).padding([4, 10]),
|
||||
button(text("Save As…").size(11)).on_press(Message::PlotStylePanelSave).style(btn_s(false)).padding([4, 10]),
|
||||
button(text("Clear Table").size(11)).on_press(Message::PlotStyleClear).style(btn_s(false)).padding([4, 10]),
|
||||
button(text("Load CTB/STB").size(11))
|
||||
.on_press(Message::PlotStyleLoad)
|
||||
.style(btn_s(false))
|
||||
.padding([4, 10]),
|
||||
button(text("Save As…").size(11))
|
||||
.on_press(Message::PlotStylePanelSave)
|
||||
.style(btn_s(false))
|
||||
.padding([4, 10]),
|
||||
button(text("Clear Table").size(11))
|
||||
.on_press(Message::PlotStyleClear)
|
||||
.style(btn_s(false))
|
||||
.padding([4, 10]),
|
||||
Space::new().width(Fill),
|
||||
text(table_name).size(10).color(DIM),
|
||||
]
|
||||
.spacing(4)
|
||||
.align_y(iced::Center)
|
||||
.align_y(iced::Center),
|
||||
)
|
||||
.style(|_: &Theme| container::Style {
|
||||
background: Some(Background::Color(TB)),
|
||||
|
|
@ -85,73 +169,105 @@ pub fn view_window<'a>(
|
|||
.padding([5, 8]);
|
||||
|
||||
// ── Left: ACI list ────────────────────────────────────────────────────
|
||||
let aci_items: Vec<Element<'_, Message>> = (1u8..=255).map(|aci| {
|
||||
let is_sel = aci == selected_aci;
|
||||
let has_override = table.and_then(|t| t.aci_entries.get(aci as usize))
|
||||
.map(|e| e.color.is_some() || e.lineweight != 255 || e.screening != 100)
|
||||
.unwrap_or(false);
|
||||
let lw_str = table.and_then(|t| t.aci_entries.get(aci as usize))
|
||||
.and_then(|e| {
|
||||
if e.lineweight != 255 {
|
||||
crate::io::plot_style::LW_TABLE.get(e.lineweight as usize)
|
||||
.map(|lw| format!("{lw:.2}mm"))
|
||||
} else { None }
|
||||
}).unwrap_or_default();
|
||||
let color_str = table.and_then(|t| t.aci_entries.get(aci as usize))
|
||||
.and_then(|e| e.color.map(|[r, g, b]| format!("#{r:02X}{g:02X}{b:02X}")))
|
||||
.unwrap_or_default();
|
||||
let label = if has_override {
|
||||
format!("{aci:>3} {color_str:<9} {lw_str}")
|
||||
} else {
|
||||
format!("{aci:>3} (default)")
|
||||
};
|
||||
button(text(label).size(10).font(iced::Font::MONOSPACE))
|
||||
.on_press(Message::PlotStylePanelSelectAci(aci))
|
||||
.style(move |_: &Theme, st| button::Style {
|
||||
background: Some(Background::Color(match (is_sel, st) {
|
||||
(true, _) => ACTIVE,
|
||||
(false, button::Status::Hovered | button::Status::Pressed) => Color { r: 0.26, g: 0.26, b: 0.26, a: 1.0 },
|
||||
_ => Color::TRANSPARENT,
|
||||
})),
|
||||
text_color: TEXT,
|
||||
..Default::default()
|
||||
})
|
||||
.padding([2, 8])
|
||||
.width(Fill)
|
||||
.into()
|
||||
}).collect();
|
||||
let aci_items: Vec<Element<'_, Message>> = (1u8..=255)
|
||||
.map(|aci| {
|
||||
let is_sel = aci == selected_aci;
|
||||
let has_override = table
|
||||
.and_then(|t| t.aci_entries.get(aci as usize))
|
||||
.map(|e| e.color.is_some() || e.lineweight != 255 || e.screening != 100)
|
||||
.unwrap_or(false);
|
||||
let lw_str = table
|
||||
.and_then(|t| t.aci_entries.get(aci as usize))
|
||||
.and_then(|e| {
|
||||
if e.lineweight != 255 {
|
||||
crate::io::plot_style::LW_TABLE
|
||||
.get(e.lineweight as usize)
|
||||
.map(|lw| format!("{lw:.2}mm"))
|
||||
} else {
|
||||
None
|
||||
}
|
||||
})
|
||||
.unwrap_or_default();
|
||||
let color_str = table
|
||||
.and_then(|t| t.aci_entries.get(aci as usize))
|
||||
.and_then(|e| e.color.map(|[r, g, b]| format!("#{r:02X}{g:02X}{b:02X}")))
|
||||
.unwrap_or_default();
|
||||
let label = if has_override {
|
||||
format!("{aci:>3} {color_str:<9} {lw_str}")
|
||||
} else {
|
||||
format!("{aci:>3} (default)")
|
||||
};
|
||||
button(text(label).size(10).font(iced::Font::MONOSPACE))
|
||||
.on_press(Message::PlotStylePanelSelectAci(aci))
|
||||
.style(move |_: &Theme, st| button::Style {
|
||||
background: Some(Background::Color(match (is_sel, st) {
|
||||
(true, _) => ACTIVE,
|
||||
(false, button::Status::Hovered | button::Status::Pressed) => Color {
|
||||
r: 0.26,
|
||||
g: 0.26,
|
||||
b: 0.26,
|
||||
a: 1.0,
|
||||
},
|
||||
_ => Color::TRANSPARENT,
|
||||
})),
|
||||
text_color: TEXT,
|
||||
..Default::default()
|
||||
})
|
||||
.padding([2, 8])
|
||||
.width(Fill)
|
||||
.into()
|
||||
})
|
||||
.collect();
|
||||
|
||||
let aci_list = container(
|
||||
column![
|
||||
text("ACI Color Index").size(10).color(DIM),
|
||||
container(
|
||||
scrollable(column(aci_items).spacing(1)).height(Fill)
|
||||
)
|
||||
.style(|_: &Theme| container::Style {
|
||||
background: Some(Background::Color(LIST)),
|
||||
border: Border { color: BORDER, width: 1.0, radius: 3.0.into() },
|
||||
..Default::default()
|
||||
})
|
||||
.width(Fill)
|
||||
.height(Fill)
|
||||
.padding(2),
|
||||
container(scrollable(column(aci_items).spacing(1)).height(Fill))
|
||||
.style(|_: &Theme| container::Style {
|
||||
background: Some(Background::Color(LIST)),
|
||||
border: Border {
|
||||
color: BORDER,
|
||||
width: 1.0,
|
||||
radius: 3.0.into()
|
||||
},
|
||||
..Default::default()
|
||||
})
|
||||
.width(Fill)
|
||||
.height(Fill)
|
||||
.padding(2),
|
||||
]
|
||||
.spacing(4)
|
||||
.height(Fill)
|
||||
.height(Fill),
|
||||
)
|
||||
.width(280)
|
||||
.height(Fill)
|
||||
.padding(iced::Padding { top: 12.0, right: 8.0, bottom: 12.0, left: 12.0 });
|
||||
.padding(iced::Padding {
|
||||
top: 12.0,
|
||||
right: 8.0,
|
||||
bottom: 12.0,
|
||||
left: 12.0,
|
||||
});
|
||||
|
||||
// ── Right: Edit panel ─────────────────────────────────────────────────
|
||||
let entry = table.and_then(|t| t.aci_entries.get(selected_aci as usize));
|
||||
let cur_color = entry.and_then(|e| e.color.map(|[r,g,b]| format!("#{r:02X}{g:02X}{b:02X}"))).unwrap_or_else(|| "(none)".into());
|
||||
let cur_lw = entry.map(|e| if e.lineweight == 255 { "object".into() } else {
|
||||
crate::io::plot_style::LW_TABLE.get(e.lineweight as usize)
|
||||
.map(|lw| format!("{lw:.2}mm (idx {})", e.lineweight))
|
||||
.unwrap_or_else(|| format!("idx {}", e.lineweight))
|
||||
}).unwrap_or_else(|| "—".into());
|
||||
let cur_scr = entry.map(|e| format!("{}%", e.screening)).unwrap_or_else(|| "—".into());
|
||||
let cur_color = entry
|
||||
.and_then(|e| e.color.map(|[r, g, b]| format!("#{r:02X}{g:02X}{b:02X}")))
|
||||
.unwrap_or_else(|| "(none)".into());
|
||||
let cur_lw = entry
|
||||
.map(|e| {
|
||||
if e.lineweight == 255 {
|
||||
"object".into()
|
||||
} else {
|
||||
crate::io::plot_style::LW_TABLE
|
||||
.get(e.lineweight as usize)
|
||||
.map(|lw| format!("{lw:.2}mm (idx {})", e.lineweight))
|
||||
.unwrap_or_else(|| format!("idx {}", e.lineweight))
|
||||
}
|
||||
})
|
||||
.unwrap_or_else(|| "—".into());
|
||||
let cur_scr = entry
|
||||
.map(|e| format!("{}%", e.screening))
|
||||
.unwrap_or_else(|| "—".into());
|
||||
|
||||
let lbl = |s: &'static str| text(s).size(11).color(DIM);
|
||||
|
||||
|
|
@ -160,19 +276,27 @@ pub fn view_window<'a>(
|
|||
row![
|
||||
text("ACI:").size(11).color(DIM).width(100),
|
||||
text(format!("{selected_aci}")).size(11),
|
||||
].spacing(8).align_y(iced::Center),
|
||||
]
|
||||
.spacing(8)
|
||||
.align_y(iced::Center),
|
||||
lbl("Color override (#RRGGBB):"),
|
||||
text_input("#RRGGBB or blank", color_buf)
|
||||
.on_input(Message::PlotStylePanelColorBuf)
|
||||
.style(field_style).size(11).padding([4, 8]),
|
||||
.style(field_style)
|
||||
.size(11)
|
||||
.padding([4, 8]),
|
||||
lbl("Lineweight index (0-24, 255=obj):"),
|
||||
text_input("255", lw_buf)
|
||||
.on_input(Message::PlotStylePanelLwBuf)
|
||||
.style(field_style).size(11).padding([4, 8]),
|
||||
.style(field_style)
|
||||
.size(11)
|
||||
.padding([4, 8]),
|
||||
lbl("Screening (0-100):"),
|
||||
text_input("100", screen_buf)
|
||||
.on_input(Message::PlotStylePanelScreenBuf)
|
||||
.style(field_style).size(11).padding([4, 8]),
|
||||
.style(field_style)
|
||||
.size(11)
|
||||
.padding([4, 8]),
|
||||
Space::new().height(8),
|
||||
text("Current values:").size(10).color(DIM),
|
||||
text(format!(" Color: {cur_color}")).size(10),
|
||||
|
|
@ -185,7 +309,7 @@ pub fn view_window<'a>(
|
|||
.padding([5, 10]),
|
||||
]
|
||||
.spacing(8)
|
||||
.height(Fill)
|
||||
.height(Fill),
|
||||
)
|
||||
.width(Fill)
|
||||
.height(Fill)
|
||||
|
|
@ -193,14 +317,12 @@ pub fn view_window<'a>(
|
|||
|
||||
let body = row![aci_list, vsep(), edit_panel].height(Fill);
|
||||
|
||||
container(
|
||||
column![toolbar, hdivider(), body].spacing(0)
|
||||
)
|
||||
.style(|_: &Theme| container::Style {
|
||||
background: Some(Background::Color(BG)),
|
||||
..Default::default()
|
||||
})
|
||||
.width(Fill)
|
||||
.height(Fill)
|
||||
.into()
|
||||
container(column![toolbar, hdivider(), body].spacing(0))
|
||||
.style(|_: &Theme| container::Style {
|
||||
background: Some(Background::Color(BG)),
|
||||
..Default::default()
|
||||
})
|
||||
.width(Fill)
|
||||
.height(Fill)
|
||||
.into()
|
||||
}
|
||||
|
|
|
|||
|
|
@ -10,19 +10,19 @@
|
|||
use std::collections::HashMap;
|
||||
use std::fmt;
|
||||
|
||||
use crate::ui::ROW_H;
|
||||
use acadrust::types::{Color as AcadColor, LineWeight};
|
||||
use acadrust::Handle;
|
||||
use iced::widget::{button, column, combo_box, container, row, scrollable, text, text_input};
|
||||
use iced::{Background, Border, Color, Element, Length, Padding, Theme};
|
||||
use crate::ui::ROW_H;
|
||||
|
||||
// ── Row-height-derived constants ─────────────────────────────────────────
|
||||
const FONT_SZ: f32 = ROW_H * 0.42; // ≈11 px
|
||||
const COMBO_PAD_V: f32 = (ROW_H - FONT_SZ * 1.3 - 2.0) / 2.0; // fills combo to ROW_H
|
||||
const SWATCH_SZ: f32 = ROW_H * 0.54; // ≈14 px color swatch
|
||||
const FONT_SZ: f32 = ROW_H * 0.42; // ≈11 px
|
||||
const COMBO_PAD_V: f32 = (ROW_H - FONT_SZ * 1.3 - 2.0) / 2.0; // fills combo to ROW_H
|
||||
const SWATCH_SZ: f32 = ROW_H * 0.54; // ≈14 px color swatch
|
||||
|
||||
use crate::scene::object::{PropSection, PropValue};
|
||||
use crate::app::Message;
|
||||
use crate::scene::object::{PropSection, PropValue};
|
||||
|
||||
const VARIES_LABEL: &str = "*VARIES*";
|
||||
|
||||
|
|
@ -321,7 +321,12 @@ impl PropertiesPanel {
|
|||
Message::PropLayerChanged,
|
||||
)
|
||||
.size(FONT_SZ)
|
||||
.padding(Padding { top: COMBO_PAD_V, bottom: COMBO_PAD_V, left: 6.0, right: 6.0 })
|
||||
.padding(Padding {
|
||||
top: COMBO_PAD_V,
|
||||
bottom: COMBO_PAD_V,
|
||||
left: 6.0,
|
||||
right: 6.0,
|
||||
})
|
||||
.input_style(combo_input_style)
|
||||
.width(Length::Fill);
|
||||
|
||||
|
|
@ -360,7 +365,12 @@ impl PropertiesPanel {
|
|||
)
|
||||
.on_press(Message::PropColorPickerToggle)
|
||||
.style(combo_btn_style)
|
||||
.padding(Padding { top: COMBO_PAD_V, bottom: COMBO_PAD_V, left: 6.0, right: 6.0 })
|
||||
.padding(Padding {
|
||||
top: COMBO_PAD_V,
|
||||
bottom: COMBO_PAD_V,
|
||||
left: 6.0,
|
||||
right: 6.0,
|
||||
})
|
||||
.width(Length::Fill);
|
||||
|
||||
let color_row = prop_row_widget(label, color_btn.into());
|
||||
|
|
@ -409,7 +419,12 @@ impl PropertiesPanel {
|
|||
)
|
||||
.on_press(Message::PropColorPickerToggle)
|
||||
.style(combo_btn_style)
|
||||
.padding(Padding { top: COMBO_PAD_V, bottom: COMBO_PAD_V, left: 6.0, right: 6.0 })
|
||||
.padding(Padding {
|
||||
top: COMBO_PAD_V,
|
||||
bottom: COMBO_PAD_V,
|
||||
left: 6.0,
|
||||
right: 6.0,
|
||||
})
|
||||
.width(Length::Fill);
|
||||
|
||||
let color_row = prop_row_widget(label, color_btn.into());
|
||||
|
|
@ -443,7 +458,12 @@ impl PropertiesPanel {
|
|||
|item: LwItem| Message::PropLwChanged(item.0),
|
||||
)
|
||||
.size(FONT_SZ)
|
||||
.padding(Padding { top: COMBO_PAD_V, bottom: COMBO_PAD_V, left: 6.0, right: 6.0 })
|
||||
.padding(Padding {
|
||||
top: COMBO_PAD_V,
|
||||
bottom: COMBO_PAD_V,
|
||||
left: 6.0,
|
||||
right: 6.0,
|
||||
})
|
||||
.input_style(combo_input_style)
|
||||
.width(Length::Fill);
|
||||
|
||||
|
|
@ -458,7 +478,12 @@ impl PropertiesPanel {
|
|||
|item: LwItem| Message::PropLwChanged(item.0),
|
||||
)
|
||||
.size(FONT_SZ)
|
||||
.padding(Padding { top: COMBO_PAD_V, bottom: COMBO_PAD_V, left: 6.0, right: 6.0 })
|
||||
.padding(Padding {
|
||||
top: COMBO_PAD_V,
|
||||
bottom: COMBO_PAD_V,
|
||||
left: 6.0,
|
||||
right: 6.0,
|
||||
})
|
||||
.input_style(combo_input_style)
|
||||
.width(Length::Fill);
|
||||
|
||||
|
|
@ -486,7 +511,12 @@ impl PropertiesPanel {
|
|||
|item: LinetypeItem| Message::PropLinetypeChanged(item.name),
|
||||
)
|
||||
.size(FONT_SZ)
|
||||
.padding(Padding { top: COMBO_PAD_V, bottom: COMBO_PAD_V, left: 6.0, right: 6.0 })
|
||||
.padding(Padding {
|
||||
top: COMBO_PAD_V,
|
||||
bottom: COMBO_PAD_V,
|
||||
left: 6.0,
|
||||
right: 6.0,
|
||||
})
|
||||
.input_style(combo_input_style)
|
||||
.width(Length::Fill);
|
||||
|
||||
|
|
@ -513,7 +543,12 @@ impl PropertiesPanel {
|
|||
Message::PropGeomChoiceChanged { field, value }
|
||||
})
|
||||
.size(FONT_SZ)
|
||||
.padding(Padding { top: COMBO_PAD_V, bottom: COMBO_PAD_V, left: 6.0, right: 6.0 })
|
||||
.padding(Padding {
|
||||
top: COMBO_PAD_V,
|
||||
bottom: COMBO_PAD_V,
|
||||
left: 6.0,
|
||||
right: 6.0,
|
||||
})
|
||||
.input_style(combo_input_style)
|
||||
.width(Length::Fill);
|
||||
|
||||
|
|
@ -562,7 +597,12 @@ impl PropertiesPanel {
|
|||
Message::PropHatchPatternChanged,
|
||||
)
|
||||
.size(FONT_SZ)
|
||||
.padding(Padding { top: COMBO_PAD_V, bottom: COMBO_PAD_V, left: 6.0, right: 6.0 })
|
||||
.padding(Padding {
|
||||
top: COMBO_PAD_V,
|
||||
bottom: COMBO_PAD_V,
|
||||
left: 6.0,
|
||||
right: 6.0,
|
||||
})
|
||||
.input_style(combo_input_style)
|
||||
.width(Length::Fill);
|
||||
|
||||
|
|
@ -583,17 +623,19 @@ pub fn color_picker_dropdown<'a>(
|
|||
on_aci: impl Fn(u8) -> Message + 'a,
|
||||
) -> Element<'a, Message> {
|
||||
// ByLayer / ByBlock row (optional)
|
||||
let extras: Option<Element<'a, Message>> =
|
||||
match (by_layer_msg, by_block_msg) {
|
||||
(Some(bl), Some(bb)) => Some(
|
||||
row![picker_text_btn("ByLayer", bl), picker_text_btn("ByBlock", bb)]
|
||||
.spacing(4)
|
||||
.into(),
|
||||
),
|
||||
(Some(bl), None) => Some(picker_text_btn("ByLayer", bl)),
|
||||
(None, Some(bb)) => Some(picker_text_btn("ByBlock", bb)),
|
||||
(None, None) => None,
|
||||
};
|
||||
let extras: Option<Element<'a, Message>> = match (by_layer_msg, by_block_msg) {
|
||||
(Some(bl), Some(bb)) => Some(
|
||||
row![
|
||||
picker_text_btn("ByLayer", bl),
|
||||
picker_text_btn("ByBlock", bb)
|
||||
]
|
||||
.spacing(4)
|
||||
.into(),
|
||||
),
|
||||
(Some(bl), None) => Some(picker_text_btn("ByLayer", bl)),
|
||||
(None, Some(bb)) => Some(picker_text_btn("ByBlock", bb)),
|
||||
(None, None) => None,
|
||||
};
|
||||
|
||||
// 9 standard ACI swatches (1-9)
|
||||
let standard: Element<'a, Message> = (1u8..=9u8)
|
||||
|
|
@ -612,7 +654,11 @@ pub fn color_picker_dropdown<'a>(
|
|||
} else {
|
||||
Color::BLACK
|
||||
},
|
||||
width: if matches!(status, button::Status::Hovered) { 1.5 } else { 1.0 },
|
||||
width: if matches!(status, button::Status::Hovered) {
|
||||
1.5
|
||||
} else {
|
||||
1.0
|
||||
},
|
||||
radius: 2.0.into(),
|
||||
},
|
||||
..Default::default()
|
||||
|
|
@ -624,9 +670,13 @@ pub fn color_picker_dropdown<'a>(
|
|||
|
||||
// "More Colors…" toggle button
|
||||
let more_btn = button(
|
||||
text(if palette_open { "▲ Less" } else { "▼ More Colors…" })
|
||||
.size(10)
|
||||
.color(HINT_COLOR),
|
||||
text(if palette_open {
|
||||
"▲ Less"
|
||||
} else {
|
||||
"▼ More Colors…"
|
||||
})
|
||||
.size(10)
|
||||
.color(HINT_COLOR),
|
||||
)
|
||||
.on_press(palette_toggle_msg)
|
||||
.style(|_: &Theme, _| button::Style {
|
||||
|
|
@ -646,7 +696,11 @@ pub fn color_picker_dropdown<'a>(
|
|||
let mut col = column![container(inner)
|
||||
.style(|_: &Theme| container::Style {
|
||||
background: Some(Background::Color(PICKER_BG)),
|
||||
border: Border { color: BORDER, width: 1.0, radius: 0.0.into() },
|
||||
border: Border {
|
||||
color: BORDER,
|
||||
width: 1.0,
|
||||
radius: 0.0.into()
|
||||
},
|
||||
..Default::default()
|
||||
})
|
||||
.padding([6, 8])
|
||||
|
|
@ -661,7 +715,9 @@ pub fn color_picker_dropdown<'a>(
|
|||
while idx <= 255 {
|
||||
let mut r = row![].spacing(1);
|
||||
for _ in 0..COLS {
|
||||
if idx > 255 { break; }
|
||||
if idx > 255 {
|
||||
break;
|
||||
}
|
||||
let ci = idx as u8;
|
||||
let (bg, _) = acad_color_display(AcadColor::Index(ci));
|
||||
let msg = on_aci(ci);
|
||||
|
|
@ -674,9 +730,18 @@ pub fn color_picker_dropdown<'a>(
|
|||
color: if matches!(status, button::Status::Hovered) {
|
||||
Color::WHITE
|
||||
} else {
|
||||
Color { r: 0.0, g: 0.0, b: 0.0, a: 0.4 }
|
||||
Color {
|
||||
r: 0.0,
|
||||
g: 0.0,
|
||||
b: 0.0,
|
||||
a: 0.4,
|
||||
}
|
||||
},
|
||||
width: if matches!(status, button::Status::Hovered) {
|
||||
1.5
|
||||
} else {
|
||||
1.0
|
||||
},
|
||||
width: if matches!(status, button::Status::Hovered) { 1.5 } else { 1.0 },
|
||||
radius: 1.0.into(),
|
||||
},
|
||||
..Default::default()
|
||||
|
|
@ -691,7 +756,11 @@ pub fn color_picker_dropdown<'a>(
|
|||
container(scrollable(rows).height(160))
|
||||
.style(|_: &Theme| container::Style {
|
||||
background: Some(Background::Color(PICKER_BG)),
|
||||
border: Border { color: BORDER, width: 1.0, radius: 0.0.into() },
|
||||
border: Border {
|
||||
color: BORDER,
|
||||
width: 1.0,
|
||||
radius: 0.0.into(),
|
||||
},
|
||||
..Default::default()
|
||||
})
|
||||
.padding([4, 6])
|
||||
|
|
@ -707,30 +776,31 @@ pub fn color_picker_dropdown<'a>(
|
|||
/// A boolean toggle button row (for "Invisible" etc.).
|
||||
fn render_bool_row<'a>(label: &'a str, field: &'static str, value: bool) -> Element<'a, Message> {
|
||||
let btn_label = if value { "Yes" } else { "No" };
|
||||
let btn = button(
|
||||
text(btn_label)
|
||||
.size(FONT_SZ)
|
||||
.color(if value { WARN_COLOR } else { VALUE_COLOR }),
|
||||
)
|
||||
.on_press(Message::PropBoolToggle(field))
|
||||
.style(move |_: &Theme, status| {
|
||||
let bg = match status {
|
||||
button::Status::Hovered | button::Status::Pressed => HOVER_BG,
|
||||
_ => VALUE_BG,
|
||||
};
|
||||
button::Style {
|
||||
background: Some(Background::Color(bg)),
|
||||
border: Border {
|
||||
color: BORDER,
|
||||
width: 1.0,
|
||||
radius: 2.0.into(),
|
||||
},
|
||||
text_color: if value { WARN_COLOR } else { VALUE_COLOR },
|
||||
..Default::default()
|
||||
}
|
||||
})
|
||||
.padding([2, 6])
|
||||
.width(Length::Fill);
|
||||
let btn =
|
||||
button(
|
||||
text(btn_label)
|
||||
.size(FONT_SZ)
|
||||
.color(if value { WARN_COLOR } else { VALUE_COLOR }),
|
||||
)
|
||||
.on_press(Message::PropBoolToggle(field))
|
||||
.style(move |_: &Theme, status| {
|
||||
let bg = match status {
|
||||
button::Status::Hovered | button::Status::Pressed => HOVER_BG,
|
||||
_ => VALUE_BG,
|
||||
};
|
||||
button::Style {
|
||||
background: Some(Background::Color(bg)),
|
||||
border: Border {
|
||||
color: BORDER,
|
||||
width: 1.0,
|
||||
radius: 2.0.into(),
|
||||
},
|
||||
text_color: if value { WARN_COLOR } else { VALUE_COLOR },
|
||||
..Default::default()
|
||||
}
|
||||
})
|
||||
.padding([2, 6])
|
||||
.width(Length::Fill);
|
||||
|
||||
prop_row_widget(label, btn.into())
|
||||
}
|
||||
|
|
@ -744,7 +814,12 @@ fn render_ro_row<'a>(label: &'a str, value: &'a str) -> Element<'a, Message> {
|
|||
.width(Length::FillPortion(5))
|
||||
.height(Length::Fixed(ROW_H))
|
||||
.align_y(iced::Center)
|
||||
.padding(Padding { top: 0.0, bottom: 0.0, left: 6.0, right: 6.0 });
|
||||
.padding(Padding {
|
||||
top: 0.0,
|
||||
bottom: 0.0,
|
||||
left: 6.0,
|
||||
right: 6.0,
|
||||
});
|
||||
let value_col = container(text(value).size(FONT_SZ).color(VALUE_COLOR))
|
||||
.style(|_: &Theme| container::Style {
|
||||
background: Some(Background::Color(VALUE_BG)),
|
||||
|
|
@ -753,7 +828,12 @@ fn render_ro_row<'a>(label: &'a str, value: &'a str) -> Element<'a, Message> {
|
|||
.width(Length::FillPortion(6))
|
||||
.height(Length::Fixed(ROW_H))
|
||||
.align_y(iced::Center)
|
||||
.padding(Padding { top: 0.0, bottom: 0.0, left: 6.0, right: 6.0 });
|
||||
.padding(Padding {
|
||||
top: 0.0,
|
||||
bottom: 0.0,
|
||||
left: 6.0,
|
||||
right: 6.0,
|
||||
});
|
||||
container(row![label_col, value_col])
|
||||
.height(Length::Fixed(ROW_H))
|
||||
.style(|_: &Theme| container::Style {
|
||||
|
|
@ -777,7 +857,12 @@ fn prop_row_widget<'a>(label: &'a str, widget: Element<'a, Message>) -> Element<
|
|||
.width(Length::FillPortion(5))
|
||||
.height(Length::Fixed(ROW_H))
|
||||
.align_y(iced::Center)
|
||||
.padding(Padding { top: 0.0, bottom: 0.0, left: 6.0, right: 6.0 });
|
||||
.padding(Padding {
|
||||
top: 0.0,
|
||||
bottom: 0.0,
|
||||
left: 6.0,
|
||||
right: 6.0,
|
||||
});
|
||||
let value_col = container(widget)
|
||||
.style(|_: &Theme| container::Style {
|
||||
background: Some(Background::Color(VALUE_BG)),
|
||||
|
|
@ -786,7 +871,12 @@ fn prop_row_widget<'a>(label: &'a str, widget: Element<'a, Message>) -> Element<
|
|||
.width(Length::FillPortion(6))
|
||||
.height(Length::Fixed(ROW_H))
|
||||
.align_y(iced::Center)
|
||||
.padding(Padding { top: 0.0, bottom: 0.0, left: 2.0, right: 2.0 });
|
||||
.padding(Padding {
|
||||
top: 0.0,
|
||||
bottom: 0.0,
|
||||
left: 2.0,
|
||||
right: 2.0,
|
||||
});
|
||||
container(row![label_col, value_col])
|
||||
.height(Length::Fixed(ROW_H))
|
||||
.style(|_: &Theme| container::Style {
|
||||
|
|
|
|||
|
|
@ -12,10 +12,10 @@ use acadrust::types::{Color as AcadColor, LineWeight};
|
|||
use iced::widget::{button, column, container, mouse_area, row, scrollable, svg, text};
|
||||
use iced::{Background, Border, Color, Element, Fill, Length, Padding, Theme};
|
||||
|
||||
use crate::app::Message;
|
||||
use crate::modules::registry;
|
||||
use crate::modules::{CadModule, IconKind, RibbonItem};
|
||||
use crate::ui::properties::{LinetypeItem, color_picker_dropdown, lw_options};
|
||||
use crate::app::Message;
|
||||
use crate::ui::properties::{color_picker_dropdown, lw_options, LinetypeItem};
|
||||
|
||||
mod widgets;
|
||||
use widgets::{StyleContext, *};
|
||||
|
|
@ -104,10 +104,14 @@ impl Ribbon {
|
|||
|
||||
pub fn set_styles(
|
||||
&mut self,
|
||||
text: Vec<String>, active_text: &str,
|
||||
dim: Vec<String>, active_dim: &str,
|
||||
mleader: Vec<String>, active_mleader: &str,
|
||||
table: Vec<String>, active_table: &str,
|
||||
text: Vec<String>,
|
||||
active_text: &str,
|
||||
dim: Vec<String>,
|
||||
active_dim: &str,
|
||||
mleader: Vec<String>,
|
||||
active_mleader: &str,
|
||||
table: Vec<String>,
|
||||
active_table: &str,
|
||||
) {
|
||||
self.text_style_names = text;
|
||||
self.active_text_style = active_text.to_string();
|
||||
|
|
@ -187,7 +191,12 @@ impl Ribbon {
|
|||
|
||||
// ── View ──────────────────────────────────────────────────────────────
|
||||
|
||||
pub fn view(&self, is_paper: bool, undo_count: usize, redo_count: usize) -> Element<'_, Message> {
|
||||
pub fn view(
|
||||
&self,
|
||||
is_paper: bool,
|
||||
undo_count: usize,
|
||||
redo_count: usize,
|
||||
) -> Element<'_, Message> {
|
||||
// ── Logo ──────────────────────────────────────────────────────────
|
||||
let logo_svg = {
|
||||
let handle = svg::Handle::from_memory(include_bytes!("../../../assets/logo.svg"));
|
||||
|
|
@ -217,14 +226,28 @@ impl Ribbon {
|
|||
|
||||
// ── Tab buttons ───────────────────────────────────────────────────
|
||||
let history_controls = row![
|
||||
render_history_control("↶", "Undo", UNDO_HISTORY_ID, undo_count, &self.open_dropdown),
|
||||
render_history_control("↷", "Redo", REDO_HISTORY_ID, redo_count, &self.open_dropdown),
|
||||
render_history_control(
|
||||
"↶",
|
||||
"Undo",
|
||||
UNDO_HISTORY_ID,
|
||||
undo_count,
|
||||
&self.open_dropdown
|
||||
),
|
||||
render_history_control(
|
||||
"↷",
|
||||
"Redo",
|
||||
REDO_HISTORY_ID,
|
||||
redo_count,
|
||||
&self.open_dropdown
|
||||
),
|
||||
]
|
||||
.spacing(TOP_HIST_GAP)
|
||||
.align_y(iced::Center);
|
||||
|
||||
let tab_buttons = self.modules.iter().enumerate().fold(
|
||||
row![logo, history_controls].align_y(iced::Center).spacing(6),
|
||||
row![logo, history_controls]
|
||||
.align_y(iced::Center)
|
||||
.spacing(6),
|
||||
|row_acc, (i, module)| {
|
||||
if module.id() == "layout" && !is_paper {
|
||||
return row_acc;
|
||||
|
|
@ -232,16 +255,40 @@ impl Ribbon {
|
|||
|
||||
let is_active = i == self.active;
|
||||
let is_contextual = module.id() == "layout";
|
||||
let accent = if is_contextual { ACCENT_GOLD } else { ACCENT_BLUE };
|
||||
let text_inactive = if is_contextual {
|
||||
Color { r: 0.90, g: 0.72, b: 0.30, a: 1.0 }
|
||||
let accent = if is_contextual {
|
||||
ACCENT_GOLD
|
||||
} else {
|
||||
Color { r: 0.75, g: 0.75, b: 0.75, a: 1.0 }
|
||||
ACCENT_BLUE
|
||||
};
|
||||
let text_inactive = if is_contextual {
|
||||
Color {
|
||||
r: 0.90,
|
||||
g: 0.72,
|
||||
b: 0.30,
|
||||
a: 1.0,
|
||||
}
|
||||
} else {
|
||||
Color {
|
||||
r: 0.75,
|
||||
g: 0.75,
|
||||
b: 0.75,
|
||||
a: 1.0,
|
||||
}
|
||||
};
|
||||
let hover_bg = if is_contextual {
|
||||
Color { r: 0.28, g: 0.24, b: 0.12, a: 1.0 }
|
||||
Color {
|
||||
r: 0.28,
|
||||
g: 0.24,
|
||||
b: 0.12,
|
||||
a: 1.0,
|
||||
}
|
||||
} else {
|
||||
Color { r: 0.25, g: 0.25, b: 0.25, a: 1.0 }
|
||||
Color {
|
||||
r: 0.25,
|
||||
g: 0.25,
|
||||
b: 0.25,
|
||||
a: 1.0,
|
||||
}
|
||||
};
|
||||
let btn = container(
|
||||
button(text(module.title()).size(12))
|
||||
|
|
@ -252,9 +299,17 @@ impl Ribbon {
|
|||
(false, button::Status::Hovered) => hover_bg,
|
||||
_ => Color::TRANSPARENT,
|
||||
})),
|
||||
text_color: if is_active { Color::WHITE } else { text_inactive },
|
||||
text_color: if is_active {
|
||||
Color::WHITE
|
||||
} else {
|
||||
text_inactive
|
||||
},
|
||||
border: Border {
|
||||
color: if is_active { accent } else { Color::TRANSPARENT },
|
||||
color: if is_active {
|
||||
accent
|
||||
} else {
|
||||
Color::TRANSPARENT
|
||||
},
|
||||
width: if is_active { 2.0 } else { 0.0 },
|
||||
radius: 0.0.into(),
|
||||
},
|
||||
|
|
@ -265,7 +320,11 @@ impl Ribbon {
|
|||
)
|
||||
.style(move |_: &Theme| container::Style {
|
||||
border: Border {
|
||||
color: if is_active { accent } else { Color::TRANSPARENT },
|
||||
color: if is_active {
|
||||
accent
|
||||
} else {
|
||||
Color::TRANSPARENT
|
||||
},
|
||||
width: if is_active { 2.0 } else { 0.0 },
|
||||
radius: 0.0.into(),
|
||||
},
|
||||
|
|
@ -344,10 +403,10 @@ impl Ribbon {
|
|||
let is_large = matches!(
|
||||
&item,
|
||||
RibbonItem::LargeTool(_)
|
||||
| RibbonItem::LargeDropdown { .. }
|
||||
| RibbonItem::LayerComboGroup { .. }
|
||||
| RibbonItem::PropertiesGroup { .. }
|
||||
| RibbonItem::StyleComboGroup { .. }
|
||||
| RibbonItem::LargeDropdown { .. }
|
||||
| RibbonItem::LayerComboGroup { .. }
|
||||
| RibbonItem::PropertiesGroup { .. }
|
||||
| RibbonItem::StyleComboGroup { .. }
|
||||
);
|
||||
|
||||
if is_large {
|
||||
|
|
@ -391,8 +450,7 @@ impl Ribbon {
|
|||
widgets.push(
|
||||
column![
|
||||
tools_el,
|
||||
container(text(group.title).size(9).color(GROUP_LABEL))
|
||||
.padding([1, 4]),
|
||||
container(text(group.title).size(9).color(GROUP_LABEL)).padding([1, 4]),
|
||||
]
|
||||
.spacing(0)
|
||||
.padding([3u16, 4])
|
||||
|
|
@ -518,8 +576,12 @@ impl Ribbon {
|
|||
'outer: for group in &groups {
|
||||
for item in &group.tools {
|
||||
let (id, items, default) = match item {
|
||||
RibbonItem::Dropdown { id, items, default, .. } => (id, items, default),
|
||||
RibbonItem::LargeDropdown { id, items, default, .. } => (id, items, default),
|
||||
RibbonItem::Dropdown {
|
||||
id, items, default, ..
|
||||
} => (id, items, default),
|
||||
RibbonItem::LargeDropdown {
|
||||
id, items, default, ..
|
||||
} => (id, items, default),
|
||||
_ => continue,
|
||||
};
|
||||
if *id == open_id {
|
||||
|
|
@ -539,7 +601,11 @@ impl Ribbon {
|
|||
let is_current = *cmd == last_cmd;
|
||||
let checkmark = text(if is_current { "✓" } else { " " })
|
||||
.size(11)
|
||||
.color(if is_current { CHECK_COLOR } else { Color::TRANSPARENT })
|
||||
.color(if is_current {
|
||||
CHECK_COLOR
|
||||
} else {
|
||||
Color::TRANSPARENT
|
||||
})
|
||||
.width(Length::Fixed(14.0));
|
||||
let icon_el: Element<Message> = match *item_icon {
|
||||
IconKind::Glyph(s) => text(s)
|
||||
|
|
@ -552,9 +618,10 @@ impl Ribbon {
|
|||
svg(handle).width(20).height(20).into()
|
||||
}
|
||||
};
|
||||
let label_el = text(*label)
|
||||
.size(11)
|
||||
.color(if is_current { LABEL_ON } else { LABEL_OFF });
|
||||
let label_el =
|
||||
text(*label)
|
||||
.size(11)
|
||||
.color(if is_current { LABEL_ON } else { LABEL_OFF });
|
||||
|
||||
button(
|
||||
row![checkmark, icon_el, label_el]
|
||||
|
|
@ -626,7 +693,12 @@ impl Ribbon {
|
|||
.style(move |_: &Theme| container::Style {
|
||||
background: Some(Background::Color(lc)),
|
||||
border: Border {
|
||||
color: Color { r: 0.0, g: 0.0, b: 0.0, a: 0.5 },
|
||||
color: Color {
|
||||
r: 0.0,
|
||||
g: 0.0,
|
||||
b: 0.0,
|
||||
a: 0.5,
|
||||
},
|
||||
width: 1.0,
|
||||
radius: 1.0.into(),
|
||||
},
|
||||
|
|
@ -635,30 +707,63 @@ impl Ribbon {
|
|||
.width(12)
|
||||
.height(12);
|
||||
|
||||
let vis = text(if lv { "●" } else { "○" })
|
||||
.size(10)
|
||||
.color(if lv {
|
||||
Color { r: 0.95, g: 0.85, b: 0.20, a: 1.0 }
|
||||
} else {
|
||||
Color { r: 0.45, g: 0.45, b: 0.45, a: 1.0 }
|
||||
});
|
||||
let freeze = text("✱").size(10).color(if lf {
|
||||
Color { r: 0.40, g: 0.80, b: 1.00, a: 1.0 }
|
||||
let vis = text(if lv { "●" } else { "○" }).size(10).color(if lv {
|
||||
Color {
|
||||
r: 0.95,
|
||||
g: 0.85,
|
||||
b: 0.20,
|
||||
a: 1.0,
|
||||
}
|
||||
} else {
|
||||
Color { r: 0.95, g: 0.85, b: 0.20, a: 1.0 }
|
||||
Color {
|
||||
r: 0.45,
|
||||
g: 0.45,
|
||||
b: 0.45,
|
||||
a: 1.0,
|
||||
}
|
||||
});
|
||||
let freeze = text("✱").size(10).color(if lf {
|
||||
Color {
|
||||
r: 0.40,
|
||||
g: 0.80,
|
||||
b: 1.00,
|
||||
a: 1.0,
|
||||
}
|
||||
} else {
|
||||
Color {
|
||||
r: 0.95,
|
||||
g: 0.85,
|
||||
b: 0.20,
|
||||
a: 1.0,
|
||||
}
|
||||
});
|
||||
let lock = text(if ll { "🔒" } else { "🔓" }).size(10).color(if ll {
|
||||
Color { r: 0.95, g: 0.70, b: 0.20, a: 1.0 }
|
||||
Color {
|
||||
r: 0.95,
|
||||
g: 0.70,
|
||||
b: 0.20,
|
||||
a: 1.0,
|
||||
}
|
||||
} else {
|
||||
Color { r: 0.55, g: 0.55, b: 0.55, a: 1.0 }
|
||||
Color {
|
||||
r: 0.55,
|
||||
g: 0.55,
|
||||
b: 0.55,
|
||||
a: 1.0,
|
||||
}
|
||||
});
|
||||
let checkmark = text(if is_active { "✓" } else { " " })
|
||||
.size(11)
|
||||
.color(if is_active { CHECK_COLOR } else { Color::TRANSPARENT })
|
||||
.color(if is_active {
|
||||
CHECK_COLOR
|
||||
} else {
|
||||
Color::TRANSPARENT
|
||||
})
|
||||
.width(Length::Fixed(14.0));
|
||||
let label = text(&info.name)
|
||||
.size(11)
|
||||
.color(if is_active { LABEL_ON } else { LABEL_OFF });
|
||||
let label =
|
||||
text(&info.name)
|
||||
.size(11)
|
||||
.color(if is_active { LABEL_ON } else { LABEL_OFF });
|
||||
|
||||
button(
|
||||
row![checkmark, vis, freeze, lock, swatch, label]
|
||||
|
|
@ -682,7 +787,11 @@ impl Ribbon {
|
|||
let panel = container(column(rows))
|
||||
.style(|_: &Theme| container::Style {
|
||||
background: Some(Background::Color(PANEL_BG)),
|
||||
border: Border { color: PANEL_BORDER, width: 1.0, radius: 3.0.into() },
|
||||
border: Border {
|
||||
color: PANEL_BORDER,
|
||||
width: 1.0,
|
||||
radius: 3.0.into(),
|
||||
},
|
||||
..Default::default()
|
||||
})
|
||||
.width(Length::Fixed(220.0));
|
||||
|
|
@ -693,11 +802,19 @@ impl Ribbon {
|
|||
let positioned = container(panel)
|
||||
.align_left(Fill)
|
||||
.align_top(Fill)
|
||||
.padding(Padding { top: top_offset, left: left_offset, ..Default::default() })
|
||||
.padding(Padding {
|
||||
top: top_offset,
|
||||
left: left_offset,
|
||||
..Default::default()
|
||||
})
|
||||
.width(Fill)
|
||||
.height(Fill);
|
||||
|
||||
Some(mouse_area(positioned).on_press(Message::CloseRibbonDropdown).into())
|
||||
Some(
|
||||
mouse_area(positioned)
|
||||
.on_press(Message::CloseRibbonDropdown)
|
||||
.into(),
|
||||
)
|
||||
}
|
||||
|
||||
fn prop_color_overlay(&self) -> Option<Element<'_, Message>> {
|
||||
|
|
@ -712,7 +829,11 @@ impl Ribbon {
|
|||
let panel = container(picker)
|
||||
.style(|_: &Theme| container::Style {
|
||||
background: Some(Background::Color(PANEL_BG)),
|
||||
border: Border { color: PANEL_BORDER, width: 1.0, radius: 3.0.into() },
|
||||
border: Border {
|
||||
color: PANEL_BORDER,
|
||||
width: 1.0,
|
||||
radius: 3.0.into(),
|
||||
},
|
||||
..Default::default()
|
||||
})
|
||||
.width(Length::Fixed(200.0));
|
||||
|
|
@ -723,19 +844,33 @@ impl Ribbon {
|
|||
let positioned = container(panel)
|
||||
.align_left(Fill)
|
||||
.align_top(Fill)
|
||||
.padding(Padding { top: top_offset, left: left_offset, ..Default::default() })
|
||||
.padding(Padding {
|
||||
top: top_offset,
|
||||
left: left_offset,
|
||||
..Default::default()
|
||||
})
|
||||
.width(Fill)
|
||||
.height(Fill);
|
||||
|
||||
Some(mouse_area(positioned).on_press(Message::CloseRibbonDropdown).into())
|
||||
Some(
|
||||
mouse_area(positioned)
|
||||
.on_press(Message::CloseRibbonDropdown)
|
||||
.into(),
|
||||
)
|
||||
}
|
||||
|
||||
fn prop_linetype_overlay(&self) -> Option<Element<'_, Message>> {
|
||||
let active_lt = &self.active_linetype;
|
||||
|
||||
let mut items: Vec<LinetypeItem> = vec![
|
||||
LinetypeItem { name: "ByLayer".to_string(), art: String::new() },
|
||||
LinetypeItem { name: "ByBlock".to_string(), art: String::new() },
|
||||
LinetypeItem {
|
||||
name: "ByLayer".to_string(),
|
||||
art: String::new(),
|
||||
},
|
||||
LinetypeItem {
|
||||
name: "ByBlock".to_string(),
|
||||
art: String::new(),
|
||||
},
|
||||
];
|
||||
for lt in &self.available_linetypes {
|
||||
if lt.name != "ByLayer" && lt.name != "ByBlock" {
|
||||
|
|
@ -749,34 +884,50 @@ impl Ribbon {
|
|||
let is_cur = lt.name == *active_lt;
|
||||
let check = text(if is_cur { "✓" } else { " " })
|
||||
.size(11)
|
||||
.color(if is_cur { CHECK_COLOR } else { Color::TRANSPARENT })
|
||||
.color(if is_cur {
|
||||
CHECK_COLOR
|
||||
} else {
|
||||
Color::TRANSPARENT
|
||||
})
|
||||
.width(Length::Fixed(14.0));
|
||||
let name_col = text(lt.name.clone())
|
||||
.size(11)
|
||||
.color(if is_cur { LABEL_ON } else { LABEL_OFF })
|
||||
.width(Length::Fixed(90.0));
|
||||
let art_col =
|
||||
text(lt.art.clone()).size(9).color(Color { r: 0.55, g: 0.55, b: 0.55, a: 1.0 });
|
||||
let art_col = text(lt.art.clone()).size(9).color(Color {
|
||||
r: 0.55,
|
||||
g: 0.55,
|
||||
b: 0.55,
|
||||
a: 1.0,
|
||||
});
|
||||
let name = lt.name.clone();
|
||||
button(row![check, name_col, art_col].spacing(4).align_y(iced::Center))
|
||||
.on_press(Message::RibbonLinetypeChanged(name))
|
||||
.style(|_: &Theme, status| button::Style {
|
||||
background: Some(Background::Color(match status {
|
||||
button::Status::Hovered | button::Status::Pressed => ROW_HOVER,
|
||||
_ => Color::TRANSPARENT,
|
||||
})),
|
||||
..Default::default()
|
||||
})
|
||||
.width(Fill)
|
||||
.padding([4, 6])
|
||||
.into()
|
||||
button(
|
||||
row![check, name_col, art_col]
|
||||
.spacing(4)
|
||||
.align_y(iced::Center),
|
||||
)
|
||||
.on_press(Message::RibbonLinetypeChanged(name))
|
||||
.style(|_: &Theme, status| button::Style {
|
||||
background: Some(Background::Color(match status {
|
||||
button::Status::Hovered | button::Status::Pressed => ROW_HOVER,
|
||||
_ => Color::TRANSPARENT,
|
||||
})),
|
||||
..Default::default()
|
||||
})
|
||||
.width(Fill)
|
||||
.padding([4, 6])
|
||||
.into()
|
||||
})
|
||||
.collect();
|
||||
|
||||
let list = container(scrollable(column(rows)).height(Length::Fixed(200.0)))
|
||||
.style(|_: &Theme| container::Style {
|
||||
background: Some(Background::Color(PANEL_BG)),
|
||||
border: Border { color: PANEL_BORDER, width: 1.0, radius: 3.0.into() },
|
||||
border: Border {
|
||||
color: PANEL_BORDER,
|
||||
width: 1.0,
|
||||
radius: 3.0.into(),
|
||||
},
|
||||
..Default::default()
|
||||
})
|
||||
.width(Length::Fixed(220.0));
|
||||
|
|
@ -787,11 +938,19 @@ impl Ribbon {
|
|||
let positioned = container(list)
|
||||
.align_left(Fill)
|
||||
.align_top(Fill)
|
||||
.padding(Padding { top: top_offset, left: left_offset, ..Default::default() })
|
||||
.padding(Padding {
|
||||
top: top_offset,
|
||||
left: left_offset,
|
||||
..Default::default()
|
||||
})
|
||||
.width(Fill)
|
||||
.height(Fill);
|
||||
|
||||
Some(mouse_area(positioned).on_press(Message::CloseRibbonDropdown).into())
|
||||
Some(
|
||||
mouse_area(positioned)
|
||||
.on_press(Message::CloseRibbonDropdown)
|
||||
.into(),
|
||||
)
|
||||
}
|
||||
|
||||
fn prop_lw_overlay(&self) -> Option<Element<'_, Message>> {
|
||||
|
|
@ -803,7 +962,11 @@ impl Ribbon {
|
|||
let label = item.to_string();
|
||||
let check = text(if is_cur { "✓" } else { " " })
|
||||
.size(11)
|
||||
.color(if is_cur { CHECK_COLOR } else { Color::TRANSPARENT })
|
||||
.color(if is_cur {
|
||||
CHECK_COLOR
|
||||
} else {
|
||||
Color::TRANSPARENT
|
||||
})
|
||||
.width(Length::Fixed(14.0));
|
||||
button(
|
||||
row![
|
||||
|
|
@ -841,7 +1004,11 @@ impl Ribbon {
|
|||
let panel = container(column(rows))
|
||||
.style(|_: &Theme| container::Style {
|
||||
background: Some(Background::Color(PANEL_BG)),
|
||||
border: Border { color: PANEL_BORDER, width: 1.0, radius: 3.0.into() },
|
||||
border: Border {
|
||||
color: PANEL_BORDER,
|
||||
width: 1.0,
|
||||
radius: 3.0.into(),
|
||||
},
|
||||
..Default::default()
|
||||
})
|
||||
.width(Length::Fixed(width));
|
||||
|
|
@ -852,11 +1019,19 @@ impl Ribbon {
|
|||
let positioned = container(panel)
|
||||
.align_left(Fill)
|
||||
.align_top(Fill)
|
||||
.padding(Padding { top: top_offset, left: left_offset, ..Default::default() })
|
||||
.padding(Padding {
|
||||
top: top_offset,
|
||||
left: left_offset,
|
||||
..Default::default()
|
||||
})
|
||||
.width(Fill)
|
||||
.height(Fill);
|
||||
|
||||
Some(mouse_area(positioned).on_press(Message::CloseRibbonDropdown).into())
|
||||
Some(
|
||||
mouse_area(positioned)
|
||||
.on_press(Message::CloseRibbonDropdown)
|
||||
.into(),
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -11,7 +11,7 @@ use iced::{Background, Border, Color, Element, Fill, Length, Padding, Theme};
|
|||
|
||||
use crate::app::Message;
|
||||
use crate::modules::{IconKind, ModuleEvent, RibbonGroup, RibbonItem, StyleKey, ToolDef};
|
||||
use crate::ui::properties::{LwItem, acad_color_display};
|
||||
use crate::ui::properties::{acad_color_display, LwItem};
|
||||
|
||||
use super::LayerInfo;
|
||||
|
||||
|
|
@ -51,24 +51,114 @@ pub(super) const PROP_LW_ID: &str = "PROP_LW";
|
|||
|
||||
// ── Colours ────────────────────────────────────────────────────────────────
|
||||
|
||||
pub(super) const LOGO_RED: Color = Color { r: 0.75, g: 0.10, b: 0.10, a: 1.0 };
|
||||
pub(super) const TOPBAR_BG: Color = Color { r: 0.17, g: 0.17, b: 0.17, a: 1.0 };
|
||||
pub(super) const RIBBON_BG: Color = Color { r: 0.22, g: 0.22, b: 0.22, a: 1.0 };
|
||||
pub(super) const BORDER_DARK: Color = Color { r: 0.12, g: 0.12, b: 0.12, a: 1.0 };
|
||||
pub(super) const ACCENT_BLUE: Color = Color { r: 0.20, g: 0.55, b: 0.90, a: 1.0 };
|
||||
pub(super) const ACCENT_GOLD: Color = Color { r: 0.90, g: 0.65, b: 0.10, a: 1.0 };
|
||||
pub(super) const LABEL_COLOR: Color = Color { r: 0.82, g: 0.82, b: 0.82, a: 1.0 };
|
||||
pub(super) const GROUP_LABEL: Color = Color { r: 0.50, g: 0.50, b: 0.50, a: 1.0 };
|
||||
pub(super) const TOOL_HOVER: Color = Color { r: 0.32, g: 0.32, b: 0.32, a: 1.0 };
|
||||
pub(super) const TOOL_ACTIVE: Color = Color { r: 0.18, g: 0.42, b: 0.70, a: 1.0 };
|
||||
pub(super) const ARROW_COLOR: Color = Color { r: 0.65, g: 0.65, b: 0.65, a: 1.0 };
|
||||
pub(super) const PANEL_BG: Color = Color { r: 0.16, g: 0.16, b: 0.16, a: 0.98 };
|
||||
pub(super) const PANEL_BORDER: Color = Color { r: 0.32, g: 0.32, b: 0.32, a: 1.0 };
|
||||
pub(super) const ROW_HOVER: Color = Color { r: 0.24, g: 0.24, b: 0.24, a: 1.0 };
|
||||
pub(super) const CHECK_COLOR: Color = Color { r: 0.20, g: 0.75, b: 0.35, a: 1.0 };
|
||||
pub(super) const ICON_COLOR: Color = Color { r: 0.25, g: 0.75, b: 0.45, a: 1.0 };
|
||||
pub(super) const LABEL_ON: Color = Color { r: 0.92, g: 0.92, b: 0.92, a: 1.0 };
|
||||
pub(super) const LABEL_OFF: Color = Color { r: 0.72, g: 0.72, b: 0.72, a: 1.0 };
|
||||
pub(super) const LOGO_RED: Color = Color {
|
||||
r: 0.75,
|
||||
g: 0.10,
|
||||
b: 0.10,
|
||||
a: 1.0,
|
||||
};
|
||||
pub(super) const TOPBAR_BG: Color = Color {
|
||||
r: 0.17,
|
||||
g: 0.17,
|
||||
b: 0.17,
|
||||
a: 1.0,
|
||||
};
|
||||
pub(super) const RIBBON_BG: Color = Color {
|
||||
r: 0.22,
|
||||
g: 0.22,
|
||||
b: 0.22,
|
||||
a: 1.0,
|
||||
};
|
||||
pub(super) const BORDER_DARK: Color = Color {
|
||||
r: 0.12,
|
||||
g: 0.12,
|
||||
b: 0.12,
|
||||
a: 1.0,
|
||||
};
|
||||
pub(super) const ACCENT_BLUE: Color = Color {
|
||||
r: 0.20,
|
||||
g: 0.55,
|
||||
b: 0.90,
|
||||
a: 1.0,
|
||||
};
|
||||
pub(super) const ACCENT_GOLD: Color = Color {
|
||||
r: 0.90,
|
||||
g: 0.65,
|
||||
b: 0.10,
|
||||
a: 1.0,
|
||||
};
|
||||
pub(super) const LABEL_COLOR: Color = Color {
|
||||
r: 0.82,
|
||||
g: 0.82,
|
||||
b: 0.82,
|
||||
a: 1.0,
|
||||
};
|
||||
pub(super) const GROUP_LABEL: Color = Color {
|
||||
r: 0.50,
|
||||
g: 0.50,
|
||||
b: 0.50,
|
||||
a: 1.0,
|
||||
};
|
||||
pub(super) const TOOL_HOVER: Color = Color {
|
||||
r: 0.32,
|
||||
g: 0.32,
|
||||
b: 0.32,
|
||||
a: 1.0,
|
||||
};
|
||||
pub(super) const TOOL_ACTIVE: Color = Color {
|
||||
r: 0.18,
|
||||
g: 0.42,
|
||||
b: 0.70,
|
||||
a: 1.0,
|
||||
};
|
||||
pub(super) const ARROW_COLOR: Color = Color {
|
||||
r: 0.65,
|
||||
g: 0.65,
|
||||
b: 0.65,
|
||||
a: 1.0,
|
||||
};
|
||||
pub(super) const PANEL_BG: Color = Color {
|
||||
r: 0.16,
|
||||
g: 0.16,
|
||||
b: 0.16,
|
||||
a: 0.98,
|
||||
};
|
||||
pub(super) const PANEL_BORDER: Color = Color {
|
||||
r: 0.32,
|
||||
g: 0.32,
|
||||
b: 0.32,
|
||||
a: 1.0,
|
||||
};
|
||||
pub(super) const ROW_HOVER: Color = Color {
|
||||
r: 0.24,
|
||||
g: 0.24,
|
||||
b: 0.24,
|
||||
a: 1.0,
|
||||
};
|
||||
pub(super) const CHECK_COLOR: Color = Color {
|
||||
r: 0.20,
|
||||
g: 0.75,
|
||||
b: 0.35,
|
||||
a: 1.0,
|
||||
};
|
||||
pub(super) const ICON_COLOR: Color = Color {
|
||||
r: 0.25,
|
||||
g: 0.75,
|
||||
b: 0.45,
|
||||
a: 1.0,
|
||||
};
|
||||
pub(super) const LABEL_ON: Color = Color {
|
||||
r: 0.92,
|
||||
g: 0.92,
|
||||
b: 0.92,
|
||||
a: 1.0,
|
||||
};
|
||||
pub(super) const LABEL_OFF: Color = Color {
|
||||
r: 0.72,
|
||||
g: 0.72,
|
||||
b: 0.72,
|
||||
a: 1.0,
|
||||
};
|
||||
|
||||
// ── Style context (passed from Ribbon to render_large) ────────────────────
|
||||
|
||||
|
|
@ -86,18 +176,18 @@ pub(super) struct StyleContext {
|
|||
impl StyleContext {
|
||||
fn names_for(&self, key: StyleKey) -> &[String] {
|
||||
match key {
|
||||
StyleKey::TextStyle => &self.text_style_names,
|
||||
StyleKey::DimStyle => &self.dim_style_names,
|
||||
StyleKey::TextStyle => &self.text_style_names,
|
||||
StyleKey::DimStyle => &self.dim_style_names,
|
||||
StyleKey::MLeaderStyle => &self.mleader_style_names,
|
||||
StyleKey::TableStyle => &self.table_style_names,
|
||||
StyleKey::TableStyle => &self.table_style_names,
|
||||
}
|
||||
}
|
||||
fn active_for(&self, key: StyleKey) -> &str {
|
||||
match key {
|
||||
StyleKey::TextStyle => &self.active_text_style,
|
||||
StyleKey::DimStyle => &self.active_dim_style,
|
||||
StyleKey::TextStyle => &self.active_text_style,
|
||||
StyleKey::DimStyle => &self.active_dim_style,
|
||||
StyleKey::MLeaderStyle => &self.active_mleader_style,
|
||||
StyleKey::TableStyle => &self.active_table_style,
|
||||
StyleKey::TableStyle => &self.active_table_style,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -177,7 +267,12 @@ pub(super) fn tip_style(_theme: &Theme) -> container::Style {
|
|||
a: 0.97,
|
||||
})),
|
||||
border: Border {
|
||||
color: Color { r: 0.35, g: 0.35, b: 0.35, a: 1.0 },
|
||||
color: Color {
|
||||
r: 0.35,
|
||||
g: 0.35,
|
||||
b: 0.35,
|
||||
a: 1.0,
|
||||
},
|
||||
width: 1.0,
|
||||
radius: 3.0.into(),
|
||||
},
|
||||
|
|
@ -216,7 +311,13 @@ pub(super) fn render_small<'a>(
|
|||
.into()
|
||||
}
|
||||
|
||||
RibbonItem::Dropdown { id, icon, items, default, .. } => {
|
||||
RibbonItem::Dropdown {
|
||||
id,
|
||||
icon,
|
||||
items,
|
||||
default,
|
||||
..
|
||||
} => {
|
||||
let active = active_tool.as_deref() == Some(id)
|
||||
|| items
|
||||
.iter()
|
||||
|
|
@ -227,7 +328,10 @@ pub(super) fn render_small<'a>(
|
|||
.get(id)
|
||||
.copied()
|
||||
.and_then(|cmd| {
|
||||
items.iter().find(|(c, _, _)| *c == cmd).map(|(_, _, ik)| *ik)
|
||||
items
|
||||
.iter()
|
||||
.find(|(c, _, _)| *c == cmd)
|
||||
.map(|(_, _, ik)| *ik)
|
||||
})
|
||||
.or_else(|| items.first().map(|(_, _, ik)| *ik))
|
||||
.unwrap_or(icon);
|
||||
|
|
@ -236,7 +340,10 @@ pub(super) fn render_small<'a>(
|
|||
.get(id)
|
||||
.copied()
|
||||
.and_then(|cmd| {
|
||||
items.iter().find(|(c, _, _)| *c == cmd).map(|(_, lbl, _)| *lbl)
|
||||
items
|
||||
.iter()
|
||||
.find(|(c, _, _)| *c == cmd)
|
||||
.map(|(_, lbl, _)| *lbl)
|
||||
})
|
||||
.or_else(|| items.first().map(|(_, lbl, _)| *lbl))
|
||||
.unwrap_or(id);
|
||||
|
|
@ -267,7 +374,10 @@ pub(super) fn render_small<'a>(
|
|||
_ if dd_open => TOOL_ACTIVE,
|
||||
_ => Color::TRANSPARENT,
|
||||
})),
|
||||
border: Border { radius: 2.0.into(), ..Default::default() },
|
||||
border: Border {
|
||||
radius: 2.0.into(),
|
||||
..Default::default()
|
||||
},
|
||||
..Default::default()
|
||||
})
|
||||
.width(Length::Fixed(ARROW_W))
|
||||
|
|
@ -283,7 +393,10 @@ pub(super) fn render_small<'a>(
|
|||
.delay(Duration::from_millis(400))
|
||||
.style(tip_style);
|
||||
|
||||
row![icon_with_tip, arr_with_tip].spacing(0).height(ROW_H).into()
|
||||
row![icon_with_tip, arr_with_tip]
|
||||
.spacing(0)
|
||||
.height(ROW_H)
|
||||
.into()
|
||||
}
|
||||
|
||||
_ => text("").into(),
|
||||
|
|
@ -325,7 +438,12 @@ pub(super) fn render_large<'a>(
|
|||
.style(move |_: &Theme, status| tool_btn_style(active, status))
|
||||
.width(Length::Fixed(LARGE_W))
|
||||
.height(Fill)
|
||||
.padding(Padding { top: 6.0, right: 4.0, bottom: 4.0, left: 4.0 });
|
||||
.padding(Padding {
|
||||
top: 6.0,
|
||||
right: 4.0,
|
||||
bottom: 4.0,
|
||||
left: 4.0,
|
||||
});
|
||||
tooltip(btn, make_tip(tip_text), TipPos::Bottom)
|
||||
.gap(6.0)
|
||||
.delay(Duration::from_millis(400))
|
||||
|
|
@ -333,7 +451,13 @@ pub(super) fn render_large<'a>(
|
|||
.into()
|
||||
}
|
||||
|
||||
RibbonItem::LargeDropdown { id, label, icon, items, default } => {
|
||||
RibbonItem::LargeDropdown {
|
||||
id,
|
||||
label,
|
||||
icon,
|
||||
items,
|
||||
default,
|
||||
} => {
|
||||
let active = active_tool.as_deref() == Some(id)
|
||||
|| items
|
||||
.iter()
|
||||
|
|
@ -344,7 +468,10 @@ pub(super) fn render_large<'a>(
|
|||
.get(id)
|
||||
.copied()
|
||||
.and_then(|cmd| {
|
||||
items.iter().find(|(c, _, _)| *c == cmd).map(|(_, _, ik)| *ik)
|
||||
items
|
||||
.iter()
|
||||
.find(|(c, _, _)| *c == cmd)
|
||||
.map(|(_, _, ik)| *ik)
|
||||
})
|
||||
.or_else(|| items.first().map(|(_, _, ik)| *ik))
|
||||
.unwrap_or(icon);
|
||||
|
|
@ -353,7 +480,10 @@ pub(super) fn render_large<'a>(
|
|||
.get(id)
|
||||
.copied()
|
||||
.and_then(|cmd| {
|
||||
items.iter().find(|(c, _, _)| *c == cmd).map(|(_, lbl, _)| *lbl)
|
||||
items
|
||||
.iter()
|
||||
.find(|(c, _, _)| *c == cmd)
|
||||
.map(|(_, lbl, _)| *lbl)
|
||||
})
|
||||
.or_else(|| items.first().map(|(_, lbl, _)| *lbl))
|
||||
.unwrap_or(label);
|
||||
|
|
@ -375,7 +505,12 @@ pub(super) fn render_large<'a>(
|
|||
.style(move |_: &Theme, status| tool_btn_style(active, status))
|
||||
.width(Length::Fixed(LARGE_W))
|
||||
.height(Fill)
|
||||
.padding(Padding { top: 6.0, right: 4.0, bottom: 2.0, left: 4.0 });
|
||||
.padding(Padding {
|
||||
top: 6.0,
|
||||
right: 4.0,
|
||||
bottom: 2.0,
|
||||
left: 4.0,
|
||||
});
|
||||
|
||||
let arr_btn = button(
|
||||
container(text("▾").size(9).color(ARROW_COLOR))
|
||||
|
|
@ -391,7 +526,10 @@ pub(super) fn render_large<'a>(
|
|||
_ if dd_open => TOOL_ACTIVE,
|
||||
_ => Color::TRANSPARENT,
|
||||
})),
|
||||
border: Border { radius: 3.0.into(), ..Default::default() },
|
||||
border: Border {
|
||||
radius: 3.0.into(),
|
||||
..Default::default()
|
||||
},
|
||||
..Default::default()
|
||||
})
|
||||
.width(Length::Fixed(LARGE_W))
|
||||
|
|
@ -424,23 +562,61 @@ pub(super) fn render_large<'a>(
|
|||
let ll = info.map(|l| l.locked).unwrap_or(false);
|
||||
let is_open = open_dd.as_deref() == Some(LAYER_COMBO_ID);
|
||||
|
||||
let vis_icon = text(if lv { "●" } else { "○" }).size(10).color(
|
||||
if lv { Color { r: 0.95, g: 0.85, b: 0.20, a: 1.0 } }
|
||||
else { Color { r: 0.45, g: 0.45, b: 0.45, a: 1.0 } },
|
||||
);
|
||||
let freeze_icon = text("✱").size(10).color(
|
||||
if lf { Color { r: 0.40, g: 0.80, b: 1.00, a: 1.0 } }
|
||||
else { Color { r: 0.95, g: 0.85, b: 0.20, a: 1.0 } },
|
||||
);
|
||||
let lock_icon = text(if ll { "🔒" } else { "🔓" }).size(10).color(
|
||||
if ll { Color { r: 0.95, g: 0.70, b: 0.20, a: 1.0 } }
|
||||
else { Color { r: 0.65, g: 0.65, b: 0.65, a: 1.0 } },
|
||||
);
|
||||
let vis_icon = text(if lv { "●" } else { "○" }).size(10).color(if lv {
|
||||
Color {
|
||||
r: 0.95,
|
||||
g: 0.85,
|
||||
b: 0.20,
|
||||
a: 1.0,
|
||||
}
|
||||
} else {
|
||||
Color {
|
||||
r: 0.45,
|
||||
g: 0.45,
|
||||
b: 0.45,
|
||||
a: 1.0,
|
||||
}
|
||||
});
|
||||
let freeze_icon = text("✱").size(10).color(if lf {
|
||||
Color {
|
||||
r: 0.40,
|
||||
g: 0.80,
|
||||
b: 1.00,
|
||||
a: 1.0,
|
||||
}
|
||||
} else {
|
||||
Color {
|
||||
r: 0.95,
|
||||
g: 0.85,
|
||||
b: 0.20,
|
||||
a: 1.0,
|
||||
}
|
||||
});
|
||||
let lock_icon = text(if ll { "🔒" } else { "🔓" }).size(10).color(if ll {
|
||||
Color {
|
||||
r: 0.95,
|
||||
g: 0.70,
|
||||
b: 0.20,
|
||||
a: 1.0,
|
||||
}
|
||||
} else {
|
||||
Color {
|
||||
r: 0.65,
|
||||
g: 0.65,
|
||||
b: 0.65,
|
||||
a: 1.0,
|
||||
}
|
||||
});
|
||||
let swatch = container(text(""))
|
||||
.style(move |_: &Theme| container::Style {
|
||||
background: Some(Background::Color(lc)),
|
||||
border: Border {
|
||||
color: Color { r: 0.0, g: 0.0, b: 0.0, a: 0.5 },
|
||||
color: Color {
|
||||
r: 0.0,
|
||||
g: 0.0,
|
||||
b: 0.0,
|
||||
a: 0.5,
|
||||
},
|
||||
width: 1.0,
|
||||
radius: 1.0.into(),
|
||||
},
|
||||
|
|
@ -454,10 +630,19 @@ pub(super) fn render_large<'a>(
|
|||
|
||||
let combo_btn = button(
|
||||
row![
|
||||
vis_icon, freeze_icon, lock_icon, swatch,
|
||||
vis_icon,
|
||||
freeze_icon,
|
||||
lock_icon,
|
||||
swatch,
|
||||
container(text(active_layer).size(11).color(Color::WHITE))
|
||||
.width(name_w).clip(true),
|
||||
text("▾").size(9).color(Color { r: 0.7, g: 0.7, b: 0.7, a: 1.0 }),
|
||||
.width(name_w)
|
||||
.clip(true),
|
||||
text("▾").size(9).color(Color {
|
||||
r: 0.7,
|
||||
g: 0.7,
|
||||
b: 0.7,
|
||||
a: 1.0
|
||||
}),
|
||||
]
|
||||
.spacing(4)
|
||||
.align_y(iced::Center),
|
||||
|
|
@ -465,14 +650,34 @@ pub(super) fn render_large<'a>(
|
|||
.on_press(Message::ToggleRibbonDropdown(LAYER_COMBO_ID.to_string()))
|
||||
.style(move |_: &Theme, status| button::Style {
|
||||
background: Some(Background::Color(match (is_open, status) {
|
||||
(true, _) => Color { r: 0.14, g: 0.14, b: 0.14, a: 1.0 },
|
||||
(_, button::Status::Hovered) => Color { r: 0.26, g: 0.26, b: 0.26, a: 1.0 },
|
||||
_ => Color { r: 0.18, g: 0.18, b: 0.18, a: 1.0 },
|
||||
(true, _) => Color {
|
||||
r: 0.14,
|
||||
g: 0.14,
|
||||
b: 0.14,
|
||||
a: 1.0,
|
||||
},
|
||||
(_, button::Status::Hovered) => Color {
|
||||
r: 0.26,
|
||||
g: 0.26,
|
||||
b: 0.26,
|
||||
a: 1.0,
|
||||
},
|
||||
_ => Color {
|
||||
r: 0.18,
|
||||
g: 0.18,
|
||||
b: 0.18,
|
||||
a: 1.0,
|
||||
},
|
||||
})),
|
||||
border: Border {
|
||||
radius: 3.0.into(),
|
||||
width: 1.0,
|
||||
color: Color { r: 0.35, g: 0.35, b: 0.35, a: 1.0 },
|
||||
color: Color {
|
||||
r: 0.35,
|
||||
g: 0.35,
|
||||
b: 0.35,
|
||||
a: 1.0,
|
||||
},
|
||||
},
|
||||
..Default::default()
|
||||
})
|
||||
|
|
@ -488,12 +693,12 @@ pub(super) fn render_large<'a>(
|
|||
let event = t.event.clone();
|
||||
let icon_el: Element<Message> = match t.icon {
|
||||
IconKind::Glyph(g) => text(g).size(13).color(Color::WHITE).into(),
|
||||
IconKind::Svg(bytes) => iced::widget::svg(
|
||||
iced::widget::svg::Handle::from_memory(bytes),
|
||||
)
|
||||
.width(16)
|
||||
.height(16)
|
||||
.into(),
|
||||
IconKind::Svg(bytes) => {
|
||||
iced::widget::svg(iced::widget::svg::Handle::from_memory(bytes))
|
||||
.width(16)
|
||||
.height(16)
|
||||
.into()
|
||||
}
|
||||
};
|
||||
let msg = module_event_to_message(event);
|
||||
tooltip(
|
||||
|
|
@ -524,7 +729,12 @@ pub(super) fn render_large<'a>(
|
|||
.width(Length::Fixed(COMBO_W))
|
||||
.height(Fill)
|
||||
.align_y(iced::Center)
|
||||
.padding(Padding { top: 4.0, bottom: 4.0, left: 4.0, right: 4.0 })
|
||||
.padding(Padding {
|
||||
top: 4.0,
|
||||
bottom: 4.0,
|
||||
left: 4.0,
|
||||
right: 4.0,
|
||||
})
|
||||
.into()
|
||||
}
|
||||
|
||||
|
|
@ -541,11 +751,19 @@ pub(super) fn render_large<'a>(
|
|||
.align_x(iced::Center)
|
||||
.spacing(3),
|
||||
)
|
||||
.on_press(Message::RibbonToolClick { tool_id: mp_id, event: mp_event })
|
||||
.on_press(Message::RibbonToolClick {
|
||||
tool_id: mp_id,
|
||||
event: mp_event,
|
||||
})
|
||||
.style(move |_: &Theme, status| tool_btn_style(mp_active, status))
|
||||
.width(Length::Fixed(LARGE_W))
|
||||
.height(Fill)
|
||||
.padding(Padding { top: 6.0, right: 4.0, bottom: 4.0, left: 4.0 });
|
||||
.padding(Padding {
|
||||
top: 6.0,
|
||||
right: 4.0,
|
||||
bottom: 4.0,
|
||||
left: 4.0,
|
||||
});
|
||||
let mp_el = tooltip(mp_btn, make_tip(mp_tip), TipPos::Bottom)
|
||||
.gap(6.0)
|
||||
.delay(Duration::from_millis(400))
|
||||
|
|
@ -560,7 +778,12 @@ pub(super) fn render_large<'a>(
|
|||
.style(move |_: &Theme| container::Style {
|
||||
background: Some(Background::Color(c)),
|
||||
border: Border {
|
||||
color: Color { r: 0.0, g: 0.0, b: 0.0, a: 0.5 },
|
||||
color: Color {
|
||||
r: 0.0,
|
||||
g: 0.0,
|
||||
b: 0.0,
|
||||
a: 0.5,
|
||||
},
|
||||
width: 1.0,
|
||||
radius: 1.0.into(),
|
||||
},
|
||||
|
|
@ -578,9 +801,12 @@ pub(super) fn render_large<'a>(
|
|||
container(text(label).size(10).color(Color::WHITE))
|
||||
.width(Fill)
|
||||
.clip(true),
|
||||
text(if is_open { "▲" } else { "▼" })
|
||||
.size(7)
|
||||
.color(Color { r: 0.6, g: 0.6, b: 0.6, a: 1.0 }),
|
||||
text(if is_open { "▲" } else { "▼" }).size(7).color(Color {
|
||||
r: 0.6,
|
||||
g: 0.6,
|
||||
b: 0.6,
|
||||
a: 1.0
|
||||
}),
|
||||
]
|
||||
.spacing(4)
|
||||
.align_y(iced::Center),
|
||||
|
|
@ -588,19 +814,42 @@ pub(super) fn render_large<'a>(
|
|||
.on_press(Message::ToggleRibbonDropdown(dd_id.to_string()))
|
||||
.style(move |_: &Theme, status| button::Style {
|
||||
background: Some(Background::Color(match (is_open, status) {
|
||||
(true, _) => Color { r: 0.14, g: 0.14, b: 0.14, a: 1.0 },
|
||||
(_, button::Status::Hovered) => {
|
||||
Color { r: 0.26, g: 0.26, b: 0.26, a: 1.0 }
|
||||
}
|
||||
_ => Color { r: 0.18, g: 0.18, b: 0.18, a: 1.0 },
|
||||
(true, _) => Color {
|
||||
r: 0.14,
|
||||
g: 0.14,
|
||||
b: 0.14,
|
||||
a: 1.0,
|
||||
},
|
||||
(_, button::Status::Hovered) => Color {
|
||||
r: 0.26,
|
||||
g: 0.26,
|
||||
b: 0.26,
|
||||
a: 1.0,
|
||||
},
|
||||
_ => Color {
|
||||
r: 0.18,
|
||||
g: 0.18,
|
||||
b: 0.18,
|
||||
a: 1.0,
|
||||
},
|
||||
})),
|
||||
border: Border {
|
||||
radius: 2.0.into(),
|
||||
width: 1.0,
|
||||
color: if is_open {
|
||||
Color { r: 0.45, g: 0.65, b: 0.90, a: 1.0 }
|
||||
Color {
|
||||
r: 0.45,
|
||||
g: 0.65,
|
||||
b: 0.90,
|
||||
a: 1.0,
|
||||
}
|
||||
} else {
|
||||
Color { r: 0.35, g: 0.35, b: 0.35, a: 1.0 }
|
||||
Color {
|
||||
r: 0.35,
|
||||
g: 0.35,
|
||||
b: 0.35,
|
||||
a: 1.0,
|
||||
}
|
||||
},
|
||||
},
|
||||
..Default::default()
|
||||
|
|
@ -615,11 +864,18 @@ pub(super) fn render_large<'a>(
|
|||
let lw_row = prop_row(LwItem(active_lineweight).to_string(), PROP_LW_ID, None);
|
||||
|
||||
let combos = container(
|
||||
column![color_row, lt_row, lw_row].spacing(2).align_x(iced::Left),
|
||||
column![color_row, lt_row, lw_row]
|
||||
.spacing(2)
|
||||
.align_x(iced::Left),
|
||||
)
|
||||
.height(Fill)
|
||||
.align_y(iced::Center)
|
||||
.padding(Padding { top: 4.0, bottom: 4.0, left: 0.0, right: 4.0 });
|
||||
.padding(Padding {
|
||||
top: 4.0,
|
||||
bottom: 4.0,
|
||||
left: 0.0,
|
||||
right: 4.0,
|
||||
});
|
||||
|
||||
row![mp_el, combos]
|
||||
.spacing(4)
|
||||
|
|
@ -628,7 +884,12 @@ pub(super) fn render_large<'a>(
|
|||
.into()
|
||||
}
|
||||
|
||||
RibbonItem::StyleComboGroup { style_key, combo_id, manager_cmd, rows } => {
|
||||
RibbonItem::StyleComboGroup {
|
||||
style_key,
|
||||
combo_id,
|
||||
manager_cmd,
|
||||
rows,
|
||||
} => {
|
||||
const STYLE_COMBO_W: f32 = LARGE_W * 2.3;
|
||||
let names: Vec<String> = style_ctx.names_for(style_key).to_vec();
|
||||
let active: String = style_ctx.active_for(style_key).to_string();
|
||||
|
|
@ -640,9 +901,12 @@ pub(super) fn render_large<'a>(
|
|||
container(text(active.clone()).size(11).color(Color::WHITE))
|
||||
.width(Fill)
|
||||
.clip(true),
|
||||
text(if is_open { "▲" } else { "▾" })
|
||||
.size(9)
|
||||
.color(Color { r: 0.7, g: 0.7, b: 0.7, a: 1.0 }),
|
||||
text(if is_open { "▲" } else { "▾" }).size(9).color(Color {
|
||||
r: 0.7,
|
||||
g: 0.7,
|
||||
b: 0.7,
|
||||
a: 1.0
|
||||
}),
|
||||
]
|
||||
.spacing(4)
|
||||
.align_y(iced::Center),
|
||||
|
|
@ -650,17 +914,42 @@ pub(super) fn render_large<'a>(
|
|||
.on_press(Message::ToggleRibbonDropdown(combo_id.to_string()))
|
||||
.style(move |_: &Theme, status| button::Style {
|
||||
background: Some(Background::Color(match (is_open, status) {
|
||||
(true, _) => Color { r: 0.14, g: 0.14, b: 0.14, a: 1.0 },
|
||||
(_, button::Status::Hovered) => Color { r: 0.26, g: 0.26, b: 0.26, a: 1.0 },
|
||||
_ => Color { r: 0.18, g: 0.18, b: 0.18, a: 1.0 },
|
||||
(true, _) => Color {
|
||||
r: 0.14,
|
||||
g: 0.14,
|
||||
b: 0.14,
|
||||
a: 1.0,
|
||||
},
|
||||
(_, button::Status::Hovered) => Color {
|
||||
r: 0.26,
|
||||
g: 0.26,
|
||||
b: 0.26,
|
||||
a: 1.0,
|
||||
},
|
||||
_ => Color {
|
||||
r: 0.18,
|
||||
g: 0.18,
|
||||
b: 0.18,
|
||||
a: 1.0,
|
||||
},
|
||||
})),
|
||||
border: Border {
|
||||
radius: 3.0.into(),
|
||||
width: 1.0,
|
||||
color: if is_open {
|
||||
Color { r: 0.45, g: 0.65, b: 0.90, a: 1.0 }
|
||||
Color {
|
||||
r: 0.45,
|
||||
g: 0.65,
|
||||
b: 0.90,
|
||||
a: 1.0,
|
||||
}
|
||||
} else {
|
||||
Color { r: 0.35, g: 0.35, b: 0.35, a: 1.0 }
|
||||
Color {
|
||||
r: 0.35,
|
||||
g: 0.35,
|
||||
b: 0.35,
|
||||
a: 1.0,
|
||||
}
|
||||
},
|
||||
},
|
||||
..Default::default()
|
||||
|
|
@ -681,7 +970,12 @@ pub(super) fn render_large<'a>(
|
|||
text(if is_sel { "✓" } else { " " })
|
||||
.size(10)
|
||||
.color(if is_sel {
|
||||
Color { r: 0.2, g: 0.8, b: 0.4, a: 1.0 }
|
||||
Color {
|
||||
r: 0.2,
|
||||
g: 0.8,
|
||||
b: 0.4,
|
||||
a: 1.0,
|
||||
}
|
||||
} else {
|
||||
Color::TRANSPARENT
|
||||
}),
|
||||
|
|
@ -693,11 +987,24 @@ pub(super) fn render_large<'a>(
|
|||
.on_press(Message::RibbonStyleChanged { key, name: n })
|
||||
.style(move |_: &Theme, status| button::Style {
|
||||
background: Some(Background::Color(match status {
|
||||
button::Status::Hovered | button::Status::Pressed => {
|
||||
Color { r: 0.28, g: 0.28, b: 0.28, a: 1.0 }
|
||||
}
|
||||
_ if is_sel => Color { r: 0.20, g: 0.35, b: 0.55, a: 1.0 },
|
||||
_ => Color { r: 0.16, g: 0.16, b: 0.16, a: 1.0 },
|
||||
button::Status::Hovered | button::Status::Pressed => Color {
|
||||
r: 0.28,
|
||||
g: 0.28,
|
||||
b: 0.28,
|
||||
a: 1.0,
|
||||
},
|
||||
_ if is_sel => Color {
|
||||
r: 0.20,
|
||||
g: 0.35,
|
||||
b: 0.55,
|
||||
a: 1.0,
|
||||
},
|
||||
_ => Color {
|
||||
r: 0.16,
|
||||
g: 0.16,
|
||||
b: 0.16,
|
||||
a: 1.0,
|
||||
},
|
||||
})),
|
||||
..Default::default()
|
||||
})
|
||||
|
|
@ -711,15 +1018,27 @@ pub(super) fn render_large<'a>(
|
|||
let mut full_col = items_col;
|
||||
if let Some(mgr_cmd) = manager_cmd {
|
||||
full_col.push(
|
||||
button(
|
||||
text(format!("Manage…")).size(10)
|
||||
.color(Color { r: 0.5, g: 0.8, b: 1.0, a: 1.0 }),
|
||||
)
|
||||
button(text(format!("Manage…")).size(10).color(Color {
|
||||
r: 0.5,
|
||||
g: 0.8,
|
||||
b: 1.0,
|
||||
a: 1.0,
|
||||
}))
|
||||
.on_press(Message::Command(mgr_cmd.to_string()))
|
||||
.style(|_: &Theme, status| button::Style {
|
||||
background: Some(Background::Color(match status {
|
||||
button::Status::Hovered => Color { r: 0.24, g: 0.24, b: 0.24, a: 1.0 },
|
||||
_ => Color { r: 0.13, g: 0.13, b: 0.13, a: 1.0 },
|
||||
button::Status::Hovered => Color {
|
||||
r: 0.24,
|
||||
g: 0.24,
|
||||
b: 0.24,
|
||||
a: 1.0,
|
||||
},
|
||||
_ => Color {
|
||||
r: 0.13,
|
||||
g: 0.13,
|
||||
b: 0.13,
|
||||
a: 1.0,
|
||||
},
|
||||
})),
|
||||
..Default::default()
|
||||
})
|
||||
|
|
@ -740,11 +1059,19 @@ pub(super) fn render_large<'a>(
|
|||
.max_height(180.0)
|
||||
.width(Length::Fixed(STYLE_COMBO_W))
|
||||
.style(|_: &Theme| container::Style {
|
||||
background: Some(Background::Color(
|
||||
Color { r: 0.14, g: 0.14, b: 0.14, a: 0.98 },
|
||||
)),
|
||||
background: Some(Background::Color(Color {
|
||||
r: 0.14,
|
||||
g: 0.14,
|
||||
b: 0.14,
|
||||
a: 0.98,
|
||||
})),
|
||||
border: Border {
|
||||
color: Color { r: 0.35, g: 0.35, b: 0.35, a: 1.0 },
|
||||
color: Color {
|
||||
r: 0.35,
|
||||
g: 0.35,
|
||||
b: 0.35,
|
||||
a: 1.0,
|
||||
},
|
||||
width: 1.0,
|
||||
radius: 4.0.into(),
|
||||
},
|
||||
|
|
@ -765,12 +1092,12 @@ pub(super) fn render_large<'a>(
|
|||
let event = t.event.clone();
|
||||
let icon_el: Element<Message> = match t.icon {
|
||||
IconKind::Glyph(g) => text(g).size(13).color(Color::WHITE).into(),
|
||||
IconKind::Svg(bytes) => iced::widget::svg(
|
||||
iced::widget::svg::Handle::from_memory(bytes),
|
||||
)
|
||||
.width(16)
|
||||
.height(16)
|
||||
.into(),
|
||||
IconKind::Svg(bytes) => {
|
||||
iced::widget::svg(iced::widget::svg::Handle::from_memory(bytes))
|
||||
.width(16)
|
||||
.height(16)
|
||||
.into()
|
||||
}
|
||||
};
|
||||
let msg = module_event_to_message(event);
|
||||
tooltip(
|
||||
|
|
@ -790,11 +1117,10 @@ pub(super) fn render_large<'a>(
|
|||
row(btns).spacing(2).align_y(iced::Center).into()
|
||||
};
|
||||
|
||||
let mut col_items: Vec<Element<Message>> = vec![
|
||||
container(row![combo_btn, items_panel].spacing(0))
|
||||
let mut col_items: Vec<Element<Message>> =
|
||||
vec![container(row![combo_btn, items_panel].spacing(0))
|
||||
.width(Fill)
|
||||
.into(),
|
||||
];
|
||||
.into()];
|
||||
for row_tools in rows {
|
||||
col_items.push(make_tool_row(row_tools));
|
||||
}
|
||||
|
|
@ -803,7 +1129,12 @@ pub(super) fn render_large<'a>(
|
|||
.width(Length::Fixed(STYLE_COMBO_W))
|
||||
.height(Fill)
|
||||
.align_y(iced::Center)
|
||||
.padding(Padding { top: 4.0, bottom: 4.0, left: 4.0, right: 4.0 })
|
||||
.padding(Padding {
|
||||
top: 4.0,
|
||||
bottom: 4.0,
|
||||
left: 4.0,
|
||||
right: 4.0,
|
||||
})
|
||||
.into()
|
||||
}
|
||||
|
||||
|
|
@ -814,10 +1145,7 @@ pub(super) fn render_large<'a>(
|
|||
// ── Dropdown position helpers ──────────────────────────────────────────────
|
||||
|
||||
/// Calculate the left pixel offset of a dropdown button inside the ribbon tool area.
|
||||
pub(super) fn compute_dropdown_left(
|
||||
groups: &[RibbonGroup],
|
||||
open_id: &str,
|
||||
) -> f32 {
|
||||
pub(super) fn compute_dropdown_left(groups: &[RibbonGroup], open_id: &str) -> f32 {
|
||||
let sum_with_spacing = |widths: &[f32]| -> f32 {
|
||||
widths
|
||||
.iter()
|
||||
|
|
@ -826,7 +1154,11 @@ pub(super) fn compute_dropdown_left(
|
|||
.sum::<f32>()
|
||||
};
|
||||
let next_item_x = |widths: &[f32]| -> f32 {
|
||||
if widths.is_empty() { 0.0 } else { sum_with_spacing(widths) + 2.0 }
|
||||
if widths.is_empty() {
|
||||
0.0
|
||||
} else {
|
||||
sum_with_spacing(widths) + 2.0
|
||||
}
|
||||
};
|
||||
|
||||
let mut x = 0.0f32;
|
||||
|
|
@ -950,7 +1282,9 @@ pub(super) fn render_history_control<'a>(
|
|||
|
||||
let main_btn = {
|
||||
let btn = button(
|
||||
text(glyph).size(14).color(if active { Color::WHITE } else { LABEL_OFF }),
|
||||
text(glyph)
|
||||
.size(14)
|
||||
.color(if active { Color::WHITE } else { LABEL_OFF }),
|
||||
)
|
||||
.style(move |_: &Theme, status| top_hist_btn_style(active, dd_open, status))
|
||||
.width(Length::Fixed(TOP_HIST_W))
|
||||
|
|
@ -978,7 +1312,9 @@ pub(super) fn render_history_control<'a>(
|
|||
let arrow_btn = {
|
||||
let btn = button(
|
||||
container(
|
||||
text("▾").size(7).color(if active { ARROW_COLOR } else { LABEL_OFF }),
|
||||
text("▾")
|
||||
.size(7)
|
||||
.color(if active { ARROW_COLOR } else { LABEL_OFF }),
|
||||
)
|
||||
.width(Fill)
|
||||
.height(Fill)
|
||||
|
|
@ -1014,7 +1350,12 @@ pub(super) fn top_hist_btn_style(
|
|||
) -> button::Style {
|
||||
button::Style {
|
||||
background: Some(Background::Color(match (active, open, status) {
|
||||
(false, _, _) => Color { r: 0.20, g: 0.20, b: 0.20, a: 1.0 },
|
||||
(false, _, _) => Color {
|
||||
r: 0.20,
|
||||
g: 0.20,
|
||||
b: 0.20,
|
||||
a: 1.0,
|
||||
},
|
||||
(_, true, _) => TOOL_ACTIVE,
|
||||
(_, _, button::Status::Hovered) => TOOL_HOVER,
|
||||
(_, _, button::Status::Pressed) => TOOL_ACTIVE,
|
||||
|
|
|
|||
|
|
@ -12,15 +12,15 @@ use crate::app::Message;
|
|||
/// vp_scale = custom_scale value on the Viewport entity (0.02 for "1:50")
|
||||
const COMMON_SCALES: &[(&str, f32, f64)] = &[
|
||||
("1:100", 100.0, 0.01),
|
||||
("1:50", 50.0, 0.02),
|
||||
("1:20", 20.0, 0.05),
|
||||
("1:10", 10.0, 0.10),
|
||||
("1:5", 5.0, 0.20),
|
||||
("1:2", 2.0, 0.50),
|
||||
("1:1", 1.0, 1.00),
|
||||
("2:1", 0.5, 2.00),
|
||||
("5:1", 0.2, 5.00),
|
||||
("10:1", 0.1, 10.00),
|
||||
("1:50", 50.0, 0.02),
|
||||
("1:20", 20.0, 0.05),
|
||||
("1:10", 10.0, 0.10),
|
||||
("1:5", 5.0, 0.20),
|
||||
("1:2", 2.0, 0.50),
|
||||
("1:1", 1.0, 1.00),
|
||||
("2:1", 0.5, 2.00),
|
||||
("5:1", 0.2, 5.00),
|
||||
("10:1", 0.1, 10.00),
|
||||
];
|
||||
|
||||
#[allow(dead_code)]
|
||||
|
|
@ -93,7 +93,11 @@ pub fn scale_popup_overlay(
|
|||
fn scale_row(label: &'static str, active: bool, msg: Message) -> Element<'static, Message> {
|
||||
let check = text(if active { "✓" } else { " " })
|
||||
.size(11)
|
||||
.color(if active { CHECK_COLOR } else { Color::TRANSPARENT })
|
||||
.color(if active {
|
||||
CHECK_COLOR
|
||||
} else {
|
||||
Color::TRANSPARENT
|
||||
})
|
||||
.width(Length::Fixed(14.0));
|
||||
|
||||
let lbl = text(label)
|
||||
|
|
@ -118,9 +122,39 @@ fn scale_row(label: &'static str, active: bool, msg: Message) -> Element<'static
|
|||
|
||||
// ── Colours ───────────────────────────────────────────────────────────────
|
||||
|
||||
const PANEL_BG: Color = Color { r: 0.15, g: 0.15, b: 0.15, a: 1.0 };
|
||||
const PANEL_BORDER: Color = Color { r: 0.32, g: 0.32, b: 0.32, a: 1.0 };
|
||||
const ROW_HOVER: Color = Color { r: 0.22, g: 0.22, b: 0.22, a: 1.0 };
|
||||
const CHECK_COLOR: Color = Color { r: 0.35, g: 0.75, b: 1.00, a: 1.0 };
|
||||
const LABEL_ON: Color = Color { r: 0.92, g: 0.92, b: 0.92, a: 1.0 };
|
||||
const LABEL_OFF: Color = Color { r: 0.65, g: 0.65, b: 0.65, a: 1.0 };
|
||||
const PANEL_BG: Color = Color {
|
||||
r: 0.15,
|
||||
g: 0.15,
|
||||
b: 0.15,
|
||||
a: 1.0,
|
||||
};
|
||||
const PANEL_BORDER: Color = Color {
|
||||
r: 0.32,
|
||||
g: 0.32,
|
||||
b: 0.32,
|
||||
a: 1.0,
|
||||
};
|
||||
const ROW_HOVER: Color = Color {
|
||||
r: 0.22,
|
||||
g: 0.22,
|
||||
b: 0.22,
|
||||
a: 1.0,
|
||||
};
|
||||
const CHECK_COLOR: Color = Color {
|
||||
r: 0.35,
|
||||
g: 0.75,
|
||||
b: 1.00,
|
||||
a: 1.0,
|
||||
};
|
||||
const LABEL_ON: Color = Color {
|
||||
r: 0.92,
|
||||
g: 0.92,
|
||||
b: 0.92,
|
||||
a: 1.0,
|
||||
};
|
||||
const LABEL_OFF: Color = Color {
|
||||
r: 0.65,
|
||||
g: 0.65,
|
||||
b: 0.65,
|
||||
a: 1.0,
|
||||
};
|
||||
|
|
|
|||
|
|
@ -4,16 +4,47 @@ use crate::app::Message;
|
|||
use iced::widget::{column, container, row, scrollable, text, Space};
|
||||
use iced::{Background, Color, Element, Fill, Theme};
|
||||
|
||||
const TB: Color = Color { r: 0.13, g: 0.13, b: 0.13, a: 1.0 };
|
||||
const BG: Color = Color { r: 0.15, g: 0.15, b: 0.15, a: 1.0 };
|
||||
const BORDER: Color = Color { r: 0.35, g: 0.35, b: 0.35, 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 };
|
||||
const KEY: Color = Color { r: 0.40, g: 0.70, b: 1.00, a: 1.0 };
|
||||
const TB: Color = Color {
|
||||
r: 0.13,
|
||||
g: 0.13,
|
||||
b: 0.13,
|
||||
a: 1.0,
|
||||
};
|
||||
const BG: Color = Color {
|
||||
r: 0.15,
|
||||
g: 0.15,
|
||||
b: 0.15,
|
||||
a: 1.0,
|
||||
};
|
||||
const BORDER: Color = Color {
|
||||
r: 0.35,
|
||||
g: 0.35,
|
||||
b: 0.35,
|
||||
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,
|
||||
};
|
||||
const KEY: Color = Color {
|
||||
r: 0.40,
|
||||
g: 0.70,
|
||||
b: 1.00,
|
||||
a: 1.0,
|
||||
};
|
||||
|
||||
fn hdivider<'a>() -> Element<'a, Message> {
|
||||
container(Space::new().width(Fill).height(1))
|
||||
.width(Fill).height(1)
|
||||
.width(Fill)
|
||||
.height(1)
|
||||
.style(|_: &Theme| container::Style {
|
||||
background: Some(Background::Color(BORDER)),
|
||||
..Default::default()
|
||||
|
|
@ -23,7 +54,11 @@ fn hdivider<'a>() -> Element<'a, Message> {
|
|||
|
||||
fn shortcut_row<'a>(key: &'static str, action: &'static str) -> Element<'a, Message> {
|
||||
row![
|
||||
text(key).size(11).color(KEY).font(iced::Font::MONOSPACE).width(160),
|
||||
text(key)
|
||||
.size(11)
|
||||
.color(KEY)
|
||||
.font(iced::Font::MONOSPACE)
|
||||
.width(160),
|
||||
text(action).size(11),
|
||||
]
|
||||
.spacing(8)
|
||||
|
|
@ -34,7 +69,12 @@ fn shortcut_row<'a>(key: &'static str, action: &'static str) -> Element<'a, Mess
|
|||
|
||||
fn section<'a>(title: &'static str) -> Element<'a, Message> {
|
||||
container(text(title).size(11).color(DIM))
|
||||
.padding(iced::Padding { top: 6.0, right: 0.0, bottom: 2.0, left: 0.0 })
|
||||
.padding(iced::Padding {
|
||||
top: 6.0,
|
||||
right: 0.0,
|
||||
bottom: 2.0,
|
||||
left: 0.0,
|
||||
})
|
||||
.into()
|
||||
}
|
||||
|
||||
|
|
@ -48,7 +88,7 @@ pub fn view_window<'a>(
|
|||
.size(10)
|
||||
.color(DIM),
|
||||
]
|
||||
.align_y(iced::Center)
|
||||
.align_y(iced::Center),
|
||||
)
|
||||
.style(|_: &Theme| container::Style {
|
||||
background: Some(Background::Color(TB)),
|
||||
|
|
@ -60,36 +100,40 @@ pub fn view_window<'a>(
|
|||
// ── Shortcut entries ──────────────────────────────────────────────────
|
||||
let mut rows: Vec<Element<'_, Message>> = vec![
|
||||
section("── Function Keys ──────────────────────────────────────"),
|
||||
shortcut_row("F3", "Toggle Object Snap"),
|
||||
shortcut_row("F7", "Toggle Grid"),
|
||||
shortcut_row("F8", "Toggle Ortho"),
|
||||
shortcut_row("F9", "Toggle Grid Snap"),
|
||||
shortcut_row("F10", "Toggle Polar Tracking"),
|
||||
shortcut_row("F11", "Toggle Object Snap Tracking"),
|
||||
shortcut_row("F12", "Toggle Dynamic Input"),
|
||||
shortcut_row("F3", "Toggle Object Snap"),
|
||||
shortcut_row("F7", "Toggle Grid"),
|
||||
shortcut_row("F8", "Toggle Ortho"),
|
||||
shortcut_row("F9", "Toggle Grid Snap"),
|
||||
shortcut_row("F10", "Toggle Polar Tracking"),
|
||||
shortcut_row("F11", "Toggle Object Snap Tracking"),
|
||||
shortcut_row("F12", "Toggle Dynamic Input"),
|
||||
section("── Ctrl Shortcuts ──────────────────────────────────────"),
|
||||
shortcut_row("Ctrl+N", "New Drawing"),
|
||||
shortcut_row("Ctrl+O", "Open File"),
|
||||
shortcut_row("Ctrl+S", "Save"),
|
||||
shortcut_row("Ctrl+Shift+S", "Save As"),
|
||||
shortcut_row("Ctrl+Z", "Undo"),
|
||||
shortcut_row("Ctrl+Shift+Z / Ctrl+Y","Redo"),
|
||||
shortcut_row("Ctrl+C", "Copy to Clipboard"),
|
||||
shortcut_row("Ctrl+X", "Cut to Clipboard"),
|
||||
shortcut_row("Ctrl+V", "Paste from Clipboard"),
|
||||
shortcut_row("Ctrl+N", "New Drawing"),
|
||||
shortcut_row("Ctrl+O", "Open File"),
|
||||
shortcut_row("Ctrl+S", "Save"),
|
||||
shortcut_row("Ctrl+Shift+S", "Save As"),
|
||||
shortcut_row("Ctrl+Z", "Undo"),
|
||||
shortcut_row("Ctrl+Shift+Z / Ctrl+Y", "Redo"),
|
||||
shortcut_row("Ctrl+C", "Copy to Clipboard"),
|
||||
shortcut_row("Ctrl+X", "Cut to Clipboard"),
|
||||
shortcut_row("Ctrl+V", "Paste from Clipboard"),
|
||||
section("── Other Keys ──────────────────────────────────────────"),
|
||||
shortcut_row("Enter / Space", "Finalize command / Repeat last"),
|
||||
shortcut_row("Escape", "Cancel active command"),
|
||||
shortcut_row("Delete", "Delete selected entities"),
|
||||
shortcut_row("↑ / ↓", "Command history navigation"),
|
||||
shortcut_row("Enter / Space", "Finalize command / Repeat last"),
|
||||
shortcut_row("Escape", "Cancel active command"),
|
||||
shortcut_row("Delete", "Delete selected entities"),
|
||||
shortcut_row("↑ / ↓", "Command history navigation"),
|
||||
];
|
||||
|
||||
// Custom overrides section
|
||||
rows.push(section("── Custom Overrides (SHORTCUTS SET) ──────────────────"));
|
||||
rows.push(section(
|
||||
"── Custom Overrides (SHORTCUTS SET) ──────────────────",
|
||||
));
|
||||
if overrides.is_empty() {
|
||||
rows.push(
|
||||
text(" (none — use: SHORTCUTS SET <key> <command>)")
|
||||
.size(11).color(DIM).into()
|
||||
.size(11)
|
||||
.color(DIM)
|
||||
.into(),
|
||||
);
|
||||
} else {
|
||||
let mut sorted: Vec<_> = overrides.iter().collect();
|
||||
|
|
@ -97,21 +141,25 @@ pub fn view_window<'a>(
|
|||
for (key, cmd) in sorted {
|
||||
rows.push(
|
||||
row![
|
||||
text(key.as_str()).size(11).color(KEY).font(iced::Font::MONOSPACE).width(160),
|
||||
text(key.as_str())
|
||||
.size(11)
|
||||
.color(KEY)
|
||||
.font(iced::Font::MONOSPACE)
|
||||
.width(160),
|
||||
text(cmd.as_str()).size(11),
|
||||
]
|
||||
.spacing(8).align_y(iced::Center).padding([2, 0])
|
||||
.into()
|
||||
.spacing(8)
|
||||
.align_y(iced::Center)
|
||||
.padding([2, 0])
|
||||
.into(),
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
// ── Section headers styled separately ────────────────────────────────
|
||||
let content = scrollable(
|
||||
column(rows).spacing(3).padding([12, 16])
|
||||
)
|
||||
.width(Fill)
|
||||
.height(Fill);
|
||||
let content = scrollable(column(rows).spacing(3).padding([12, 16]))
|
||||
.width(Fill)
|
||||
.height(Fill);
|
||||
|
||||
// ── Header row with accent ────────────────────────────────────────────
|
||||
let header = container(
|
||||
|
|
@ -120,22 +168,25 @@ pub fn view_window<'a>(
|
|||
text("Action").size(10).color(ACCENT),
|
||||
]
|
||||
.spacing(8)
|
||||
.padding([4, 16])
|
||||
.padding([4, 16]),
|
||||
)
|
||||
.style(|_: &Theme| container::Style {
|
||||
background: Some(Background::Color(Color { r: 0.13, g: 0.13, b: 0.18, a: 1.0 })),
|
||||
background: Some(Background::Color(Color {
|
||||
r: 0.13,
|
||||
g: 0.13,
|
||||
b: 0.18,
|
||||
a: 1.0,
|
||||
})),
|
||||
..Default::default()
|
||||
})
|
||||
.width(Fill);
|
||||
|
||||
container(
|
||||
column![toolbar, hdivider(), header, hdivider(), content].spacing(0)
|
||||
)
|
||||
.style(|_: &Theme| container::Style {
|
||||
background: Some(Background::Color(BG)),
|
||||
..Default::default()
|
||||
})
|
||||
.width(Fill)
|
||||
.height(Fill)
|
||||
.into()
|
||||
container(column![toolbar, hdivider(), header, hdivider(), content].spacing(0))
|
||||
.style(|_: &Theme| container::Style {
|
||||
background: Some(Background::Color(BG)),
|
||||
..Default::default()
|
||||
})
|
||||
.width(Fill)
|
||||
.height(Fill)
|
||||
.into()
|
||||
}
|
||||
|
|
|
|||
|
|
@ -6,8 +6,8 @@
|
|||
use iced::widget::{button, column, container, mouse_area, row, text};
|
||||
use iced::{Background, Border, Color, Element, Fill, Length, Padding, Theme};
|
||||
|
||||
use crate::snap::{SnapType, Snapper, ALL_SNAP_MODES};
|
||||
use crate::app::Message;
|
||||
use crate::snap::{SnapType, Snapper, ALL_SNAP_MODES};
|
||||
|
||||
/// Returns a full-screen overlay element:
|
||||
/// - a transparent click-catcher that closes the popup on outside click
|
||||
|
|
|
|||
|
|
@ -4,8 +4,8 @@ use iced::widget::tooltip::Position as TipPos;
|
|||
use iced::widget::{button, container, mouse_area, row, text, text_input, tooltip, Row};
|
||||
use iced::{Background, Border, Color, Element, Length, Theme};
|
||||
|
||||
use crate::snap::Snapper;
|
||||
use crate::app::Message;
|
||||
use crate::snap::Snapper;
|
||||
|
||||
#[derive(Clone, Default)]
|
||||
pub struct StatusBar {
|
||||
|
|
@ -83,7 +83,10 @@ impl StatusBar {
|
|||
format_scale(viewport_scale)
|
||||
};
|
||||
let scale_element: Element<'_, Message> = if scale_pill_enabled {
|
||||
tip(scale_popup_btn(&scale_label, scale_popup_open), "Annotation / Viewport Scale\nClick to change")
|
||||
tip(
|
||||
scale_popup_btn(&scale_label, scale_popup_open),
|
||||
"Annotation / Viewport Scale\nClick to change",
|
||||
)
|
||||
} else {
|
||||
status_pill(scale_label).into()
|
||||
};
|
||||
|
|
@ -234,13 +237,11 @@ fn polar_pill(active: bool, increment_deg: f32) -> Element<'static, Message> {
|
|||
increment_deg as u32
|
||||
);
|
||||
|
||||
let bg_color = move |hovered: bool| {
|
||||
match (active, hovered) {
|
||||
(true, true) => SNAP_ON_HOVER,
|
||||
(true, false) => SNAP_ON_BG,
|
||||
(false, true) => SNAP_OFF_HOVER,
|
||||
(false, false) => SNAP_OFF_BG,
|
||||
}
|
||||
let bg_color = move |hovered: bool| match (active, hovered) {
|
||||
(true, true) => SNAP_ON_HOVER,
|
||||
(true, false) => SNAP_ON_BG,
|
||||
(false, true) => SNAP_OFF_HOVER,
|
||||
(false, false) => SNAP_OFF_BG,
|
||||
};
|
||||
|
||||
// Cycle to the next common angle on right-click.
|
||||
|
|
@ -251,9 +252,11 @@ fn polar_pill(active: bool, increment_deg: f32) -> Element<'static, Message> {
|
|||
_ => 15.0,
|
||||
};
|
||||
|
||||
let inner = container(
|
||||
text(label).size(10).color(if active { OSNAP_ON_TEXT } else { OSNAP_OFF_TEXT })
|
||||
)
|
||||
let inner = container(text(label).size(10).color(if active {
|
||||
OSNAP_ON_TEXT
|
||||
} else {
|
||||
OSNAP_OFF_TEXT
|
||||
}))
|
||||
.style(move |_: &Theme| container::Style {
|
||||
background: Some(Background::Color(bg_color(false))),
|
||||
border: Border {
|
||||
|
|
@ -280,7 +283,12 @@ fn polar_pill(active: bool, increment_deg: f32) -> Element<'static, Message> {
|
|||
a: 0.95,
|
||||
})),
|
||||
border: Border {
|
||||
color: Color { r: 0.35, g: 0.35, b: 0.35, a: 1.0 },
|
||||
color: Color {
|
||||
r: 0.35,
|
||||
g: 0.35,
|
||||
b: 0.35,
|
||||
a: 1.0,
|
||||
},
|
||||
width: 1.0,
|
||||
radius: 3.0.into(),
|
||||
},
|
||||
|
|
@ -389,7 +397,11 @@ fn osnap_btn(active: bool, snap_enabled: bool, open: bool) -> Element<'static, M
|
|||
/// When `rename_edit` is `Some(value)` the tab shows an inline text input
|
||||
/// instead of the normal button. The tab is not renameable when it is the
|
||||
/// "Model" tab (callers simply never pass `Some` for that name).
|
||||
fn space_tab<'a>(label: String, is_active: bool, rename_edit: Option<&'a str>) -> Element<'a, Message> {
|
||||
fn space_tab<'a>(
|
||||
label: String,
|
||||
is_active: bool,
|
||||
rename_edit: Option<&'a str>,
|
||||
) -> Element<'a, Message> {
|
||||
let bg = move |is_active: bool, hovered: bool| {
|
||||
if is_active {
|
||||
TAB_ACTIVE
|
||||
|
|
@ -401,7 +413,11 @@ fn space_tab<'a>(label: String, is_active: bool, rename_edit: Option<&'a str>) -
|
|||
};
|
||||
|
||||
let border = Border {
|
||||
color: if is_active { ACCENT } else { Color::TRANSPARENT },
|
||||
color: if is_active {
|
||||
ACCENT
|
||||
} else {
|
||||
Color::TRANSPARENT
|
||||
},
|
||||
width: if is_active { 1.0 } else { 0.0 },
|
||||
radius: 2.0.into(),
|
||||
};
|
||||
|
|
@ -409,7 +425,12 @@ fn space_tab<'a>(label: String, is_active: bool, rename_edit: Option<&'a str>) -
|
|||
let text_color = if is_active {
|
||||
Color::WHITE
|
||||
} else {
|
||||
Color { r: 0.65, g: 0.65, b: 0.65, a: 1.0 }
|
||||
Color {
|
||||
r: 0.65,
|
||||
g: 0.65,
|
||||
b: 0.65,
|
||||
a: 1.0,
|
||||
}
|
||||
};
|
||||
|
||||
if let Some(edit_val) = rename_edit {
|
||||
|
|
@ -426,16 +447,29 @@ fn space_tab<'a>(label: String, is_active: bool, rename_edit: Option<&'a str>) -
|
|||
radius: 2.0.into(),
|
||||
},
|
||||
icon: Color::WHITE,
|
||||
placeholder: Color { r: 0.5, g: 0.5, b: 0.5, a: 1.0 },
|
||||
placeholder: Color {
|
||||
r: 0.5,
|
||||
g: 0.5,
|
||||
b: 0.5,
|
||||
a: 1.0,
|
||||
},
|
||||
value: Color::WHITE,
|
||||
selection: Color { r: 0.20, g: 0.55, b: 0.90, a: 0.4 },
|
||||
selection: Color {
|
||||
r: 0.20,
|
||||
g: 0.55,
|
||||
b: 0.90,
|
||||
a: 0.4,
|
||||
},
|
||||
})
|
||||
.padding([2, 6])
|
||||
.width(Length::Fixed(90.0));
|
||||
|
||||
let cancel_btn = button(
|
||||
text("✕").size(10).color(Color { r: 0.65, g: 0.65, b: 0.65, a: 1.0 }),
|
||||
)
|
||||
let cancel_btn = button(text("✕").size(10).color(Color {
|
||||
r: 0.65,
|
||||
g: 0.65,
|
||||
b: 0.65,
|
||||
a: 1.0,
|
||||
}))
|
||||
.on_press(Message::LayoutRenameCancel)
|
||||
.style(|_: &Theme, _| button::Style {
|
||||
background: Some(Background::Color(Color::TRANSPARENT)),
|
||||
|
|
@ -446,18 +480,19 @@ fn space_tab<'a>(label: String, is_active: bool, rename_edit: Option<&'a str>) -
|
|||
})
|
||||
.padding([2, 4]);
|
||||
|
||||
row![input, cancel_btn].spacing(0).align_y(iced::Center).into()
|
||||
row![input, cancel_btn]
|
||||
.spacing(0)
|
||||
.align_y(iced::Center)
|
||||
.into()
|
||||
} else {
|
||||
// Normal clickable tab — left click switches, right click opens context menu.
|
||||
let display = container(
|
||||
text(label.clone()).size(11).color(text_color),
|
||||
)
|
||||
.style(move |_: &Theme| container::Style {
|
||||
background: Some(Background::Color(bg(is_active, false))),
|
||||
border,
|
||||
..Default::default()
|
||||
})
|
||||
.padding([3, 10]);
|
||||
let display = container(text(label.clone()).size(11).color(text_color))
|
||||
.style(move |_: &Theme| container::Style {
|
||||
background: Some(Background::Color(bg(is_active, false))),
|
||||
border,
|
||||
..Default::default()
|
||||
})
|
||||
.padding([3, 10]);
|
||||
|
||||
let switch_msg = Message::LayoutSwitch(label.clone());
|
||||
let ctx_msg = Message::LayoutContextMenu(label.clone());
|
||||
|
|
@ -489,9 +524,17 @@ fn space_mode_btn(current_layout: &str, in_mspace: bool) -> Element<'static, Mes
|
|||
("PAPER", false, Some(Message::MspaceCommand))
|
||||
};
|
||||
|
||||
let text_color = if active { SNAP_BORDER_ON } else { OSNAP_OFF_TEXT };
|
||||
let text_color = if active {
|
||||
SNAP_BORDER_ON
|
||||
} else {
|
||||
OSNAP_OFF_TEXT
|
||||
};
|
||||
let bg_normal = if active { SNAP_ON_BG } else { SNAP_OFF_BG };
|
||||
let bg_hover = if active { SNAP_ON_HOVER } else { SNAP_OFF_HOVER };
|
||||
let bg_hover = if active {
|
||||
SNAP_ON_HOVER
|
||||
} else {
|
||||
SNAP_OFF_HOVER
|
||||
};
|
||||
let border_color = if active { SNAP_BORDER_ON } else { BORDER_COLOR };
|
||||
|
||||
let clickable = on_press.is_some();
|
||||
|
|
@ -631,26 +674,30 @@ const SNAP_OFF_HOVER: Color = Color {
|
|||
|
||||
fn scale_popup_btn(label: &str, open: bool) -> Element<'static, Message> {
|
||||
let label = label.to_string();
|
||||
button(text(label).size(10).color(if open { SNAP_BORDER_ON } else { OSNAP_OFF_TEXT }))
|
||||
.on_press(Message::ToggleScalePopup)
|
||||
.style(move |_: &Theme, status| button::Style {
|
||||
background: Some(Background::Color(match (open, status) {
|
||||
(true, button::Status::Hovered) => SNAP_ON_HOVER,
|
||||
(true, _) => SNAP_ON_BG,
|
||||
(false, button::Status::Hovered) => SNAP_OFF_HOVER,
|
||||
(false, _) => SNAP_OFF_BG,
|
||||
})),
|
||||
border: Border {
|
||||
color: if open { SNAP_BORDER_ON } else { BORDER_COLOR },
|
||||
width: 1.0,
|
||||
radius: 2.0.into(),
|
||||
},
|
||||
text_color: if open { SNAP_BORDER_ON } else { OSNAP_OFF_TEXT },
|
||||
shadow: iced::Shadow::default(),
|
||||
snap: false,
|
||||
})
|
||||
.padding([2, 6])
|
||||
.into()
|
||||
button(
|
||||
text(label)
|
||||
.size(10)
|
||||
.color(if open { SNAP_BORDER_ON } else { OSNAP_OFF_TEXT }),
|
||||
)
|
||||
.on_press(Message::ToggleScalePopup)
|
||||
.style(move |_: &Theme, status| button::Style {
|
||||
background: Some(Background::Color(match (open, status) {
|
||||
(true, button::Status::Hovered) => SNAP_ON_HOVER,
|
||||
(true, _) => SNAP_ON_BG,
|
||||
(false, button::Status::Hovered) => SNAP_OFF_HOVER,
|
||||
(false, _) => SNAP_OFF_BG,
|
||||
})),
|
||||
border: Border {
|
||||
color: if open { SNAP_BORDER_ON } else { BORDER_COLOR },
|
||||
width: 1.0,
|
||||
radius: 2.0.into(),
|
||||
},
|
||||
text_color: if open { SNAP_BORDER_ON } else { OSNAP_OFF_TEXT },
|
||||
shadow: iced::Shadow::default(),
|
||||
snap: false,
|
||||
})
|
||||
.padding([2, 6])
|
||||
.into()
|
||||
}
|
||||
|
||||
// ── Scale display ─────────────────────────────────────────────────────────
|
||||
|
|
@ -686,6 +733,8 @@ fn format_scale(scale: Option<f64>) -> String {
|
|||
}
|
||||
|
||||
// Fall back to a decimal string.
|
||||
format!("{:.4}", s).trim_end_matches('0').trim_end_matches('.').to_string()
|
||||
format!("{:.4}", s)
|
||||
.trim_end_matches('0')
|
||||
.trim_end_matches('.')
|
||||
.to_string()
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -4,25 +4,84 @@ use crate::app::Message;
|
|||
use iced::widget::{button, column, container, row, scrollable, text, Space};
|
||||
use iced::{Background, Border, Color, Element, Fill, Theme};
|
||||
|
||||
const TB: Color = Color { r: 0.13, g: 0.13, b: 0.13, a: 1.0 };
|
||||
const BG: Color = Color { r: 0.15, g: 0.15, b: 0.15, a: 1.0 };
|
||||
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 };
|
||||
const ACTIVE: Color = Color { r: 0.20, g: 0.40, b: 0.70, a: 1.0 };
|
||||
const LIST: Color = Color { r: 0.12, g: 0.12, b: 0.12, a: 1.0 };
|
||||
const TB: Color = Color {
|
||||
r: 0.13,
|
||||
g: 0.13,
|
||||
b: 0.13,
|
||||
a: 1.0,
|
||||
};
|
||||
const BG: Color = Color {
|
||||
r: 0.15,
|
||||
g: 0.15,
|
||||
b: 0.15,
|
||||
a: 1.0,
|
||||
};
|
||||
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,
|
||||
};
|
||||
const ACTIVE: Color = Color {
|
||||
r: 0.20,
|
||||
g: 0.40,
|
||||
b: 0.70,
|
||||
a: 1.0,
|
||||
};
|
||||
const LIST: Color = Color {
|
||||
r: 0.12,
|
||||
g: 0.12,
|
||||
b: 0.12,
|
||||
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 },
|
||||
(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() },
|
||||
border: Border {
|
||||
color: BORDER,
|
||||
width: 1.0,
|
||||
radius: 4.0.into(),
|
||||
},
|
||||
..Default::default()
|
||||
}
|
||||
}
|
||||
|
|
@ -30,8 +89,13 @@ fn btn_s(accent: bool) -> impl Fn(&Theme, button::Status) -> button::Style {
|
|||
fn list_item(active: bool) -> impl Fn(&Theme, button::Status) -> button::Style {
|
||||
move |_: &Theme, st| button::Style {
|
||||
background: Some(Background::Color(match (active, st) {
|
||||
(true, _) => ACTIVE,
|
||||
(false, button::Status::Hovered | button::Status::Pressed) => Color { r: 0.26, g: 0.26, b: 0.26, a: 1.0 },
|
||||
(true, _) => ACTIVE,
|
||||
(false, button::Status::Hovered | button::Status::Pressed) => Color {
|
||||
r: 0.26,
|
||||
g: 0.26,
|
||||
b: 0.26,
|
||||
a: 1.0,
|
||||
},
|
||||
_ => Color::TRANSPARENT,
|
||||
})),
|
||||
text_color: TEXT,
|
||||
|
|
@ -41,7 +105,8 @@ fn list_item(active: bool) -> impl Fn(&Theme, button::Status) -> button::Style {
|
|||
|
||||
fn hdivider<'a>() -> Element<'a, Message> {
|
||||
container(Space::new().width(Fill).height(1))
|
||||
.width(Fill).height(1)
|
||||
.width(Fill)
|
||||
.height(1)
|
||||
.style(|_: &Theme| container::Style {
|
||||
background: Some(Background::Color(BORDER)),
|
||||
..Default::default()
|
||||
|
|
@ -51,7 +116,8 @@ fn hdivider<'a>() -> Element<'a, Message> {
|
|||
|
||||
fn vsep<'a>() -> Element<'a, Message> {
|
||||
container(Space::new().width(1).height(Fill))
|
||||
.width(1).height(Fill)
|
||||
.width(1)
|
||||
.height(Fill)
|
||||
.style(|_: &Theme| container::Style {
|
||||
background: Some(Background::Color(BORDER)),
|
||||
..Default::default()
|
||||
|
|
@ -67,11 +133,17 @@ pub fn view_window<'a>(
|
|||
// ── Toolbar ───────────────────────────────────────────────────────────
|
||||
let toolbar = container(
|
||||
row![
|
||||
button(text("New").size(11)).on_press(Message::TableStyleDialogNew).style(btn_s(true)).padding([4, 10]),
|
||||
button(text("Delete").size(11)).on_press(Message::TableStyleDialogDelete).style(btn_s(false)).padding([4, 10]),
|
||||
button(text("New").size(11))
|
||||
.on_press(Message::TableStyleDialogNew)
|
||||
.style(btn_s(true))
|
||||
.padding([4, 10]),
|
||||
button(text("Delete").size(11))
|
||||
.on_press(Message::TableStyleDialogDelete)
|
||||
.style(btn_s(false))
|
||||
.padding([4, 10]),
|
||||
]
|
||||
.spacing(4)
|
||||
.align_y(iced::Center)
|
||||
.align_y(iced::Center),
|
||||
)
|
||||
.style(|_: &Theme| container::Style {
|
||||
background: Some(Background::Color(TB)),
|
||||
|
|
@ -81,15 +153,18 @@ pub fn view_window<'a>(
|
|||
.padding([5, 8]);
|
||||
|
||||
// ── Left: Style list ──────────────────────────────────────────────────
|
||||
let style_items: Vec<Element<'_, Message>> = styles.iter().map(|name| {
|
||||
let is_sel = name.as_str() == selected;
|
||||
button(text(name.clone()).size(11))
|
||||
.on_press(Message::TableStyleDialogSelect(name.clone()))
|
||||
.style(list_item(is_sel))
|
||||
.padding([4, 8])
|
||||
.width(Fill)
|
||||
.into()
|
||||
}).collect();
|
||||
let style_items: Vec<Element<'_, Message>> = styles
|
||||
.iter()
|
||||
.map(|name| {
|
||||
let is_sel = name.as_str() == selected;
|
||||
button(text(name.clone()).size(11))
|
||||
.on_press(Message::TableStyleDialogSelect(name.clone()))
|
||||
.style(list_item(is_sel))
|
||||
.padding([4, 8])
|
||||
.width(Fill)
|
||||
.into()
|
||||
})
|
||||
.collect();
|
||||
|
||||
let style_list = container(
|
||||
column![
|
||||
|
|
@ -97,7 +172,11 @@ pub fn view_window<'a>(
|
|||
container(scrollable(column(style_items).spacing(2)).height(Fill))
|
||||
.style(|_: &Theme| container::Style {
|
||||
background: Some(Background::Color(LIST)),
|
||||
border: Border { color: BORDER, width: 1.0, radius: 3.0.into() },
|
||||
border: Border {
|
||||
color: BORDER,
|
||||
width: 1.0,
|
||||
radius: 3.0.into()
|
||||
},
|
||||
..Default::default()
|
||||
})
|
||||
.width(Fill)
|
||||
|
|
@ -105,28 +184,39 @@ pub fn view_window<'a>(
|
|||
.padding(2),
|
||||
]
|
||||
.spacing(4)
|
||||
.height(Fill)
|
||||
.height(Fill),
|
||||
)
|
||||
.width(200)
|
||||
.height(Fill)
|
||||
.padding(iced::Padding { top: 12.0, right: 8.0, bottom: 12.0, left: 12.0 });
|
||||
.padding(iced::Padding {
|
||||
top: 12.0,
|
||||
right: 8.0,
|
||||
bottom: 12.0,
|
||||
left: 12.0,
|
||||
});
|
||||
|
||||
// ── Right: Details panel ──────────────────────────────────────────────
|
||||
let info_row = |label: &'static str, val: String| -> Element<'_, Message> {
|
||||
row![
|
||||
text(label).size(11).color(DIM).width(160),
|
||||
text(val).size(11),
|
||||
].spacing(8).align_y(iced::Center).into()
|
||||
]
|
||||
.spacing(8)
|
||||
.align_y(iced::Center)
|
||||
.into()
|
||||
};
|
||||
|
||||
let row_info = |row_label: &'static str, rs: &acadrust::objects::RowCellStyle| -> Element<'_, Message> {
|
||||
column![
|
||||
text(row_label).size(11).color(ACCENT),
|
||||
info_row(" Text Style:", rs.text_style_name.clone()),
|
||||
info_row(" Text Height:", format!("{:.4}", rs.text_height)),
|
||||
info_row(" Alignment:", format!("{:?}", rs.alignment)),
|
||||
].spacing(3).into()
|
||||
};
|
||||
let row_info =
|
||||
|row_label: &'static str, rs: &acadrust::objects::RowCellStyle| -> Element<'_, Message> {
|
||||
column![
|
||||
text(row_label).size(11).color(ACCENT),
|
||||
info_row(" Text Style:", rs.text_style_name.clone()),
|
||||
info_row(" Text Height:", format!("{:.4}", rs.text_height)),
|
||||
info_row(" Alignment:", format!("{:?}", rs.alignment)),
|
||||
]
|
||||
.spacing(3)
|
||||
.into()
|
||||
};
|
||||
|
||||
let details: Element<'_, Message> = if let Some(s) = selected_style {
|
||||
scrollable(
|
||||
|
|
@ -139,28 +229,28 @@ pub fn view_window<'a>(
|
|||
row_info("Data Row:", &s.data_row_style),
|
||||
row_info("Header Row:", &s.header_row_style),
|
||||
row_info("Title Row:", &s.title_row_style),
|
||||
].spacing(6).padding([12, 12])
|
||||
).height(Fill).into()
|
||||
]
|
||||
.spacing(6)
|
||||
.padding([12, 12]),
|
||||
)
|
||||
.height(Fill)
|
||||
.into()
|
||||
} else {
|
||||
container(
|
||||
text("Select a style to view details.").size(11).color(DIM)
|
||||
).padding([12, 12]).into()
|
||||
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);
|
||||
let right_panel = container(details).width(Fill).height(Fill);
|
||||
|
||||
let body = row![style_list, vsep(), right_panel].height(Fill);
|
||||
|
||||
container(
|
||||
column![toolbar, hdivider(), body].spacing(0)
|
||||
)
|
||||
.style(|_: &Theme| container::Style {
|
||||
background: Some(Background::Color(BG)),
|
||||
..Default::default()
|
||||
})
|
||||
.width(Fill)
|
||||
.height(Fill)
|
||||
.into()
|
||||
container(column![toolbar, hdivider(), body].spacing(0))
|
||||
.style(|_: &Theme| container::Style {
|
||||
background: Some(Background::Color(BG)),
|
||||
..Default::default()
|
||||
})
|
||||
.width(Fill)
|
||||
.height(Fill)
|
||||
.into()
|
||||
}
|
||||
|
|
|
|||
|
|
@ -4,34 +4,113 @@ use crate::app::Message;
|
|||
use iced::widget::{button, column, container, row, scrollable, text, text_input, Space};
|
||||
use iced::{Background, Border, Color, Element, Fill, Theme};
|
||||
|
||||
const TB: Color = Color { r: 0.13, g: 0.13, b: 0.13, a: 1.0 };
|
||||
const BG: Color = Color { r: 0.15, g: 0.15, b: 0.15, a: 1.0 };
|
||||
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 };
|
||||
const ACTIVE: Color = Color { r: 0.20, g: 0.40, b: 0.70, a: 1.0 };
|
||||
const FIELD: Color = Color { r: 0.10, g: 0.10, b: 0.10, a: 1.0 };
|
||||
const LIST: Color = Color { r: 0.12, g: 0.12, b: 0.12, a: 1.0 };
|
||||
const TB: Color = Color {
|
||||
r: 0.13,
|
||||
g: 0.13,
|
||||
b: 0.13,
|
||||
a: 1.0,
|
||||
};
|
||||
const BG: Color = Color {
|
||||
r: 0.15,
|
||||
g: 0.15,
|
||||
b: 0.15,
|
||||
a: 1.0,
|
||||
};
|
||||
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,
|
||||
};
|
||||
const ACTIVE: Color = Color {
|
||||
r: 0.20,
|
||||
g: 0.40,
|
||||
b: 0.70,
|
||||
a: 1.0,
|
||||
};
|
||||
const FIELD: Color = Color {
|
||||
r: 0.10,
|
||||
g: 0.10,
|
||||
b: 0.10,
|
||||
a: 1.0,
|
||||
};
|
||||
const LIST: Color = Color {
|
||||
r: 0.12,
|
||||
g: 0.12,
|
||||
b: 0.12,
|
||||
a: 1.0,
|
||||
};
|
||||
|
||||
const BUILTIN_FONTS: &[&str] = &[
|
||||
"CourierCad.cxf", "Cursive.cxf", "GothGBT.cxf", "GothGRT.cxf", "GothITT.cxf",
|
||||
"GreekC.cxf", "GreekS.cxf", "ItalicC.cxf", "ItalicT.cxf",
|
||||
"RomanC.cxf", "RomanD.cxf", "RomanS.cxf", "RomanT.cxf",
|
||||
"SansND.cxf", "SansNS.cxf", "ScriptC.cxf", "ScriptS.cxf",
|
||||
"Standard.cxf", "Unicode.cxf", "SymbolCad.cxf",
|
||||
"CourierCad.cxf",
|
||||
"Cursive.cxf",
|
||||
"GothGBT.cxf",
|
||||
"GothGRT.cxf",
|
||||
"GothITT.cxf",
|
||||
"GreekC.cxf",
|
||||
"GreekS.cxf",
|
||||
"ItalicC.cxf",
|
||||
"ItalicT.cxf",
|
||||
"RomanC.cxf",
|
||||
"RomanD.cxf",
|
||||
"RomanS.cxf",
|
||||
"RomanT.cxf",
|
||||
"SansND.cxf",
|
||||
"SansNS.cxf",
|
||||
"ScriptC.cxf",
|
||||
"ScriptS.cxf",
|
||||
"Standard.cxf",
|
||||
"Unicode.cxf",
|
||||
"SymbolCad.cxf",
|
||||
];
|
||||
|
||||
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 },
|
||||
(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() },
|
||||
border: Border {
|
||||
color: BORDER,
|
||||
width: 1.0,
|
||||
radius: 4.0.into(),
|
||||
},
|
||||
..Default::default()
|
||||
}
|
||||
}
|
||||
|
|
@ -39,8 +118,13 @@ fn btn_s(accent: bool) -> impl Fn(&Theme, button::Status) -> button::Style {
|
|||
fn list_item(active: bool) -> impl Fn(&Theme, button::Status) -> button::Style {
|
||||
move |_: &Theme, st| button::Style {
|
||||
background: Some(Background::Color(match (active, st) {
|
||||
(true, _) => ACTIVE,
|
||||
(false, button::Status::Hovered | button::Status::Pressed) => Color { r: 0.26, g: 0.26, b: 0.26, a: 1.0 },
|
||||
(true, _) => ACTIVE,
|
||||
(false, button::Status::Hovered | button::Status::Pressed) => Color {
|
||||
r: 0.26,
|
||||
g: 0.26,
|
||||
b: 0.26,
|
||||
a: 1.0,
|
||||
},
|
||||
_ => Color::TRANSPARENT,
|
||||
})),
|
||||
text_color: TEXT,
|
||||
|
|
@ -51,14 +135,22 @@ fn list_item(active: bool) -> impl Fn(&Theme, button::Status) -> button::Style {
|
|||
fn field_style(_: &Theme, _: text_input::Status) -> text_input::Style {
|
||||
text_input::Style {
|
||||
background: Background::Color(FIELD),
|
||||
border: Border { color: BORDER, width: 1.0, radius: 3.0.into() },
|
||||
icon: TEXT, placeholder: DIM, value: TEXT, selection: ACCENT,
|
||||
border: Border {
|
||||
color: BORDER,
|
||||
width: 1.0,
|
||||
radius: 3.0.into(),
|
||||
},
|
||||
icon: TEXT,
|
||||
placeholder: DIM,
|
||||
value: TEXT,
|
||||
selection: ACCENT,
|
||||
}
|
||||
}
|
||||
|
||||
fn hdivider<'a>() -> Element<'a, Message> {
|
||||
container(Space::new().width(Fill).height(1))
|
||||
.width(Fill).height(1)
|
||||
.width(Fill)
|
||||
.height(1)
|
||||
.style(|_: &Theme| container::Style {
|
||||
background: Some(Background::Color(BORDER)),
|
||||
..Default::default()
|
||||
|
|
@ -68,7 +160,8 @@ fn hdivider<'a>() -> Element<'a, Message> {
|
|||
|
||||
fn vsep<'a>() -> Element<'a, Message> {
|
||||
container(Space::new().width(1).height(Fill))
|
||||
.width(1).height(Fill)
|
||||
.width(1)
|
||||
.height(Fill)
|
||||
.style(|_: &Theme| container::Style {
|
||||
background: Some(Background::Color(BORDER)),
|
||||
..Default::default()
|
||||
|
|
@ -86,14 +179,26 @@ pub fn view_window<'a>(
|
|||
// ── Toolbar ───────────────────────────────────────────────────────────
|
||||
let toolbar = container(
|
||||
row![
|
||||
button(text("New").size(11)).on_press(Message::TextStyleDialogNew).style(btn_s(false)).padding([4, 10]),
|
||||
button(text("Delete").size(11)).on_press(Message::TextStyleDialogDelete).style(btn_s(false)).padding([4, 10]),
|
||||
button(text("New").size(11))
|
||||
.on_press(Message::TextStyleDialogNew)
|
||||
.style(btn_s(false))
|
||||
.padding([4, 10]),
|
||||
button(text("Delete").size(11))
|
||||
.on_press(Message::TextStyleDialogDelete)
|
||||
.style(btn_s(false))
|
||||
.padding([4, 10]),
|
||||
Space::new().width(Fill),
|
||||
button(text("Set Current").size(11)).on_press(Message::TextStyleDialogSetCurrent).style(btn_s(false)).padding([4, 10]),
|
||||
button(text("Apply").size(11)).on_press(Message::TextStyleApply).style(btn_s(true)).padding([4, 14]),
|
||||
button(text("Set Current").size(11))
|
||||
.on_press(Message::TextStyleDialogSetCurrent)
|
||||
.style(btn_s(false))
|
||||
.padding([4, 10]),
|
||||
button(text("Apply").size(11))
|
||||
.on_press(Message::TextStyleApply)
|
||||
.style(btn_s(true))
|
||||
.padding([4, 14]),
|
||||
]
|
||||
.spacing(4)
|
||||
.align_y(iced::Center)
|
||||
.align_y(iced::Center),
|
||||
)
|
||||
.style(|_: &Theme| container::Style {
|
||||
background: Some(Background::Color(TB)),
|
||||
|
|
@ -103,15 +208,18 @@ pub fn view_window<'a>(
|
|||
.padding([5, 8]);
|
||||
|
||||
// ── Left: Style list ──────────────────────────────────────────────────
|
||||
let style_items: Vec<Element<'_, Message>> = styles.iter().map(|name| {
|
||||
let is_sel = name.as_str() == selected;
|
||||
button(text(name.clone()).size(11))
|
||||
.on_press(Message::TextStyleDialogSelect(name.clone()))
|
||||
.style(list_item(is_sel))
|
||||
.padding([4, 8])
|
||||
.width(Fill)
|
||||
.into()
|
||||
}).collect();
|
||||
let style_items: Vec<Element<'_, Message>> = styles
|
||||
.iter()
|
||||
.map(|name| {
|
||||
let is_sel = name.as_str() == selected;
|
||||
button(text(name.clone()).size(11))
|
||||
.on_press(Message::TextStyleDialogSelect(name.clone()))
|
||||
.style(list_item(is_sel))
|
||||
.padding([4, 8])
|
||||
.width(Fill)
|
||||
.into()
|
||||
})
|
||||
.collect();
|
||||
|
||||
let style_panel = container(
|
||||
column![
|
||||
|
|
@ -119,7 +227,11 @@ pub fn view_window<'a>(
|
|||
container(scrollable(column(style_items).spacing(1)).height(Fill))
|
||||
.style(|_: &Theme| container::Style {
|
||||
background: Some(Background::Color(LIST)),
|
||||
border: Border { color: BORDER, width: 1.0, radius: 3.0.into() },
|
||||
border: Border {
|
||||
color: BORDER,
|
||||
width: 1.0,
|
||||
radius: 3.0.into()
|
||||
},
|
||||
..Default::default()
|
||||
})
|
||||
.width(Fill)
|
||||
|
|
@ -127,22 +239,30 @@ pub fn view_window<'a>(
|
|||
.padding(2),
|
||||
]
|
||||
.spacing(4)
|
||||
.height(Fill)
|
||||
.height(Fill),
|
||||
)
|
||||
.width(170)
|
||||
.height(Fill)
|
||||
.padding(iced::Padding { top: 12.0, right: 8.0, bottom: 12.0, left: 12.0 });
|
||||
.padding(iced::Padding {
|
||||
top: 12.0,
|
||||
right: 8.0,
|
||||
bottom: 12.0,
|
||||
left: 12.0,
|
||||
});
|
||||
|
||||
// ── Middle: Font browser ──────────────────────────────────────────────
|
||||
let font_items: Vec<Element<'_, Message>> = BUILTIN_FONTS.iter().map(|&f| {
|
||||
let is_sel = font_buf == f;
|
||||
button(text(f).size(10))
|
||||
.on_press(Message::TextStyleFontPick(f.to_string()))
|
||||
.style(list_item(is_sel))
|
||||
.padding([3, 8])
|
||||
.width(Fill)
|
||||
.into()
|
||||
}).collect();
|
||||
let font_items: Vec<Element<'_, Message>> = BUILTIN_FONTS
|
||||
.iter()
|
||||
.map(|&f| {
|
||||
let is_sel = font_buf == f;
|
||||
button(text(f).size(10))
|
||||
.on_press(Message::TextStyleFontPick(f.to_string()))
|
||||
.style(list_item(is_sel))
|
||||
.padding([3, 8])
|
||||
.width(Fill)
|
||||
.into()
|
||||
})
|
||||
.collect();
|
||||
|
||||
let font_panel = container(
|
||||
column![
|
||||
|
|
@ -150,20 +270,27 @@ pub fn view_window<'a>(
|
|||
container(scrollable(column(font_items).spacing(1)).height(Fill))
|
||||
.style(|_: &Theme| container::Style {
|
||||
background: Some(Background::Color(LIST)),
|
||||
border: Border { color: BORDER, width: 1.0, radius: 3.0.into() },
|
||||
border: Border {
|
||||
color: BORDER,
|
||||
width: 1.0,
|
||||
radius: 3.0.into()
|
||||
},
|
||||
..Default::default()
|
||||
})
|
||||
.width(Fill)
|
||||
.height(Fill)
|
||||
.padding(2),
|
||||
text_input("font file…", font_buf)
|
||||
.on_input(|v| Message::TextStyleEdit { field: "font", value: v })
|
||||
.on_input(|v| Message::TextStyleEdit {
|
||||
field: "font",
|
||||
value: v
|
||||
})
|
||||
.style(field_style)
|
||||
.size(11)
|
||||
.width(Fill),
|
||||
]
|
||||
.spacing(6)
|
||||
.height(Fill)
|
||||
.height(Fill),
|
||||
)
|
||||
.width(190)
|
||||
.height(Fill)
|
||||
|
|
@ -176,45 +303,64 @@ pub fn view_window<'a>(
|
|||
row![
|
||||
text("Width Factor:").size(11).color(DIM).width(120),
|
||||
text_input("1.0", width_buf)
|
||||
.on_input(|v| Message::TextStyleEdit { field: "width", value: v })
|
||||
.style(field_style).size(11).width(90),
|
||||
].spacing(6).align_y(iced::Center),
|
||||
.on_input(|v| Message::TextStyleEdit {
|
||||
field: "width",
|
||||
value: v
|
||||
})
|
||||
.style(field_style)
|
||||
.size(11)
|
||||
.width(90),
|
||||
]
|
||||
.spacing(6)
|
||||
.align_y(iced::Center),
|
||||
row![
|
||||
text("Oblique (°):").size(11).color(DIM).width(120),
|
||||
text_input("0.0", oblique_buf)
|
||||
.on_input(|v| Message::TextStyleEdit { field: "oblique", value: v })
|
||||
.style(field_style).size(11).width(90),
|
||||
].spacing(6).align_y(iced::Center),
|
||||
.on_input(|v| Message::TextStyleEdit {
|
||||
field: "oblique",
|
||||
value: v
|
||||
})
|
||||
.style(field_style)
|
||||
.size(11)
|
||||
.width(90),
|
||||
]
|
||||
.spacing(6)
|
||||
.align_y(iced::Center),
|
||||
Space::new().height(8),
|
||||
text("Preview").size(10).color(DIM),
|
||||
container(
|
||||
text("AaBbCc 0123").size(20)
|
||||
)
|
||||
.style(|_: &Theme| container::Style {
|
||||
background: Some(Background::Color(FIELD)),
|
||||
border: Border { color: BORDER, width: 1.0, radius: 4.0.into() },
|
||||
..Default::default()
|
||||
})
|
||||
.padding(12)
|
||||
.width(Fill),
|
||||
container(text("AaBbCc 0123").size(20))
|
||||
.style(|_: &Theme| container::Style {
|
||||
background: Some(Background::Color(FIELD)),
|
||||
border: Border {
|
||||
color: BORDER,
|
||||
width: 1.0,
|
||||
radius: 4.0.into()
|
||||
},
|
||||
..Default::default()
|
||||
})
|
||||
.padding(12)
|
||||
.width(Fill),
|
||||
]
|
||||
.spacing(10)
|
||||
.height(Fill)
|
||||
.height(Fill),
|
||||
)
|
||||
.width(Fill)
|
||||
.height(Fill)
|
||||
.padding(iced::Padding { top: 12.0, right: 12.0, bottom: 12.0, left: 8.0 });
|
||||
.padding(iced::Padding {
|
||||
top: 12.0,
|
||||
right: 12.0,
|
||||
bottom: 12.0,
|
||||
left: 8.0,
|
||||
});
|
||||
|
||||
let body = row![style_panel, vsep(), font_panel, vsep(), props_panel].height(Fill);
|
||||
|
||||
container(
|
||||
column![toolbar, hdivider(), body].spacing(0)
|
||||
)
|
||||
.style(|_: &Theme| container::Style {
|
||||
background: Some(Background::Color(BG)),
|
||||
..Default::default()
|
||||
})
|
||||
.width(Fill)
|
||||
.height(Fill)
|
||||
.into()
|
||||
container(column![toolbar, hdivider(), body].spacing(0))
|
||||
.style(|_: &Theme| container::Style {
|
||||
background: Some(Background::Color(BG)),
|
||||
..Default::default()
|
||||
})
|
||||
.width(Fill)
|
||||
.height(Fill)
|
||||
.into()
|
||||
}
|
||||
|
|
|
|||
Loading…
Reference in a new issue