feat(locale): localize full interface
This commit is contained in:
parent
b90b3e3ecf
commit
3e6693b93f
205 changed files with 32367 additions and 3808 deletions
|
|
@ -8,6 +8,7 @@ use crate::ui::properties::acad_color_display;
|
|||
use acadrust::types::Color as AcadColor;
|
||||
use iced::widget::{button, column, container, row, text};
|
||||
use iced::{Background, Border, Color, Element, Length, Theme};
|
||||
use crate::t;
|
||||
|
||||
/// Which "logical" entries the colour list offers besides the standard ACI
|
||||
/// colours.
|
||||
|
|
@ -86,7 +87,7 @@ pub fn color_display_name(c: AcadColor) -> String {
|
|||
_ => label.to_string(),
|
||||
}
|
||||
} else {
|
||||
label.to_string()
|
||||
t!(label).into_owned()
|
||||
}
|
||||
}
|
||||
|
||||
|
|
@ -196,7 +197,7 @@ pub fn color_list<'a>(
|
|||
let named_row = |color: AcadColor| -> Element<'a, Message> {
|
||||
let (bg, name) = acad_color_display(color);
|
||||
button(
|
||||
row![swatch(bg), text(name).size(11)]
|
||||
row![swatch(bg), text(t!(name)).size(11)]
|
||||
.spacing(5)
|
||||
.align_y(iced::Center),
|
||||
)
|
||||
|
|
@ -218,7 +219,7 @@ pub fn color_list<'a>(
|
|||
list = list.push(named_row(AcadColor::Index(i)));
|
||||
}
|
||||
list = list.push(
|
||||
button(text("More…").size(11))
|
||||
button(text(t!("More…")).size(11))
|
||||
.on_press(on_more)
|
||||
.style(list_row_style)
|
||||
.padding([2, 4])
|
||||
|
|
|
|||
|
|
@ -4,6 +4,7 @@ use iced::time::Instant;
|
|||
|
||||
use crate::app::Message;
|
||||
use crate::command::CmdOption;
|
||||
use crate::t;
|
||||
use iced::widget::{
|
||||
button, column, container, opaque, row, rule, text, text_editor, text_input, tooltip, Space,
|
||||
};
|
||||
|
|
@ -194,20 +195,20 @@ impl CommandLine {
|
|||
}
|
||||
|
||||
pub fn push_command(&mut self, cmd: &str) {
|
||||
self.push(EntryKind::Command, format!("Command: {cmd}"));
|
||||
self.push(EntryKind::Command, format!("{} {cmd}", t!("Command:")));
|
||||
}
|
||||
pub fn push_output(&mut self, msg: &str) {
|
||||
self.push(EntryKind::Output, msg.to_string());
|
||||
}
|
||||
pub fn push_error(&mut self, msg: &str) {
|
||||
self.push(EntryKind::Error, format!("*Invalid* {msg}"));
|
||||
self.push(EntryKind::Error, format!("*{}* {msg}", t!("Invalid")));
|
||||
}
|
||||
/// Append an error unless it is already the latest history line. Repeated
|
||||
/// retry failures should refresh the concise message, not flood history
|
||||
/// with identical copies (#498).
|
||||
#[cfg(not(target_arch = "wasm32"))]
|
||||
pub fn push_error_once(&mut self, msg: &str) {
|
||||
let text = format!("*Invalid* {msg}");
|
||||
let text = format!("*{}* {msg}", t!("Invalid"));
|
||||
if let Some(last) = self
|
||||
.history
|
||||
.last_mut()
|
||||
|
|
@ -579,7 +580,7 @@ impl CommandLine {
|
|||
let copy_btn = button(
|
||||
row![
|
||||
crate::ui::icons::themed_success(crate::ui::icons::COPY, 11.0),
|
||||
text("Copy").size(11),
|
||||
text(t!("Copy")).size(11),
|
||||
]
|
||||
.spacing(4)
|
||||
.align_y(iced::Center),
|
||||
|
|
@ -590,7 +591,7 @@ impl CommandLine {
|
|||
let clear_btn = button(
|
||||
row![
|
||||
crate::ui::icons::themed_warning(crate::ui::icons::TRASH, 11.0),
|
||||
text("Clear").size(11),
|
||||
text(t!("Clear")).size(11),
|
||||
]
|
||||
.spacing(4)
|
||||
.align_y(iced::Center),
|
||||
|
|
@ -814,8 +815,8 @@ mod tests {
|
|||
fn issue_498_repeated_save_error_is_not_duplicated() {
|
||||
let mut line = CommandLine::new();
|
||||
let initial_len = line.history.len();
|
||||
line.push_error_once("Unable to save: file is in use.");
|
||||
line.push_error_once("Unable to save: file is in use.");
|
||||
line.push_error_once(t!("Unable to save: file is in use.").as_ref());
|
||||
line.push_error_once(t!("Unable to save: file is in use.").as_ref());
|
||||
assert_eq!(line.history.len(), initial_len + 1);
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -322,7 +322,16 @@ pub fn modal<'a>(
|
|||
// The first stack layer dictates its intrinsic size. Its top spacer reserves
|
||||
// room for the title, while the actual title bar and resize grip overlay it
|
||||
// without influencing the modal dimensions.
|
||||
let measured_content = sensor(content).on_resize(Message::ModalContentResized);
|
||||
// Iced draws vertical scrollbars inside a scrollable's bounds. Reserve a
|
||||
// shared end gutter at the modal boundary so a scrollbar cannot cover the
|
||||
// last control column in Options or any other dialog.
|
||||
let measured_content = container(sensor(content).on_resize(Message::ModalContentResized))
|
||||
.padding(Padding {
|
||||
top: 0.0,
|
||||
right: 12.0,
|
||||
bottom: 0.0,
|
||||
left: 0.0,
|
||||
});
|
||||
let body_base = column![
|
||||
Space::new().height(Length::Fixed(24.0)),
|
||||
measured_content,
|
||||
|
|
|
|||
|
|
@ -5,6 +5,7 @@ use iced::{Element, Fill};
|
|||
|
||||
use crate::app::Message;
|
||||
use crate::ui::statusbar::status_menu::Entry;
|
||||
use crate::t;
|
||||
|
||||
/// - `has_selection`: enables Isolate / Hide (they act on the selection).
|
||||
/// - `isolation_active`: enables End Isolation (something is hidden).
|
||||
|
|
@ -41,7 +42,7 @@ fn action_entry(label: &'static str, enabled: bool, msg: Message) -> Entry<'stat
|
|||
}
|
||||
|
||||
fn action_row(label: &'static str, enabled: bool, msg: Message) -> Element<'static, Message> {
|
||||
let lbl = text(label).size(11);
|
||||
let lbl = text(t!(label)).size(11);
|
||||
let content = row![lbl].align_y(iced::Center);
|
||||
|
||||
let mut btn = button(content)
|
||||
|
|
|
|||
|
|
@ -2,6 +2,7 @@
|
|||
|
||||
use iced::widget::{button, container, row, text, text_input};
|
||||
use iced::{Element, Fill, Length};
|
||||
use crate::t;
|
||||
|
||||
use crate::app::Message;
|
||||
use crate::ui::statusbar::status_menu::Entry;
|
||||
|
|
@ -32,7 +33,7 @@ pub fn menu_entries<'a>(
|
|||
.collect();
|
||||
|
||||
// Free-entry custom angle: type a value and press Enter to apply.
|
||||
let custom_field = text_input("Custom…", custom)
|
||||
let custom_field = text_input(t!("Custom…").as_ref(), custom)
|
||||
.on_input(Message::PolarCustomInput)
|
||||
.on_submit(Message::SubmitPolarCustom)
|
||||
.size(11)
|
||||
|
|
|
|||
|
|
@ -2,6 +2,7 @@
|
|||
|
||||
use iced::widget::{button, row, text};
|
||||
use iced::{Element, Fill};
|
||||
use crate::t;
|
||||
|
||||
use crate::app::Message;
|
||||
use crate::ui::statusbar::status_menu::Entry;
|
||||
|
|
@ -63,7 +64,7 @@ fn scale_row(label: String, active: bool, msg: Message) -> Element<'static, Mess
|
|||
}
|
||||
|
||||
fn manage_row() -> Element<'static, Message> {
|
||||
button(text("Manage...").size(11))
|
||||
button(text(t!("Manage...")).size(11))
|
||||
.on_press(Message::ScaleManagerOpen)
|
||||
.style(button::primary)
|
||||
.width(Fill)
|
||||
|
|
|
|||
|
|
@ -6,6 +6,7 @@ use rustc_hash::FxHashSet as HashSet;
|
|||
|
||||
use iced::widget::{button, container, row, text};
|
||||
use iced::{Background, Element, Fill, Theme};
|
||||
use crate::t;
|
||||
|
||||
use crate::app::Message;
|
||||
use crate::ui::statusbar::status_menu::Entry;
|
||||
|
|
@ -66,7 +67,7 @@ pub fn menu_entries(
|
|||
fn type_row(name: String, included: bool) -> Element<'static, Message> {
|
||||
let check = crate::ui::icons::themed_check_cell(included);
|
||||
|
||||
let lbl = text(name.clone()).size(11);
|
||||
let lbl = text(t!(name.as_str())).size(11);
|
||||
|
||||
let content = row![check, lbl].spacing(6).align_y(iced::Center);
|
||||
|
||||
|
|
@ -80,7 +81,7 @@ fn type_row(name: String, included: bool) -> Element<'static, Message> {
|
|||
|
||||
fn empty_row() -> Element<'static, Message> {
|
||||
container(
|
||||
text("No objects").size(11).style(|theme: &Theme| text::Style {
|
||||
text(t!("No objects")).size(11).style(|theme: &Theme| text::Style {
|
||||
color: Some(
|
||||
theme
|
||||
.palette()
|
||||
|
|
@ -96,7 +97,7 @@ fn empty_row() -> Element<'static, Message> {
|
|||
}
|
||||
|
||||
fn header_btn(label: &str, msg: Message, enabled: bool) -> Element<'_, Message> {
|
||||
let b = button(text(label).size(10));
|
||||
let b = button(text(t!(label)).size(10));
|
||||
let b = if enabled { b.on_press(msg) } else { b };
|
||||
b.style(button::secondary)
|
||||
.padding([3, 8])
|
||||
|
|
|
|||
|
|
@ -6,6 +6,7 @@ use iced::{Background, Element, Fill, Length, Theme};
|
|||
use crate::app::Message;
|
||||
use crate::snap::{SnapType, Snapper, ALL_SNAP_MODES};
|
||||
use crate::ui::statusbar::status_menu::Entry;
|
||||
use crate::t;
|
||||
|
||||
pub fn menu_entries<'a>(snapper: &'a Snapper) -> Vec<Entry<'a>> {
|
||||
let all_on = snapper.all_on();
|
||||
|
|
@ -54,7 +55,7 @@ fn snap_row<'a>(snap_type: SnapType, label: &'a str, active: bool) -> Element<'a
|
|||
.width(Length::Fixed(16.0))
|
||||
.align_x(iced::Center);
|
||||
|
||||
let label_el = text(label).size(11);
|
||||
let label_el = text(t!(label)).size(11);
|
||||
|
||||
let content = row![checkmark, icon_el, label_el]
|
||||
.spacing(4)
|
||||
|
|
@ -69,7 +70,7 @@ fn snap_row<'a>(snap_type: SnapType, label: &'a str, active: bool) -> Element<'a
|
|||
}
|
||||
|
||||
fn header_btn(label: &str, msg: Message, enabled: bool) -> Element<'_, Message> {
|
||||
let b = button(text(label).size(10));
|
||||
let b = button(text(t!(label)).size(10));
|
||||
let b = if enabled { b.on_press(msg) } else { b };
|
||||
b.style(button::secondary)
|
||||
.padding([3, 8])
|
||||
|
|
|
|||
|
|
@ -5,6 +5,7 @@ use iced::{Element, Fill};
|
|||
|
||||
use crate::app::Message;
|
||||
use crate::ui::statusbar::status_menu::Entry;
|
||||
use crate::t;
|
||||
|
||||
/// Units offered in the picker: (INSUNITS code, menu label).
|
||||
const UNITS: &[(i16, &str)] = &[
|
||||
|
|
@ -51,7 +52,7 @@ pub fn menu_entries(current: i16) -> Vec<Entry<'static>> {
|
|||
fn unit_row(label: &'static str, active: bool, msg: Message) -> Element<'static, Message> {
|
||||
let check = crate::ui::icons::themed_check_cell(active);
|
||||
|
||||
let lbl = text(label).size(11);
|
||||
let lbl = text(t!(label)).size(11);
|
||||
|
||||
let content = row![check, lbl].spacing(6).align_y(iced::Center);
|
||||
|
||||
|
|
|
|||
|
|
@ -19,6 +19,7 @@ use iced::widget::{
|
|||
use iced::{
|
||||
mouse, Background, Border, Color, Element, Length, Padding, Point, Rectangle, Size, Theme,
|
||||
};
|
||||
use crate::t;
|
||||
|
||||
// ── Row-height-derived constants ─────────────────────────────────────────
|
||||
const FONT_SZ: f32 = ROW_H * 0.42; // ≈11 px
|
||||
|
|
@ -60,9 +61,9 @@ pub struct LwItem(pub LineWeight);
|
|||
impl fmt::Display for LwItem {
|
||||
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
|
||||
match self.0 {
|
||||
LineWeight::ByLayer => write!(f, "ByLayer"),
|
||||
LineWeight::ByBlock => write!(f, "ByBlock"),
|
||||
LineWeight::Default => write!(f, "Default"),
|
||||
LineWeight::ByLayer => write!(f, "{}", crate::t!("ByLayer")),
|
||||
LineWeight::ByBlock => write!(f, "{}", crate::t!("ByBlock")),
|
||||
LineWeight::Default => write!(f, "{}", crate::t!("Default")),
|
||||
LineWeight::Value(v) => write!(f, "{:.2} mm", v as f64 / 100.0),
|
||||
}
|
||||
}
|
||||
|
|
@ -244,6 +245,27 @@ pub fn attr_edit_key(tag: &str) -> String {
|
|||
format!("\x01attr\x01{tag}")
|
||||
}
|
||||
|
||||
/// A translated choice label paired with the unchanged value stored in the
|
||||
/// drawing and emitted by the properties panel.
|
||||
#[derive(Clone, Debug, PartialEq, Eq)]
|
||||
pub struct LocalizedChoice {
|
||||
pub raw: String,
|
||||
label: String,
|
||||
}
|
||||
|
||||
impl LocalizedChoice {
|
||||
pub fn new(raw: String) -> Self {
|
||||
let label = crate::i18n::translate(&raw).into_owned();
|
||||
Self { raw, label }
|
||||
}
|
||||
}
|
||||
|
||||
impl fmt::Display for LocalizedChoice {
|
||||
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
|
||||
f.write_str(&self.label)
|
||||
}
|
||||
}
|
||||
|
||||
// ── PropertiesPanel ───────────────────────────────────────────────────────
|
||||
|
||||
#[derive(Clone)]
|
||||
|
|
@ -255,7 +277,7 @@ pub struct PropertiesPanel {
|
|||
/// Linetype items (name + ASCII art) from the document — used for combo_box options.
|
||||
pub linetype_items: Vec<LinetypeItem>,
|
||||
pub selection_group_combo: combo_box::State<SelectionGroup>,
|
||||
pub choice_combos: HashMap<String, combo_box::State<String>>,
|
||||
pub choice_combos: HashMap<String, combo_box::State<LocalizedChoice>>,
|
||||
pub layer_combo: combo_box::State<String>,
|
||||
pub lineweight_combo: combo_box::State<LwItem>,
|
||||
pub linetype_combo: combo_box::State<LinetypeItem>,
|
||||
|
|
@ -333,7 +355,7 @@ impl Default for PropertiesPanel {
|
|||
impl PropertiesPanel {
|
||||
pub fn empty() -> Self {
|
||||
Self {
|
||||
title: "No Selection".into(),
|
||||
title: t!("No selection").into_owned(),
|
||||
..Default::default()
|
||||
}
|
||||
}
|
||||
|
|
@ -347,7 +369,7 @@ impl PropertiesPanel {
|
|||
|
||||
pub fn view(&self) -> Element<'_, Message> {
|
||||
// ── Header ──────────────────────────────────────────────────────────
|
||||
let header = container(text("Properties").size(12))
|
||||
let header = container(text(t!("Properties")).size(12))
|
||||
.style(|theme: &Theme| container::Style {
|
||||
background: Some(Background::Color(
|
||||
theme.palette().background.weak.color,
|
||||
|
|
@ -397,7 +419,7 @@ impl PropertiesPanel {
|
|||
// ── Content ─────────────────────────────────────────────────────────
|
||||
let content: Element<'_, Message> = if self.sections.is_empty() {
|
||||
container(
|
||||
text("Select an object to view properties")
|
||||
text(t!("Select an object to view properties"))
|
||||
.size(10)
|
||||
.style(hint_text_style),
|
||||
)
|
||||
|
|
@ -823,10 +845,13 @@ impl PropertiesPanel {
|
|||
let selected = if current == VARIES_LABEL {
|
||||
None
|
||||
} else {
|
||||
Some(current.to_string())
|
||||
Some(LocalizedChoice::new(current.to_string()))
|
||||
};
|
||||
let combo = combo_box(state, VARIES_LABEL, selected.as_ref(), move |value| {
|
||||
Message::PropGeomChoiceChanged { field, value }
|
||||
let combo = combo_box(state, VARIES_LABEL, selected.as_ref(), move |choice| {
|
||||
Message::PropGeomChoiceChanged {
|
||||
field,
|
||||
value: choice.raw,
|
||||
}
|
||||
})
|
||||
.size(FONT_SZ)
|
||||
.padding(Padding {
|
||||
|
|
@ -1058,7 +1083,7 @@ impl PropertiesPanel {
|
|||
return prop_row_widget(label, head.into());
|
||||
}
|
||||
|
||||
let search = text_input("Search patterns…", &self.hatch_pattern_search)
|
||||
let search = text_input(t!("Search patterns…").as_ref(), &self.hatch_pattern_search)
|
||||
.id(iced::widget::Id::new("hatch-pattern-search"))
|
||||
.on_input(Message::PropHatchPatternSearchChanged)
|
||||
.on_submit(Message::PropHatchPatternConfirm)
|
||||
|
|
@ -1127,7 +1152,7 @@ impl PropertiesPanel {
|
|||
|
||||
let results: Element<'_, Message> = if visible.is_empty() {
|
||||
container(
|
||||
text("No matching patterns")
|
||||
text(t!("No matching patterns"))
|
||||
.size(FONT_SZ)
|
||||
.style(hint_text_style),
|
||||
)
|
||||
|
|
@ -1227,9 +1252,13 @@ pub fn color_picker_dropdown<'a>(
|
|||
} else {
|
||||
crate::ui::icons::themed_arrow_down(9.0)
|
||||
},
|
||||
text(if palette_open { "Less" } else { "More Colors…" })
|
||||
.size(10)
|
||||
.style(hint_text_style),
|
||||
text(if palette_open {
|
||||
t!("Less").into_owned()
|
||||
} else {
|
||||
t!("More Colors…").into_owned()
|
||||
})
|
||||
.size(10)
|
||||
.style(hint_text_style),
|
||||
]
|
||||
.spacing(4)
|
||||
.align_y(iced::Center),
|
||||
|
|
@ -1365,7 +1394,11 @@ fn render_stepper_row<'a>(label: &'a str, display: &'a str) -> Element<'a, Messa
|
|||
}
|
||||
|
||||
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_label = if value {
|
||||
t!("Yes").into_owned()
|
||||
} else {
|
||||
t!("No").into_owned()
|
||||
};
|
||||
let btn =
|
||||
button(
|
||||
text(btn_label)
|
||||
|
|
@ -1617,7 +1650,7 @@ fn prop_row_widget<'a>(label: &'a str, widget: Element<'a, Message>) -> Element<
|
|||
|
||||
/// A plain text button used inside the color picker for ByLayer / ByBlock.
|
||||
fn picker_text_btn(label: &str, msg: Message) -> Element<'_, Message> {
|
||||
button(text(label).size(FONT_SZ))
|
||||
button(text(t!(label)).size(FONT_SZ))
|
||||
.on_press(msg)
|
||||
.style(button::secondary)
|
||||
.padding([2, 8])
|
||||
|
|
|
|||
|
|
@ -26,6 +26,7 @@ mod collapse;
|
|||
use collapse::{CollapsePanels, Panel};
|
||||
pub use collapse::CollapseMode;
|
||||
use crate::ui::wrap_bar::{PosReport, WrapBar, WrapFlow};
|
||||
use crate::t;
|
||||
|
||||
// ── Ribbon state ───────────────────────────────────────────────────────────
|
||||
|
||||
|
|
@ -742,7 +743,7 @@ impl Ribbon {
|
|||
button(
|
||||
row![
|
||||
crate::ui::icons::themed_check_cell(m == current),
|
||||
text(m.label()).size(11),
|
||||
text(t!(m.label())).size(11),
|
||||
]
|
||||
.spacing(4)
|
||||
.align_y(iced::Center),
|
||||
|
|
@ -824,7 +825,7 @@ impl Ribbon {
|
|||
.width(Length::Fixed(20.0))
|
||||
.into();
|
||||
let label_el =
|
||||
text(*label)
|
||||
text(t!(*label))
|
||||
.size(11)
|
||||
.style(move |theme: &Theme| iced::widget::text::Style {
|
||||
color: (!is_current).then_some(
|
||||
|
|
@ -958,14 +959,14 @@ impl Ribbon {
|
|||
let row_count = rows.len().max(1);
|
||||
let list_h = (row_count as f32 * 26.0).min(420.0);
|
||||
// Search box (#343): filters the list live as the user types.
|
||||
let search = iced::widget::text_input("Search layers…", &self.layer_filter)
|
||||
let search = iced::widget::text_input(t!("Search layers…").as_ref(), &self.layer_filter)
|
||||
.on_input(Message::RibbonLayerFilterChanged)
|
||||
.size(11)
|
||||
.padding([4, 6]);
|
||||
let state_manager = button(
|
||||
row![
|
||||
crate::ui::icons::themed_arrow_right(9.0),
|
||||
text("Layer State Manager…").size(11),
|
||||
text(t!("Layer State Manager…")).size(11),
|
||||
]
|
||||
.spacing(7)
|
||||
.align_y(iced::Center),
|
||||
|
|
@ -1081,7 +1082,7 @@ impl Ribbon {
|
|||
|
||||
if let Some(mgr) = manager_cmd {
|
||||
rows.push(
|
||||
button(text("Manage…").size(11))
|
||||
button(text(t!("Manage…")).size(11))
|
||||
.on_press(Message::Command(mgr.to_string()))
|
||||
.style(popup_row_style)
|
||||
.width(Fill)
|
||||
|
|
@ -1323,7 +1324,7 @@ fn render_group<'a>(
|
|||
|
||||
column![
|
||||
tools_el,
|
||||
container(text(group.title).size(9).style(muted_text_style)).padding([1, 4]),
|
||||
container(text(t!(group.title)).size(9).style(muted_text_style)).padding([1, 4]),
|
||||
]
|
||||
.align_x(iced::Center)
|
||||
.spacing(0)
|
||||
|
|
@ -1410,7 +1411,7 @@ fn collapse_button<'a>(
|
|||
column![
|
||||
icon,
|
||||
row![
|
||||
text(title.to_string()).size(9).style(muted_text_style),
|
||||
text(t!(title)).size(9).style(muted_text_style),
|
||||
crate::ui::icons::themed_secondary_arrow_down(8.0),
|
||||
]
|
||||
.spacing(3)
|
||||
|
|
|
|||
|
|
@ -16,6 +16,7 @@ use crate::modules::{IconKind, ModuleEvent, RibbonItem, StyleKey, ToolDef};
|
|||
use crate::ui::wrap_bar::PosReport;
|
||||
use crate::ui::icons;
|
||||
use crate::ui::properties::{acad_color_display, LwItem};
|
||||
use crate::t;
|
||||
|
||||
use super::LayerInfo;
|
||||
|
||||
|
|
@ -311,7 +312,7 @@ pub(super) fn render_small<'a>(
|
|||
let dim = start_dimmed(&state, &t.event);
|
||||
let event = t.event.clone();
|
||||
let tool_id = t.id.to_string();
|
||||
let tip_text = format!("{}\nCommand: {}", t.label, t.id);
|
||||
let tip_text = format!("{}\n{} {}", t!(t.label), t!("Command:"), t.id);
|
||||
let btn = button(make_icon_dim(t.icon, SMALL_ICON, dim))
|
||||
.on_press(Message::RibbonToolClick { tool_id, event })
|
||||
.style(move |theme: &Theme, status| tool_btn_style(theme, active, status))
|
||||
|
|
@ -373,7 +374,7 @@ pub(super) fn render_small<'a>(
|
|||
})
|
||||
.or_else(|| items.first().map(|(_, lbl, _)| *lbl))
|
||||
.unwrap_or(*id);
|
||||
let tip_text = format!("{}\nCommand: {}", cur_label, last);
|
||||
let tip_text = format!("{}\n{} {}", t!(cur_label), t!("Command:"), last);
|
||||
|
||||
let icon_btn = button(make_icon_dim(cur_icon, SMALL_ICON, dim))
|
||||
.on_press(Message::RibbonToolClick {
|
||||
|
|
@ -385,7 +386,7 @@ pub(super) fn render_small<'a>(
|
|||
.height(ROW_H)
|
||||
.padding([4, 4]);
|
||||
|
||||
let arr_tip = format!("{} options", cur_label);
|
||||
let arr_tip = format!("{} {}", t!(cur_label), t!("options"));
|
||||
let arr_btn = button(
|
||||
container(icons::themed_arrow_down(8.0))
|
||||
.width(Fill)
|
||||
|
|
@ -431,7 +432,7 @@ pub(super) fn render_small<'a>(
|
|||
pub(super) fn render_large_dropdown<'a>(
|
||||
id: &'static str,
|
||||
icon: IconKind,
|
||||
explicit_label: Option<&str>,
|
||||
explicit_label: Option<&'a str>,
|
||||
items: &[(&'static str, &'static str, IconKind)],
|
||||
default: &'static str,
|
||||
active_tool: &Option<String>,
|
||||
|
|
@ -459,14 +460,14 @@ pub(super) fn render_large_dropdown<'a>(
|
|||
.or_else(|| items.first().map(|(_, lbl, _)| *lbl))
|
||||
.unwrap_or(id);
|
||||
let label = explicit_label.unwrap_or(cur_label);
|
||||
let tip_text = format!("{}\nCommand: {}", cur_label, last);
|
||||
let arr_tip = format!("{} options", label);
|
||||
let tip_text = format!("{}\n{} {}", t!(cur_label), t!("Command:"), last);
|
||||
let arr_tip = format!("{} {}", t!(label), t!("options"));
|
||||
|
||||
// Icon on top with the label beneath it, then the ▾ strip at the very bottom.
|
||||
let top_btn = button(
|
||||
column![
|
||||
make_icon_dim(cur_icon, LARGE_ICON, dim),
|
||||
text(label.to_string())
|
||||
text(t!(label))
|
||||
.size(10)
|
||||
.style(move |theme: &Theme| tool_label_style(theme, dim)),
|
||||
]
|
||||
|
|
@ -545,11 +546,11 @@ pub(super) fn render_large<'a>(
|
|||
let dim = start_dimmed(&state, &t.event);
|
||||
let event = t.event.clone();
|
||||
let tool_id = t.id.to_string();
|
||||
let tip_text = format!("{}\nCommand: {}", t.label, t.id);
|
||||
let tip_text = format!("{}\n{} {}", t!(t.label), t!("Command:"), t.id);
|
||||
let btn = button(
|
||||
column![
|
||||
make_icon_dim(t.icon, LARGE_ICON, dim),
|
||||
text(t.label)
|
||||
text(t!(t.label))
|
||||
.size(10)
|
||||
.style(move |theme: &Theme| tool_label_style(theme, dim)),
|
||||
]
|
||||
|
|
@ -685,7 +686,7 @@ pub(super) fn render_large<'a>(
|
|||
.map(|t| {
|
||||
let is_active = active_tool.as_deref() == Some(t.id);
|
||||
let dim = start_dimmed(&state, &t.event);
|
||||
let tip = t.label;
|
||||
let tip = t!(t.label);
|
||||
let event = t.event.clone();
|
||||
let icon_el: Element<Message> = if dim {
|
||||
make_icon_dim(t.icon, 16.0, true)
|
||||
|
|
@ -752,11 +753,16 @@ pub(super) fn render_large<'a>(
|
|||
let mp_dim = start_dimmed(&state, &match_prop.event);
|
||||
let mp_event = match_prop.event.clone();
|
||||
let mp_id = match_prop.id.to_string();
|
||||
let mp_tip = format!("{}\nCommand: {}", match_prop.label, match_prop.id);
|
||||
let mp_tip = format!(
|
||||
"{}\n{} {}",
|
||||
t!(match_prop.label),
|
||||
t!("Command:"),
|
||||
match_prop.id
|
||||
);
|
||||
let mp_btn = button(
|
||||
column![
|
||||
make_icon_dim(match_prop.icon, LARGE_ICON, mp_dim),
|
||||
text(match_prop.label)
|
||||
text(t!(match_prop.label))
|
||||
.size(10)
|
||||
.style(move |theme: &Theme| tool_label_style(theme, mp_dim)),
|
||||
]
|
||||
|
|
@ -906,7 +912,7 @@ pub(super) fn render_large<'a>(
|
|||
.map(|t| {
|
||||
let is_active = active_tool.as_deref() == Some(t.id);
|
||||
let dim = start_dimmed(&state, &t.event);
|
||||
let tip = t.label;
|
||||
let tip = t!(t.label);
|
||||
let event = t.event.clone();
|
||||
let icon_el: Element<Message> = if dim {
|
||||
make_icon_dim(t.icon, 16.0, true)
|
||||
|
|
@ -1006,7 +1012,7 @@ pub(super) fn quick_access_btn<'a>(
|
|||
.width(Length::Fixed(TOP_HIST_W))
|
||||
.height(24)
|
||||
.padding([2, 0]);
|
||||
tooltip(btn, make_tip(label.to_string()), TipPos::Bottom)
|
||||
tooltip(btn, make_tip(t!(label).into_owned()), TipPos::Bottom)
|
||||
.gap(6.0)
|
||||
.delay(Duration::from_millis(400))
|
||||
.style(tip_style)
|
||||
|
|
@ -1052,7 +1058,11 @@ pub(super) fn render_history_control<'a>(
|
|||
};
|
||||
tooltip(
|
||||
btn,
|
||||
make_tip(format!("{label}\n{count} steps available")),
|
||||
make_tip(format!(
|
||||
"{}\n{}",
|
||||
t!(label),
|
||||
t!("%{count} steps available", count = count)
|
||||
)),
|
||||
TipPos::Right,
|
||||
)
|
||||
.gap(6.0)
|
||||
|
|
@ -1085,7 +1095,10 @@ pub(super) fn render_history_control<'a>(
|
|||
};
|
||||
tooltip(
|
||||
btn,
|
||||
make_tip(format!("Choose {label} history")),
|
||||
make_tip(format!(
|
||||
"{}",
|
||||
t!("%{label} history", label = t!(label))
|
||||
)),
|
||||
TipPos::Right,
|
||||
)
|
||||
.gap(6.0)
|
||||
|
|
|
|||
|
|
@ -26,6 +26,7 @@ use crate::snap::Snapper;
|
|||
use crate::ui::statusbar::statusbar_config::{StatusBarConfig, StatusPill};
|
||||
use crate::ui::statusbar::status_menu::Entry as StatusMenuEntry;
|
||||
use crate::ui::wrap_bar::WrapBar;
|
||||
use crate::t;
|
||||
|
||||
const ST_ANNO_VISIBILITY: &[u8] = include_bytes!("../../../assets/icons/scale_list.svg");
|
||||
const ST_ANNO_AUTO_ADD: &[u8] = include_bytes!("../../../assets/icons/add_scale.svg");
|
||||
|
|
@ -142,7 +143,7 @@ impl StatusBar {
|
|||
let menu_btn = if is_start {
|
||||
tip(
|
||||
menu_button.into(),
|
||||
"Open or create a drawing to manage layouts.",
|
||||
t!("Open or create a drawing to manage layouts."),
|
||||
)
|
||||
} else {
|
||||
status_menu::menu_bar(
|
||||
|
|
@ -150,7 +151,7 @@ impl StatusBar {
|
|||
menu_button
|
||||
.on_press(Message::StatusMenuTooltipHidden(true))
|
||||
.into(),
|
||||
"Model and layout list",
|
||||
t!("Model and layout list"),
|
||||
tooltip_hidden,
|
||||
),
|
||||
statusbar_menu::layout_entries(&layout_names, ¤t_layout),
|
||||
|
|
@ -164,7 +165,7 @@ impl StatusBar {
|
|||
let add_btn = if is_start {
|
||||
tip(
|
||||
add_button.into(),
|
||||
"Open or create a drawing to add a layout.",
|
||||
t!("Open or create a drawing to add a layout."),
|
||||
)
|
||||
} else {
|
||||
add_button.on_press(Message::LayoutCreate).into()
|
||||
|
|
@ -174,7 +175,7 @@ impl StatusBar {
|
|||
let osnap_active = snapper.is_active();
|
||||
|
||||
let vp_label = if viewport_count > 0 {
|
||||
format!("{} VP", viewport_count)
|
||||
t!("%{n} VP", n = viewport_count).into_owned()
|
||||
} else {
|
||||
String::new()
|
||||
};
|
||||
|
|
@ -205,7 +206,7 @@ impl StatusBar {
|
|||
status_menu::menu_bar(
|
||||
menu_tip(
|
||||
popup_pill(&scale_label),
|
||||
"Annotation / Viewport Scale\nClick to change",
|
||||
t!("Annotation / Viewport Scale\nClick to change"),
|
||||
tooltip_hidden,
|
||||
),
|
||||
crate::ui::popup::scale_popup::menu_entries(
|
||||
|
|
@ -229,7 +230,7 @@ impl StatusBar {
|
|||
pills.push(
|
||||
tip(
|
||||
action_pill(&coords_label, Message::CycleCoordsMode),
|
||||
"Cursor coordinates ($COORDS)\nClick to cycle: static / live / polar",
|
||||
t!("Cursor coordinates ($COORDS)\nClick to cycle: static / live / polar"),
|
||||
)
|
||||
.into(),
|
||||
);
|
||||
|
|
@ -238,7 +239,7 @@ impl StatusBar {
|
|||
pills.push(
|
||||
tip(
|
||||
toggle_pill(crate::ui::icons::ST_ORTHO, ortho_mode, Message::ToggleOrtho),
|
||||
"Orthogonal Mode\nF8",
|
||||
t!("Orthogonal Mode\nF8"),
|
||||
)
|
||||
.into(),
|
||||
);
|
||||
|
|
@ -247,7 +248,7 @@ impl StatusBar {
|
|||
pills.push(
|
||||
tip(
|
||||
toggle_pill(crate::ui::icons::ST_LWT, lineweight_display, Message::ToggleLineweightDisplay),
|
||||
"Show Lineweight\nLWDISPLAY",
|
||||
t!("Show Lineweight\nLWDISPLAY"),
|
||||
)
|
||||
.into(),
|
||||
);
|
||||
|
|
@ -270,7 +271,7 @@ impl StatusBar {
|
|||
pills.push(
|
||||
tip(
|
||||
toggle_pill(crate::ui::icons::ST_DYN, dyn_input, Message::ToggleDynInput),
|
||||
"Dynamic Input\nF12",
|
||||
t!("Dynamic Input\nF12"),
|
||||
)
|
||||
.into(),
|
||||
);
|
||||
|
|
@ -279,7 +280,7 @@ impl StatusBar {
|
|||
pills.push(
|
||||
tip(
|
||||
toggle_pill(crate::ui::icons::ST_OTRACK, otrack, Message::ToggleOTrack),
|
||||
"Object Snap Tracking\nF11",
|
||||
t!("Object Snap Tracking\nF11"),
|
||||
)
|
||||
.into(),
|
||||
);
|
||||
|
|
@ -299,7 +300,7 @@ impl StatusBar {
|
|||
pills.push(
|
||||
tip(
|
||||
space_mode_btn(¤t_layout, in_mspace),
|
||||
"PAPER: double-click viewport to enter MSPACE\nMODEL: click to switch to Model Space",
|
||||
t!("PAPER: double-click viewport to enter MSPACE\nMODEL: click to switch to Model Space"),
|
||||
)
|
||||
.into(),
|
||||
);
|
||||
|
|
@ -315,7 +316,7 @@ impl StatusBar {
|
|||
annotation_all_visible,
|
||||
Message::ToggleAnnotationVisibility,
|
||||
),
|
||||
"Show Annotation Objects",
|
||||
t!("Show Annotation Objects"),
|
||||
)
|
||||
.into(),
|
||||
);
|
||||
|
|
@ -328,7 +329,7 @@ impl StatusBar {
|
|||
annotation_auto_add,
|
||||
Message::ToggleAnnotationAutoAdd,
|
||||
),
|
||||
"Automatically Add Scales",
|
||||
t!("Automatically Add Scales"),
|
||||
)
|
||||
.into(),
|
||||
);
|
||||
|
|
@ -342,7 +343,7 @@ impl StatusBar {
|
|||
synced,
|
||||
Message::SyncViewportAnnotationScale,
|
||||
),
|
||||
"Viewport / Annotation Scale Sync",
|
||||
t!("Viewport / Annotation Scale Sync"),
|
||||
)
|
||||
.into(),
|
||||
);
|
||||
|
|
@ -352,8 +353,8 @@ impl StatusBar {
|
|||
pills.push(
|
||||
status_menu::menu_bar(
|
||||
menu_tip(
|
||||
popup_pill(crate::ui::popup::units_popup::unit_short(insertion_units)),
|
||||
"Drawing Units (INSUNITS)\nClick to change",
|
||||
popup_pill(t!(crate::ui::popup::units_popup::unit_short(insertion_units))),
|
||||
t!("Drawing Units (INSUNITS)\nClick to change"),
|
||||
tooltip_hidden,
|
||||
),
|
||||
crate::ui::popup::units_popup::menu_entries(insertion_units),
|
||||
|
|
@ -370,7 +371,7 @@ impl StatusBar {
|
|||
transparency_display,
|
||||
Message::ToggleTransparencyDisplay,
|
||||
),
|
||||
"Show Transparency\nForce opaque when off",
|
||||
t!("Show Transparency\nForce opaque when off"),
|
||||
)
|
||||
.into(),
|
||||
);
|
||||
|
|
@ -384,7 +385,7 @@ impl StatusBar {
|
|||
isolation_active,
|
||||
Message::StatusMenuTooltipHidden(true),
|
||||
),
|
||||
"Isolate Objects\nClick for Isolate / Hide / End",
|
||||
t!("Isolate Objects\nClick for Isolate / Hide / End"),
|
||||
tooltip_hidden,
|
||||
),
|
||||
crate::ui::popup::isolate_popup::menu_entries(
|
||||
|
|
@ -400,7 +401,7 @@ impl StatusBar {
|
|||
pills.push(
|
||||
tip(
|
||||
toggle_pill(crate::ui::icons::ST_QUICKPROPS, quick_properties, Message::ToggleQuickProperties),
|
||||
"Quick Properties\nFloating panel on selection",
|
||||
t!("Quick Properties\nFloating panel on selection"),
|
||||
)
|
||||
.into(),
|
||||
);
|
||||
|
|
@ -414,7 +415,7 @@ impl StatusBar {
|
|||
selection_filter_active,
|
||||
Message::StatusMenuTooltipHidden(true),
|
||||
),
|
||||
"Selection Filtering\nLimit which object types can be picked",
|
||||
t!("Selection Filtering\nLimit which object types can be picked"),
|
||||
tooltip_hidden,
|
||||
),
|
||||
crate::ui::popup::selection_filter_popup::menu_entries(
|
||||
|
|
@ -430,7 +431,7 @@ impl StatusBar {
|
|||
pills.push(
|
||||
tip(
|
||||
toggle_pill(crate::ui::icons::ST_SELCYCLE, selection_cycling, Message::ToggleSelectionCycling),
|
||||
"Selection Cycling\nRepeat-click to step through overlapping objects",
|
||||
t!("Selection Cycling\nRepeat-click to step through overlapping objects"),
|
||||
)
|
||||
.into(),
|
||||
);
|
||||
|
|
@ -439,7 +440,7 @@ impl StatusBar {
|
|||
pills.push(
|
||||
tip(
|
||||
status_pill(vp_label).into(),
|
||||
"Viewport count in active layout",
|
||||
t!("Viewport count in active layout"),
|
||||
)
|
||||
.into(),
|
||||
);
|
||||
|
|
@ -448,7 +449,7 @@ impl StatusBar {
|
|||
pills.push(
|
||||
tip(
|
||||
toggle_pill(crate::ui::icons::ST_CLEANSCREEN, clean_screen, Message::ToggleCleanScreen),
|
||||
"Clean Screen\nHide ribbon and panels",
|
||||
t!("Clean Screen\nHide ribbon and panels"),
|
||||
)
|
||||
.into(),
|
||||
);
|
||||
|
|
@ -458,7 +459,7 @@ impl StatusBar {
|
|||
status_menu::menu_bar(
|
||||
menu_tip(
|
||||
customize_btn(),
|
||||
"Customization\nShow or hide status-bar items",
|
||||
t!("Customization\nShow or hide status-bar items"),
|
||||
tooltip_hidden,
|
||||
),
|
||||
statusbar_menu::customization_entries(config),
|
||||
|
|
@ -589,7 +590,10 @@ fn customize_btn() -> Element<'static, Message> {
|
|||
|
||||
// ── Tooltip helper ────────────────────────────────────────────────────────
|
||||
|
||||
fn tip<'a>(content: Element<'a, Message>, label: &'static str) -> Element<'a, Message> {
|
||||
fn tip<'a>(
|
||||
content: Element<'a, Message>,
|
||||
label: std::borrow::Cow<'static, str>,
|
||||
) -> Element<'a, Message> {
|
||||
tip_node(content, text(label).size(11).into())
|
||||
}
|
||||
|
||||
|
|
@ -597,7 +601,7 @@ fn tip<'a>(content: Element<'a, Message>, label: &'static str) -> Element<'a, Me
|
|||
/// opened menu resets suppression for the next hover without covering the menu.
|
||||
fn menu_tip<'a>(
|
||||
content: Element<'a, Message>,
|
||||
label: &'static str,
|
||||
label: std::borrow::Cow<'static, str>,
|
||||
hidden: bool,
|
||||
) -> Element<'a, Message> {
|
||||
let content = if hidden {
|
||||
|
|
@ -707,8 +711,9 @@ fn polar_pill<'a>(
|
|||
entries: Vec<StatusMenuEntry<'a>>,
|
||||
) -> Element<'a, Message> {
|
||||
let angle = crate::ui::popup::polar_popup::angle_label(increment_deg);
|
||||
let tooltip_text = format!(
|
||||
"Polar Tracking ({angle})\nF10 — left-click on/off\nRight-click cycles · ▾ picks angle",
|
||||
let tooltip_text = t!(
|
||||
"Polar Tracking (%{angle})\nF10 — left-click on/off\nRight-click cycles · ▾ picks angle",
|
||||
angle = angle,
|
||||
);
|
||||
|
||||
// Right-click quick-cycles through the same increments the picker lists.
|
||||
|
|
@ -762,7 +767,7 @@ fn polar_pill<'a>(
|
|||
)
|
||||
.on_press(Message::StatusMenuTooltipHidden(true))
|
||||
.into(),
|
||||
"Polar angle\nClick to choose",
|
||||
t!("Polar angle\nClick to choose"),
|
||||
tooltip_hidden,
|
||||
),
|
||||
entries,
|
||||
|
|
@ -792,7 +797,7 @@ fn osnap_btn<'a>(
|
|||
mouse_area(snap_icon)
|
||||
.on_press(Message::ToggleSnapEnabled)
|
||||
.into(),
|
||||
"Object Snap: toggle on/off\nF3",
|
||||
t!("Object Snap: toggle on/off\nF3"),
|
||||
);
|
||||
|
||||
let caret = status_menu::menu_bar(
|
||||
|
|
@ -807,7 +812,7 @@ fn osnap_btn<'a>(
|
|||
)
|
||||
.on_press(Message::StatusMenuTooltipHidden(true))
|
||||
.into(),
|
||||
"Object Snap list\nClick to choose snap types",
|
||||
t!("Object Snap list\nClick to choose snap types"),
|
||||
tooltip_hidden,
|
||||
),
|
||||
entries,
|
||||
|
|
@ -820,7 +825,7 @@ fn osnap_btn<'a>(
|
|||
// ── Helpers ───────────────────────────────────────────────────────────────
|
||||
|
||||
fn layout_tab_context_menu(name: String) -> Element<'static, Message> {
|
||||
let item = |label: &'static str, msg: Message| {
|
||||
let item = |label: std::borrow::Cow<'static, str>, msg: Message| {
|
||||
button(text(label).size(12))
|
||||
.on_press(msg)
|
||||
.style(button::subtle)
|
||||
|
|
@ -830,8 +835,8 @@ fn layout_tab_context_menu(name: String) -> Element<'static, Message> {
|
|||
|
||||
container(
|
||||
column![
|
||||
item("Rename", Message::LayoutRenameStart(name.clone())),
|
||||
item("Delete", Message::LayoutDelete(name)),
|
||||
item(t!("Rename"), Message::LayoutRenameStart(name.clone())),
|
||||
item(t!("Delete"), Message::LayoutDelete(name)),
|
||||
]
|
||||
.spacing(0)
|
||||
.width(160),
|
||||
|
|
@ -888,7 +893,7 @@ fn space_tab<'a>(
|
|||
format!("{report_key_prefix}:{label}"),
|
||||
tip(
|
||||
display.into(),
|
||||
"Open or create a drawing to switch layouts.",
|
||||
t!("Open or create a drawing to switch layouts."),
|
||||
),
|
||||
)
|
||||
.into()
|
||||
|
|
@ -960,11 +965,11 @@ fn space_mode_btn(current_layout: &str, in_mspace: bool) -> Element<'static, Mes
|
|||
// PAPER = currently in paper-space editing
|
||||
// MODEL = currently in model-space editing (either the Model tab or MSPACE)
|
||||
let (label, active, on_press) = if is_model_tab {
|
||||
("MODEL", false, None::<Message>)
|
||||
(t!("MODEL"), false, None::<Message>)
|
||||
} else if in_mspace {
|
||||
("MODEL", true, Some(Message::ExitViewport))
|
||||
(t!("MODEL"), true, Some(Message::ExitViewport))
|
||||
} else {
|
||||
("PAPER", false, Some(Message::MspaceCommand))
|
||||
(t!("PAPER"), false, Some(Message::MspaceCommand))
|
||||
};
|
||||
|
||||
let clickable = on_press.is_some();
|
||||
|
|
@ -1002,12 +1007,12 @@ fn status_pill(label: impl Into<String>) -> Element<'static, Message> {
|
|||
// ── Scale popup button ────────────────────────────────────────────────────
|
||||
|
||||
/// Shared visual root for labelled status-bar menus (units, scale, …).
|
||||
fn popup_pill(label: &str) -> Element<'static, Message> {
|
||||
fn popup_pill(label: impl Into<String>) -> Element<'static, Message> {
|
||||
action_pill(label, Message::StatusMenuTooltipHidden(true))
|
||||
}
|
||||
|
||||
fn action_pill(label: &str, msg: Message) -> Element<'static, Message> {
|
||||
let label = label.to_string();
|
||||
fn action_pill(label: impl Into<String>, msg: Message) -> Element<'static, Message> {
|
||||
let label = label.into();
|
||||
button(text(label).size(12))
|
||||
.on_press(msg)
|
||||
.style(button::subtle)
|
||||
|
|
|
|||
|
|
@ -6,13 +6,14 @@ use iced::{Background, Element, Fill, Theme};
|
|||
use crate::app::Message;
|
||||
use crate::ui::statusbar::statusbar_config::{StatusBarConfig, StatusPill};
|
||||
use crate::ui::statusbar::status_menu::Entry;
|
||||
use crate::t;
|
||||
|
||||
pub fn customization_entries(config: &StatusBarConfig) -> Vec<Entry<'static>> {
|
||||
StatusPill::ALL
|
||||
.iter()
|
||||
.map(|&pill| {
|
||||
Entry::stay(menu_row(
|
||||
pill.label(),
|
||||
t!(pill.label()),
|
||||
config.is_visible(pill),
|
||||
Message::ToggleStatusPill(pill),
|
||||
))
|
||||
|
|
@ -48,7 +49,7 @@ fn layout_row<'a>(name: String, is_current: bool) -> Element<'a, Message> {
|
|||
.into()
|
||||
}
|
||||
|
||||
fn menu_row(label: &'static str, checked: bool, msg: Message) -> Element<'static, Message> {
|
||||
fn menu_row(label: std::borrow::Cow<'static, str>, checked: bool, msg: Message) -> Element<'static, Message> {
|
||||
let check = crate::ui::icons::themed_check_cell(checked);
|
||||
|
||||
let lbl = text(label).size(11);
|
||||
|
|
|
|||
|
|
@ -10,6 +10,7 @@ use crate::app::Message;
|
|||
use crate::ui::style::style_manager::{hdivider, muted_text_style, tb_button};
|
||||
use iced::widget::{column, container, mouse_area, row, scrollable, text, Space};
|
||||
use iced::{Background, Border, Element, Theme};
|
||||
use crate::t;
|
||||
|
||||
/// `scales` is `(name, "paper:drawing" ratio, is_member)`. Every label is cloned
|
||||
/// into the widget tree, so the returned element borrows nothing from the args.
|
||||
|
|
@ -20,9 +21,9 @@ pub fn view_window(
|
|||
) -> Element<'static, Message> {
|
||||
let toolbar = container(
|
||||
row![
|
||||
text(format!("Object: {object_label}")).size(11),
|
||||
text(t!("Object: %{object_label}", object_label = object_label)).size(11),
|
||||
Space::new().width(sizing.width),
|
||||
tb_button("Close", Message::CloseModal, true),
|
||||
tb_button(t!("Close"), Message::CloseModal, true),
|
||||
]
|
||||
.spacing(4)
|
||||
.align_y(iced::Center),
|
||||
|
|
@ -75,7 +76,7 @@ pub fn view_window(
|
|||
|
||||
let body = container(
|
||||
column![
|
||||
text("Click a scale to add or remove the object's representation for it.")
|
||||
text(t!("Click a scale to add or remove the object's representation for it."))
|
||||
.size(10)
|
||||
.style(muted_text_style),
|
||||
list,
|
||||
|
|
|
|||
|
|
@ -5,6 +5,21 @@ use iced::widget::{
|
|||
button, checkbox, column, container, row, scrollable, text, text_input, Space,
|
||||
};
|
||||
use iced::{Background, Border, Element, Theme};
|
||||
use crate::t;
|
||||
use std::borrow::Cow;
|
||||
use std::fmt;
|
||||
|
||||
#[derive(Clone, Debug, PartialEq, Eq)]
|
||||
struct DimEnumChoice {
|
||||
code: String,
|
||||
label: String,
|
||||
}
|
||||
|
||||
impl fmt::Display for DimEnumChoice {
|
||||
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
|
||||
f.write_str(&self.label)
|
||||
}
|
||||
}
|
||||
|
||||
/// All DimStyle field values needed by the view.
|
||||
pub struct DimStyleValues<'a> {
|
||||
|
|
@ -173,34 +188,34 @@ pub fn view_window<'a>(
|
|||
) -> Element<'a, Message> {
|
||||
// ── Tab bar ───────────────────────────────────────────────────────────
|
||||
let tabs = row![
|
||||
button(text("Lines").size(11))
|
||||
button(text(t!("Lines")).size(11))
|
||||
.on_press(Message::DimStyleDialogTab(0))
|
||||
.style(tab_btn_style(tab == 0))
|
||||
.padding([4, 10]),
|
||||
button(text("Arrows").size(11))
|
||||
button(text(t!("Arrows")).size(11))
|
||||
.on_press(Message::DimStyleDialogTab(1))
|
||||
.style(tab_btn_style(tab == 1))
|
||||
.padding([4, 10]),
|
||||
button(text("Text").size(11))
|
||||
button(text(t!("Text")).size(11))
|
||||
.on_press(Message::DimStyleDialogTab(2))
|
||||
.style(tab_btn_style(tab == 2))
|
||||
.padding([4, 10]),
|
||||
button(text("Scale/Units").size(11))
|
||||
button(text(t!("Scale/Units")).size(11))
|
||||
.on_press(Message::DimStyleDialogTab(3))
|
||||
.style(tab_btn_style(tab == 3))
|
||||
.padding([4, 10]),
|
||||
button(text("Tolerances").size(11))
|
||||
button(text(t!("Tolerances")).size(11))
|
||||
.on_press(Message::DimStyleDialogTab(4))
|
||||
.style(tab_btn_style(tab == 4))
|
||||
.padding([4, 10]),
|
||||
button(text("Alternate").size(11))
|
||||
button(text(t!("Alternate")).size(11))
|
||||
.on_press(Message::DimStyleDialogTab(5))
|
||||
.style(tab_btn_style(tab == 5))
|
||||
.padding([4, 10]),
|
||||
]
|
||||
.spacing(2);
|
||||
|
||||
let lbl = |s: &'static str| text(s).size(11).style(muted_style).width(180);
|
||||
let lbl = |s: Cow<'static, str>| text(s).size(11).style(muted_style).width(180);
|
||||
|
||||
let mk_field = |fld: DsField, val: &'a str| -> Element<'a, Message> {
|
||||
text_input("", val)
|
||||
|
|
@ -211,7 +226,7 @@ pub fn view_window<'a>(
|
|||
.into()
|
||||
};
|
||||
|
||||
let chk = |label: &'static str, val: bool, fld: DsField| -> Element<'a, Message> {
|
||||
let chk = |label: Cow<'static, str>, val: bool, fld: DsField| -> Element<'a, Message> {
|
||||
checkbox(val)
|
||||
.label(label)
|
||||
.on_toggle(move |_| Message::DsToggle(fld.clone()))
|
||||
|
|
@ -222,27 +237,31 @@ pub fn view_window<'a>(
|
|||
|
||||
// Enum dropdown: maps the stored integer code to a named option and back,
|
||||
// so the user picks "Above" rather than typing "1".
|
||||
let enum_field = move |label: &'static str,
|
||||
let enum_field = move |label: Cow<'static, str>,
|
||||
fld: DsField,
|
||||
val: &'a str,
|
||||
opts: &'static [(&'static str, &'static str)]|
|
||||
-> Element<'a, Message> {
|
||||
let labels: Vec<String> = opts.iter().map(|(_, l)| (*l).to_string()).collect();
|
||||
let cur = opts
|
||||
let options: Vec<DimEnumChoice> = opts
|
||||
.iter()
|
||||
.find(|(c, _)| *c == val.trim())
|
||||
.map(|(_, l)| (*l).to_string())
|
||||
.unwrap_or_else(|| val.to_string());
|
||||
.map(|(code, label)| DimEnumChoice {
|
||||
code: (*code).to_string(),
|
||||
label: crate::i18n::translate(label).into_owned(),
|
||||
})
|
||||
.collect();
|
||||
let cur = options
|
||||
.iter()
|
||||
.find(|choice| choice.code == val.trim())
|
||||
.cloned()
|
||||
.unwrap_or_else(|| DimEnumChoice {
|
||||
code: val.to_string(),
|
||||
label: val.to_string(),
|
||||
});
|
||||
row![
|
||||
lbl(label),
|
||||
iced::widget::pick_list(Some(cur), labels, |value| value.to_string())
|
||||
.on_select(move |chosen| {
|
||||
let code = opts
|
||||
.iter()
|
||||
.find(|(_, l)| *l == chosen.as_str())
|
||||
.map(|(c, _)| (*c).to_string())
|
||||
.unwrap_or(chosen);
|
||||
Message::DsEdit(fld.clone(), code)
|
||||
iced::widget::pick_list(Some(cur), options, |value| value.to_string())
|
||||
.on_select(move |chosen: DimEnumChoice| {
|
||||
Message::DsEdit(fld.clone(), chosen.code)
|
||||
})
|
||||
.text_size(11)
|
||||
.width(150),
|
||||
|
|
@ -275,7 +294,7 @@ pub fn view_window<'a>(
|
|||
// Shared colour selector (main dropdown + "more" palette), reusing the
|
||||
// existing DsEdit path (the chosen colour is sent as an ACI string).
|
||||
let color_open = vals.color_open.clone();
|
||||
let color_row = move |label: &'static str, fld: DsField, _val: &'a str| -> Element<'a, Message> {
|
||||
let color_row = move |label: Cow<'static, str>, fld: DsField, _val: &'a str| -> Element<'a, Message> {
|
||||
let cur = crate::ui::color_select::aci_string_to_color(_val);
|
||||
let open = color_open.as_ref() == Some(&fld);
|
||||
let f_sel = fld.clone();
|
||||
|
|
@ -301,7 +320,7 @@ pub fn view_window<'a>(
|
|||
|
||||
// Block / linetype Handle dropdown: pick a block-record (arrowheads) or a
|
||||
// linetype by name from the available records.
|
||||
let hrow = move |label: &'static str,
|
||||
let hrow = move |label: Cow<'static, str>,
|
||||
options: Vec<String>,
|
||||
selected: String,
|
||||
field: &'static str|
|
||||
|
|
@ -320,81 +339,81 @@ pub fn view_window<'a>(
|
|||
|
||||
let tab_content: Element<'_, Message> = match tab {
|
||||
0 => column![
|
||||
text("Dimension Line").size(11).style(primary_style),
|
||||
text(t!("Dimension Line")).size(11).style(primary_style),
|
||||
row![
|
||||
lbl("Extension (DIMDLE)"),
|
||||
lbl(t!("Extension (DIMDLE)")),
|
||||
mk_field(DsField::Dimdle, vals.dimdle)
|
||||
]
|
||||
.spacing(8)
|
||||
.align_y(iced::Center),
|
||||
row![
|
||||
lbl("Spacing (DIMDLI)"),
|
||||
lbl(t!("Spacing (DIMDLI)")),
|
||||
mk_field(DsField::Dimdli, vals.dimdli)
|
||||
]
|
||||
.spacing(8)
|
||||
.align_y(iced::Center),
|
||||
row![
|
||||
lbl("Text gap (DIMGAP)"),
|
||||
lbl(t!("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).style(primary_style),
|
||||
chk(t!("Suppress 1st line (DIMSD1)"), vals.dimsd1, DsField::Dimsd1),
|
||||
chk(t!("Suppress 2nd line (DIMSD2)"), vals.dimsd2, DsField::Dimsd2),
|
||||
text(t!("Extension Line")).size(11).style(primary_style),
|
||||
row![
|
||||
lbl("Extension (DIMEXE)"),
|
||||
lbl(t!("Extension (DIMEXE)")),
|
||||
mk_field(DsField::Dimexe, vals.dimexe)
|
||||
]
|
||||
.spacing(8)
|
||||
.align_y(iced::Center),
|
||||
row![
|
||||
lbl("Offset (DIMEXO)"),
|
||||
lbl(t!("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),
|
||||
color_row("Dim line color ACI (DIMCLRD)", DsField::Dimclrd, vals.dimclrd),
|
||||
chk(t!("Suppress 1st line (DIMSE1)"), vals.dimse1, DsField::Dimse1),
|
||||
chk(t!("Suppress 2nd line (DIMSE2)"), vals.dimse2, DsField::Dimse2),
|
||||
color_row(t!("Dim line color ACI (DIMCLRD)"), DsField::Dimclrd, vals.dimclrd),
|
||||
row![
|
||||
lbl("Dim line weight (DIMLWD)"),
|
||||
lbl(t!("Dim line weight (DIMLWD)")),
|
||||
mk_field(DsField::Dimlwd, vals.dimlwd)
|
||||
]
|
||||
.spacing(8)
|
||||
.align_y(iced::Center),
|
||||
color_row("Ext line color ACI (DIMCLRE)", DsField::Dimclre, vals.dimclre),
|
||||
color_row(t!("Ext line color ACI (DIMCLRE)"), DsField::Dimclre, vals.dimclre),
|
||||
row![
|
||||
lbl("Ext line weight (DIMLWE)"),
|
||||
lbl(t!("Ext line weight (DIMLWE)")),
|
||||
mk_field(DsField::Dimlwe, vals.dimlwe)
|
||||
]
|
||||
.spacing(8)
|
||||
.align_y(iced::Center),
|
||||
chk(
|
||||
"Fixed-length ext lines (DIMFXLON)",
|
||||
t!("Fixed-length ext lines (DIMFXLON)"),
|
||||
vals.dimfxlon,
|
||||
DsField::Dimfxlon
|
||||
),
|
||||
row![
|
||||
lbl("Fixed length (DIMFXL)"),
|
||||
lbl(t!("Fixed length (DIMFXL)")),
|
||||
mk_field(DsField::Dimfxl, vals.dimfxl)
|
||||
]
|
||||
.spacing(8)
|
||||
.align_y(iced::Center),
|
||||
hrow(
|
||||
"Dim line linetype (DIMLTYPE)",
|
||||
t!("Dim line linetype (DIMLTYPE)"),
|
||||
vals.lt_opts.clone(),
|
||||
vals.dimltex_name.clone(),
|
||||
"dimltex_handle"
|
||||
),
|
||||
hrow(
|
||||
"Ext line 1 linetype (DIMLTEX1)",
|
||||
t!("Ext line 1 linetype (DIMLTEX1)"),
|
||||
vals.lt_opts.clone(),
|
||||
vals.dimltex1_name.clone(),
|
||||
"dimltex1_handle"
|
||||
),
|
||||
hrow(
|
||||
"Ext line 2 linetype (DIMLTEX2)",
|
||||
t!("Ext line 2 linetype (DIMLTEX2)"),
|
||||
vals.lt_opts.clone(),
|
||||
vals.dimltex2_name.clone(),
|
||||
"dimltex2_handle"
|
||||
|
|
@ -403,62 +422,62 @@ pub fn view_window<'a>(
|
|||
.spacing(7)
|
||||
.into(),
|
||||
1 => column![
|
||||
text("Arrows").size(11).style(primary_style),
|
||||
text(t!("Arrows")).size(11).style(primary_style),
|
||||
hrow(
|
||||
"Arrowhead (DIMBLK)",
|
||||
t!("Arrowhead (DIMBLK)"),
|
||||
vals.block_opts.clone(),
|
||||
vals.dimblk_name.clone(),
|
||||
"dimblk"
|
||||
),
|
||||
hrow(
|
||||
"1st arrowhead (DIMBLK1)",
|
||||
t!("1st arrowhead (DIMBLK1)"),
|
||||
vals.block_opts.clone(),
|
||||
vals.dimblk1_name.clone(),
|
||||
"dimblk1"
|
||||
),
|
||||
hrow(
|
||||
"2nd arrowhead (DIMBLK2)",
|
||||
t!("2nd arrowhead (DIMBLK2)"),
|
||||
vals.block_opts.clone(),
|
||||
vals.dimblk2_name.clone(),
|
||||
"dimblk2"
|
||||
),
|
||||
hrow(
|
||||
"Leader arrowhead (DIMLDRBLK)",
|
||||
t!("Leader arrowhead (DIMLDRBLK)"),
|
||||
vals.block_opts.clone(),
|
||||
vals.dimldrblk_name.clone(),
|
||||
"dimldrblk"
|
||||
),
|
||||
row![
|
||||
lbl("Arrow size (DIMASZ)"),
|
||||
lbl(t!("Arrow size (DIMASZ)")),
|
||||
mk_field(DsField::Dimasz, vals.dimasz)
|
||||
]
|
||||
.spacing(8)
|
||||
.align_y(iced::Center),
|
||||
row![
|
||||
lbl("Center mark (DIMCEN)"),
|
||||
lbl(t!("Center mark (DIMCEN)")),
|
||||
mk_field(DsField::Dimcen, vals.dimcen)
|
||||
]
|
||||
.spacing(8)
|
||||
.align_y(iced::Center),
|
||||
row![
|
||||
lbl("Tick size (DIMTSZ)"),
|
||||
lbl(t!("Tick size (DIMTSZ)")),
|
||||
mk_field(DsField::Dimtsz, vals.dimtsz)
|
||||
]
|
||||
.spacing(8)
|
||||
.align_y(iced::Center),
|
||||
chk(
|
||||
"Separate arrow blocks (DIMSAH)",
|
||||
t!("Separate arrow blocks (DIMSAH)"),
|
||||
vals.dimsah,
|
||||
DsField::Dimsah
|
||||
),
|
||||
enum_field(
|
||||
"Arc length symbol (DIMARCSYM)",
|
||||
t!("Arc length symbol (DIMARCSYM)"),
|
||||
DsField::Dimarcsym,
|
||||
vals.dimarcsym,
|
||||
&[("0", "Before text"), ("1", "Above text"), ("2", "None")],
|
||||
),
|
||||
row![
|
||||
lbl("Jog angle ° (DIMJOGANG)"),
|
||||
lbl(t!("Jog angle ° (DIMJOGANG)")),
|
||||
mk_field(DsField::Dimjogang, vals.dimjogang)
|
||||
]
|
||||
.spacing(8)
|
||||
|
|
@ -467,21 +486,21 @@ pub fn view_window<'a>(
|
|||
.spacing(7)
|
||||
.into(),
|
||||
2 => column![
|
||||
text("Text").size(11).style(primary_style),
|
||||
text(t!("Text")).size(11).style(primary_style),
|
||||
row![
|
||||
lbl("Height (DIMTXT)"),
|
||||
lbl(t!("Height (DIMTXT)")),
|
||||
mk_field(DsField::Dimtxt, vals.dimtxt)
|
||||
]
|
||||
.spacing(8)
|
||||
.align_y(iced::Center),
|
||||
row![
|
||||
lbl("Style (DIMTXSTY)"),
|
||||
lbl(t!("Style (DIMTXSTY)")),
|
||||
mk_field(DsField::Dimtxsty, vals.dimtxsty)
|
||||
]
|
||||
.spacing(8)
|
||||
.align_y(iced::Center),
|
||||
enum_field(
|
||||
"Vertical pos (DIMTAD)",
|
||||
t!("Vertical pos (DIMTAD)"),
|
||||
DsField::Dimtad,
|
||||
vals.dimtad,
|
||||
&[
|
||||
|
|
@ -492,11 +511,11 @@ pub fn view_window<'a>(
|
|||
("4", "Below"),
|
||||
],
|
||||
),
|
||||
chk("Horizontal inside (DIMTIH)", vals.dimtih, DsField::Dimtih),
|
||||
chk("Horizontal outside (DIMTOH)", vals.dimtoh, DsField::Dimtoh),
|
||||
color_row("Text color ACI (DIMCLRT)", DsField::Dimclrt, vals.dimclrt),
|
||||
chk(t!("Horizontal inside (DIMTIH)"), vals.dimtih, DsField::Dimtih),
|
||||
chk(t!("Horizontal outside (DIMTOH)"), vals.dimtoh, DsField::Dimtoh),
|
||||
color_row(t!("Text color ACI (DIMCLRT)"), DsField::Dimclrt, vals.dimclrt),
|
||||
enum_field(
|
||||
"Horizontal just (DIMJUST)",
|
||||
t!("Horizontal just (DIMJUST)"),
|
||||
DsField::Dimjust,
|
||||
vals.dimjust,
|
||||
&[
|
||||
|
|
@ -508,13 +527,13 @@ pub fn view_window<'a>(
|
|||
],
|
||||
),
|
||||
row![
|
||||
lbl("Vertical pos (DIMTVP)"),
|
||||
lbl(t!("Vertical pos (DIMTVP)")),
|
||||
mk_field(DsField::Dimtvp, vals.dimtvp)
|
||||
]
|
||||
.spacing(8)
|
||||
.align_y(iced::Center),
|
||||
enum_field(
|
||||
"Text fill mode (DIMTFILL)",
|
||||
t!("Text fill mode (DIMTFILL)"),
|
||||
DsField::Dimtfill,
|
||||
vals.dimtfill,
|
||||
&[
|
||||
|
|
@ -523,9 +542,9 @@ pub fn view_window<'a>(
|
|||
("2", "Color"),
|
||||
],
|
||||
),
|
||||
color_row("Fill color ACI (DIMTFILLCLR)", DsField::Dimtfillclr, vals.dimtfillclr),
|
||||
color_row(t!("Fill color ACI (DIMTFILLCLR)"), DsField::Dimtfillclr, vals.dimtfillclr),
|
||||
chk(
|
||||
"Left-to-right (DIMTXTDIRECTION)",
|
||||
t!("Left-to-right (DIMTXTDIRECTION)"),
|
||||
vals.dimtxtdirection,
|
||||
DsField::Dimtxtdirection
|
||||
),
|
||||
|
|
@ -533,10 +552,10 @@ pub fn view_window<'a>(
|
|||
.spacing(7)
|
||||
.into(),
|
||||
3 => column![
|
||||
text("Scale").size(11).style(primary_style),
|
||||
chk("Annotative", vals.annotative, DsField::Annotative),
|
||||
text(t!("Scale")).size(11).style(primary_style),
|
||||
chk(t!("Annotative"), vals.annotative, DsField::Annotative),
|
||||
row![
|
||||
lbl("Overall scale (DIMSCALE)"),
|
||||
lbl(t!("Overall scale (DIMSCALE)")),
|
||||
if vals.annotative {
|
||||
text_input("", "0")
|
||||
.style(field_style)
|
||||
|
|
@ -550,40 +569,40 @@ pub fn view_window<'a>(
|
|||
.spacing(8)
|
||||
.align_y(iced::Center),
|
||||
row![
|
||||
lbl("Linear factor (DIMLFAC)"),
|
||||
lbl(t!("Linear factor (DIMLFAC)")),
|
||||
mk_field(DsField::Dimlfac, vals.dimlfac)
|
||||
]
|
||||
.spacing(8)
|
||||
.align_y(iced::Center),
|
||||
text("Units").size(11).style(primary_style),
|
||||
enum_field("Format (DIMLUNIT)", DsField::Dimlunit, vals.dimlunit, OPT_LUNIT),
|
||||
text(t!("Units")).size(11).style(primary_style),
|
||||
enum_field(t!("Format (DIMLUNIT)"), DsField::Dimlunit, vals.dimlunit, OPT_LUNIT),
|
||||
row![
|
||||
lbl("Decimals (DIMDEC)"),
|
||||
lbl(t!("Decimals (DIMDEC)")),
|
||||
mk_field(DsField::Dimdec, vals.dimdec)
|
||||
]
|
||||
.spacing(8)
|
||||
.align_y(iced::Center),
|
||||
row![
|
||||
lbl("Suffix (DIMPOST)"),
|
||||
lbl(t!("Suffix (DIMPOST)")),
|
||||
mk_field(DsField::Dimpost, vals.dimpost)
|
||||
]
|
||||
.spacing(8)
|
||||
.align_y(iced::Center),
|
||||
row![
|
||||
lbl("Decimal sep ASCII (DIMDSEP)"),
|
||||
lbl(t!("Decimal sep ASCII (DIMDSEP)")),
|
||||
mk_field(DsField::Dimdsep, vals.dimdsep)
|
||||
]
|
||||
.spacing(8)
|
||||
.align_y(iced::Center),
|
||||
row![
|
||||
lbl("Round off (DIMRND)"),
|
||||
lbl(t!("Round off (DIMRND)")),
|
||||
mk_field(DsField::Dimrnd, vals.dimrnd)
|
||||
]
|
||||
.spacing(8)
|
||||
.align_y(iced::Center),
|
||||
enum_field("Zero suppress (DIMZIN)", DsField::Dimzin, vals.dimzin, OPT_ZIN),
|
||||
enum_field(t!("Zero suppress (DIMZIN)"), DsField::Dimzin, vals.dimzin, OPT_ZIN),
|
||||
enum_field(
|
||||
"Fraction format (DIMFRAC)",
|
||||
t!("Fraction format (DIMFRAC)"),
|
||||
DsField::Dimfrac,
|
||||
vals.dimfrac,
|
||||
&[
|
||||
|
|
@ -593,7 +612,7 @@ pub fn view_window<'a>(
|
|||
],
|
||||
),
|
||||
enum_field(
|
||||
"Angular unit (DIMAUNIT)",
|
||||
t!("Angular unit (DIMAUNIT)"),
|
||||
DsField::Dimaunit,
|
||||
vals.dimaunit,
|
||||
&[
|
||||
|
|
@ -604,19 +623,19 @@ pub fn view_window<'a>(
|
|||
],
|
||||
),
|
||||
row![
|
||||
lbl("Angular decimals (DIMADEC)"),
|
||||
lbl(t!("Angular decimals (DIMADEC)")),
|
||||
mk_field(DsField::Dimadec, vals.dimadec)
|
||||
]
|
||||
.spacing(8)
|
||||
.align_y(iced::Center),
|
||||
row![
|
||||
lbl("Unit format (DIMUNIT)"),
|
||||
lbl(t!("Unit format (DIMUNIT)")),
|
||||
mk_field(DsField::Dimunit, vals.dimunit)
|
||||
]
|
||||
.spacing(8)
|
||||
.align_y(iced::Center),
|
||||
enum_field(
|
||||
"Angular zero supp (DIMAZIN)",
|
||||
t!("Angular zero supp (DIMAZIN)"),
|
||||
DsField::Dimazin,
|
||||
vals.dimazin,
|
||||
&[
|
||||
|
|
@ -626,9 +645,9 @@ pub fn view_window<'a>(
|
|||
("3", "Leading & trailing"),
|
||||
],
|
||||
),
|
||||
text("Fit").size(11).style(primary_style),
|
||||
text(t!("Fit")).size(11).style(primary_style),
|
||||
enum_field(
|
||||
"Fit (DIMATFIT)",
|
||||
t!("Fit (DIMATFIT)"),
|
||||
DsField::Dimatfit,
|
||||
vals.dimatfit,
|
||||
&[
|
||||
|
|
@ -639,7 +658,7 @@ pub fn view_window<'a>(
|
|||
],
|
||||
),
|
||||
enum_field(
|
||||
"Text movement (DIMTMOVE)",
|
||||
t!("Text movement (DIMTMOVE)"),
|
||||
DsField::Dimtmove,
|
||||
vals.dimtmove,
|
||||
&[
|
||||
|
|
@ -649,20 +668,20 @@ pub fn view_window<'a>(
|
|||
],
|
||||
),
|
||||
row![
|
||||
lbl("Fit (legacy DIMFIT)"),
|
||||
lbl(t!("Fit (legacy DIMFIT)")),
|
||||
mk_field(DsField::Dimfit, vals.dimfit)
|
||||
]
|
||||
.spacing(8)
|
||||
.align_y(iced::Center),
|
||||
chk("Force text inside (DIMTIX)", vals.dimtix, DsField::Dimtix),
|
||||
chk(t!("Force text inside (DIMTIX)"), vals.dimtix, DsField::Dimtix),
|
||||
chk(
|
||||
"Suppress outside arrows (DIMSOXD)",
|
||||
t!("Suppress outside arrows (DIMSOXD)"),
|
||||
vals.dimsoxd,
|
||||
DsField::Dimsoxd
|
||||
),
|
||||
chk("Place text manually (DIMUPT)", vals.dimupt, DsField::Dimupt),
|
||||
chk(t!("Place text manually (DIMUPT)"), vals.dimupt, DsField::Dimupt),
|
||||
chk(
|
||||
"Dim line between ext (DIMTOFL)",
|
||||
t!("Dim line between ext (DIMTOFL)"),
|
||||
vals.dimtofl,
|
||||
DsField::Dimtofl
|
||||
),
|
||||
|
|
@ -670,46 +689,46 @@ pub fn view_window<'a>(
|
|||
.spacing(7)
|
||||
.into(),
|
||||
5 => column![
|
||||
text("Alternate Units").size(11).style(primary_style),
|
||||
text(t!("Alternate Units")).size(11).style(primary_style),
|
||||
chk(
|
||||
"Enable alternate units (DIMALT)",
|
||||
t!("Enable alternate units (DIMALT)"),
|
||||
vals.dimalt,
|
||||
DsField::Dimalt
|
||||
),
|
||||
row![
|
||||
lbl("Multiplier (DIMALTF)"),
|
||||
lbl(t!("Multiplier (DIMALTF)")),
|
||||
mk_field(DsField::Dimaltf, vals.dimaltf)
|
||||
]
|
||||
.spacing(8)
|
||||
.align_y(iced::Center),
|
||||
row![
|
||||
lbl("Decimals (DIMALTD)"),
|
||||
lbl(t!("Decimals (DIMALTD)")),
|
||||
mk_field(DsField::Dimaltd, vals.dimaltd)
|
||||
]
|
||||
.spacing(8)
|
||||
.align_y(iced::Center),
|
||||
enum_field("Unit format (DIMALTU)", DsField::Dimaltu, vals.dimaltu, OPT_LUNIT),
|
||||
enum_field(t!("Unit format (DIMALTU)"), DsField::Dimaltu, vals.dimaltu, OPT_LUNIT),
|
||||
row![
|
||||
lbl("Tol decimals (DIMALTTD)"),
|
||||
lbl(t!("Tol decimals (DIMALTTD)")),
|
||||
mk_field(DsField::Dimalttd, vals.dimalttd)
|
||||
]
|
||||
.spacing(8)
|
||||
.align_y(iced::Center),
|
||||
row![
|
||||
lbl("Round off (DIMALTRND)"),
|
||||
lbl(t!("Round off (DIMALTRND)")),
|
||||
mk_field(DsField::Dimaltrnd, vals.dimaltrnd)
|
||||
]
|
||||
.spacing(8)
|
||||
.align_y(iced::Center),
|
||||
row![
|
||||
lbl("Suffix (DIMAPOST)"),
|
||||
lbl(t!("Suffix (DIMAPOST)")),
|
||||
mk_field(DsField::Dimapost, vals.dimapost)
|
||||
]
|
||||
.spacing(8)
|
||||
.align_y(iced::Center),
|
||||
enum_field("Zero suppress (DIMALTZ)", DsField::Dimaltz, vals.dimaltz, OPT_ZIN),
|
||||
enum_field(t!("Zero suppress (DIMALTZ)"), DsField::Dimaltz, vals.dimaltz, OPT_ZIN),
|
||||
enum_field(
|
||||
"Tol zero supp (DIMALTTZ)",
|
||||
t!("Tol zero supp (DIMALTTZ)"),
|
||||
DsField::Dimalttz,
|
||||
vals.dimalttz,
|
||||
OPT_ZIN,
|
||||
|
|
@ -718,40 +737,40 @@ pub fn view_window<'a>(
|
|||
.spacing(7)
|
||||
.into(),
|
||||
_ => column![
|
||||
text("Tolerances").size(11).style(primary_style),
|
||||
chk("Generate tolerances (DIMTOL)", vals.dimtol, DsField::Dimtol),
|
||||
chk("Limits generation (DIMLIM)", vals.dimlim, DsField::Dimlim),
|
||||
text(t!("Tolerances")).size(11).style(primary_style),
|
||||
chk(t!("Generate tolerances (DIMTOL)"), vals.dimtol, DsField::Dimtol),
|
||||
chk(t!("Limits generation (DIMLIM)"), vals.dimlim, DsField::Dimlim),
|
||||
row![
|
||||
lbl("Plus tolerance (DIMTP)"),
|
||||
lbl(t!("Plus tolerance (DIMTP)")),
|
||||
mk_field(DsField::Dimtp, vals.dimtp)
|
||||
]
|
||||
.spacing(8)
|
||||
.align_y(iced::Center),
|
||||
row![
|
||||
lbl("Minus tolerance (DIMTM)"),
|
||||
lbl(t!("Minus tolerance (DIMTM)")),
|
||||
mk_field(DsField::Dimtm, vals.dimtm)
|
||||
]
|
||||
.spacing(8)
|
||||
.align_y(iced::Center),
|
||||
row![
|
||||
lbl("Tol. decimals (DIMTDEC)"),
|
||||
lbl(t!("Tol. decimals (DIMTDEC)")),
|
||||
mk_field(DsField::Dimtdec, vals.dimtdec)
|
||||
]
|
||||
.spacing(8)
|
||||
.align_y(iced::Center),
|
||||
row![
|
||||
lbl("Tol. scale (DIMTFAC)"),
|
||||
lbl(t!("Tol. scale (DIMTFAC)")),
|
||||
mk_field(DsField::Dimtfac, vals.dimtfac)
|
||||
]
|
||||
.spacing(8)
|
||||
.align_y(iced::Center),
|
||||
enum_field(
|
||||
"Tol. vert just (DIMTOLJ)",
|
||||
t!("Tol. vert just (DIMTOLJ)"),
|
||||
DsField::Dimtolj,
|
||||
vals.dimtolj,
|
||||
&[("0", "Bottom"), ("1", "Middle"), ("2", "Top")],
|
||||
),
|
||||
enum_field("Tol. zero supp (DIMTZIN)", DsField::Dimtzin, vals.dimtzin, OPT_ZIN),
|
||||
enum_field(t!("Tol. zero supp (DIMTZIN)"), DsField::Dimtzin, vals.dimtzin, OPT_ZIN),
|
||||
]
|
||||
.spacing(7)
|
||||
.into(),
|
||||
|
|
@ -763,7 +782,7 @@ pub fn view_window<'a>(
|
|||
// content container instead of the panel padding.
|
||||
let right_panel = container(
|
||||
column![
|
||||
text(format!("Editing: {selected}")).size(11).style(muted_style),
|
||||
text(t!("Editing: %{selected}", selected = selected)).size(11).style(muted_style),
|
||||
tabs,
|
||||
hdivider(sizing.width),
|
||||
scrollable(container(tab_content).padding([12, 12]).width(sizing.width))
|
||||
|
|
|
|||
|
|
@ -5,6 +5,18 @@ use iced::widget::{
|
|||
button, checkbox, column, container, row, scrollable, text, text_input,
|
||||
};
|
||||
use iced::{Background, Border, Element, Theme};
|
||||
use crate::t;
|
||||
use std::borrow::Cow;
|
||||
use std::fmt;
|
||||
|
||||
#[derive(Clone, Debug, Eq, PartialEq)]
|
||||
struct EnumChoice(String);
|
||||
|
||||
impl fmt::Display for EnumChoice {
|
||||
fn fmt(&self, formatter: &mut fmt::Formatter<'_>) -> fmt::Result {
|
||||
formatter.write_str(crate::i18n::translate(&self.0).as_ref())
|
||||
}
|
||||
}
|
||||
|
||||
fn btn_s(accent: bool) -> impl Fn(&Theme, button::Status) -> button::Style {
|
||||
move |theme: &Theme, st| {
|
||||
|
|
@ -83,12 +95,12 @@ pub struct MLeaderStyleView<'a> {
|
|||
pub color_open: Option<&'static str>,
|
||||
}
|
||||
|
||||
fn section<'a>(label: &'static str) -> Element<'a, Message> {
|
||||
fn section<'a>(label: Cow<'static, str>) -> Element<'a, Message> {
|
||||
text(label).size(11).style(primary_style).into()
|
||||
}
|
||||
|
||||
fn num_row<'a>(
|
||||
label: &'static str,
|
||||
label: Cow<'static, str>,
|
||||
placeholder: &'static str,
|
||||
value: &'a str,
|
||||
field: &'static str,
|
||||
|
|
@ -105,7 +117,10 @@ fn num_row<'a>(
|
|||
.into()
|
||||
}
|
||||
|
||||
fn readonly_num_row<'a>(label: &'static str, value: &'a str) -> Element<'a, Message> {
|
||||
fn readonly_num_row<'a>(
|
||||
label: Cow<'static, str>,
|
||||
value: Cow<'static, str>,
|
||||
) -> Element<'a, Message> {
|
||||
row![
|
||||
text(label).size(11).style(muted_style).width(150),
|
||||
text(value).size(11).style(muted_style),
|
||||
|
|
@ -118,7 +133,7 @@ fn readonly_num_row<'a>(label: &'static str, value: &'a str) -> Element<'a, Mess
|
|||
/// Shared colour selector row. Reuses MLeaderStyleEdit by sending the chosen
|
||||
/// colour as an ACI string; `open` shows the expanded palette.
|
||||
fn color_row<'a>(
|
||||
label: &'static str,
|
||||
label: Cow<'static, str>,
|
||||
value: &'a str,
|
||||
field: &'static str,
|
||||
open: bool,
|
||||
|
|
@ -148,15 +163,15 @@ fn color_row<'a>(
|
|||
}
|
||||
|
||||
fn enum_row<'a>(
|
||||
label: &'static str,
|
||||
options: Vec<String>,
|
||||
label: Cow<'static, str>,
|
||||
options: Vec<EnumChoice>,
|
||||
selected: String,
|
||||
field: &'static str,
|
||||
) -> Element<'a, Message> {
|
||||
row![
|
||||
text(label).size(11).style(muted_style).width(150),
|
||||
iced::widget::pick_list(Some(selected), options, |value| value.to_string())
|
||||
.on_select(move |value| Message::MLeaderStyleSetEnum { field, value })
|
||||
iced::widget::pick_list(Some(EnumChoice(selected)), options, |value| value.to_string())
|
||||
.on_select(move |choice| Message::MLeaderStyleSetEnum { field, value: choice.0 })
|
||||
.text_size(11)
|
||||
.width(190),
|
||||
]
|
||||
|
|
@ -168,7 +183,7 @@ fn enum_row<'a>(
|
|||
fn lineweight_row<'a>(selected: acadrust::types::LineWeight) -> Element<'a, Message> {
|
||||
let selected = crate::ui::properties::LwItem(selected);
|
||||
row![
|
||||
text("Line weight:").size(11).style(muted_style).width(150),
|
||||
text(t!("Line weight:")).size(11).style(muted_style).width(150),
|
||||
iced::widget::pick_list(
|
||||
Some(selected),
|
||||
crate::ui::properties::lw_options(),
|
||||
|
|
@ -198,14 +213,14 @@ const ATTACH_OPTS: [&str; 11] = [
|
|||
"CenterOfTextOverline",
|
||||
];
|
||||
|
||||
fn opts(list: &[&str]) -> Vec<String> {
|
||||
list.iter().map(|s| s.to_string()).collect()
|
||||
fn opts(list: &[&str]) -> Vec<EnumChoice> {
|
||||
list.iter().map(|value| EnumChoice((*value).to_string())).collect()
|
||||
}
|
||||
|
||||
/// Dropdown for an Option<Handle> field (linetype / arrowhead / text style /
|
||||
/// block content). Options are record names; "None" clears the handle.
|
||||
fn handle_row<'a>(
|
||||
label: &'static str,
|
||||
label: Cow<'static, str>,
|
||||
options: Vec<String>,
|
||||
selected: String,
|
||||
field: &'static str,
|
||||
|
|
@ -222,7 +237,7 @@ fn handle_row<'a>(
|
|||
.into()
|
||||
}
|
||||
|
||||
fn chk<'a>(label: &'static str, val: bool, field: &'static str) -> Element<'a, Message> {
|
||||
fn chk<'a>(label: Cow<'static, str>, val: bool, field: &'static str) -> Element<'a, Message> {
|
||||
checkbox(val)
|
||||
.label(label)
|
||||
.on_toggle(move |_| Message::MLeaderStyleToggle(field))
|
||||
|
|
@ -240,174 +255,174 @@ pub fn view_window<'a>(
|
|||
scrollable(
|
||||
column![
|
||||
row![
|
||||
text("Name:").size(11).style(muted_style).width(150),
|
||||
text(t!("Name:")).size(11).style(muted_style).width(150),
|
||||
text(s.name.clone()).size(11),
|
||||
]
|
||||
.spacing(8),
|
||||
num_row("Description:", "", v.description, "description"),
|
||||
num_row(t!("Description:"), "", v.description, "description"),
|
||||
// Leader Format
|
||||
section("Leader Format"),
|
||||
section(t!("Leader Format")),
|
||||
enum_row(
|
||||
"Path type:",
|
||||
t!("Path type:"),
|
||||
opts(&["Invisible", "StraightLineSegments", "Spline"]),
|
||||
format!("{:?}", s.path_type),
|
||||
"path_type"
|
||||
),
|
||||
color_row("Line color (ACI):", v.line_color, "line_color", v.color_open == Some("line_color")),
|
||||
color_row(t!("Line color (ACI):"), v.line_color, "line_color", v.color_open == Some("line_color")),
|
||||
lineweight_row(s.line_weight),
|
||||
handle_row(
|
||||
"Line type:",
|
||||
t!("Line type:"),
|
||||
v.lt_opts.clone(),
|
||||
v.line_type_name.clone(),
|
||||
"line_type_handle"
|
||||
),
|
||||
handle_row(
|
||||
"Arrowhead block:",
|
||||
t!("Arrowhead block:"),
|
||||
v.block_opts.clone(),
|
||||
v.arrowhead_name.clone(),
|
||||
"arrowhead_handle"
|
||||
),
|
||||
num_row(
|
||||
"Arrowhead size:",
|
||||
t!("Arrowhead size:"),
|
||||
"0.18",
|
||||
v.arrowhead_size,
|
||||
"arrowhead_size"
|
||||
),
|
||||
num_row("Break gap size:", "0.125", v.break_gap, "break_gap"),
|
||||
num_row(t!("Break gap size:"), "0.125", v.break_gap, "break_gap"),
|
||||
// Leader Structure
|
||||
section("Leader Structure"),
|
||||
chk("Enable landing", s.enable_landing, "enable_landing"),
|
||||
chk("Enable dogleg", s.enable_dogleg, "enable_dogleg"),
|
||||
section(t!("Leader Structure")),
|
||||
chk(t!("Enable landing"), s.enable_landing, "enable_landing"),
|
||||
chk(t!("Enable dogleg"), s.enable_dogleg, "enable_dogleg"),
|
||||
num_row(
|
||||
"Landing distance:",
|
||||
t!("Landing distance:"),
|
||||
"8.0",
|
||||
v.landing_distance,
|
||||
"landing_distance"
|
||||
),
|
||||
num_row("Landing gap:", "0.09", v.landing_gap, "landing_gap"),
|
||||
num_row("Max leader points:", "2", v.max_points, "max_points"),
|
||||
num_row(t!("Landing gap:"), "0.09", v.landing_gap, "landing_gap"),
|
||||
num_row(t!("Max leader points:"), "2", v.max_points, "max_points"),
|
||||
num_row(
|
||||
"First seg. angle:",
|
||||
t!("First seg. angle:"),
|
||||
"0",
|
||||
v.first_seg_angle,
|
||||
"first_seg_angle"
|
||||
),
|
||||
num_row(
|
||||
"Second seg. angle:",
|
||||
t!("Second seg. angle:"),
|
||||
"0",
|
||||
v.second_seg_angle,
|
||||
"second_seg_angle"
|
||||
),
|
||||
if s.is_annotative {
|
||||
readonly_num_row("Scale factor:", "By annotation scale")
|
||||
readonly_num_row(t!("Scale factor:"), t!("By annotation scale"))
|
||||
} else {
|
||||
num_row("Scale factor:", "1.0", v.scale_factor, "scale_factor")
|
||||
num_row(t!("Scale factor:"), "1.0", v.scale_factor, "scale_factor")
|
||||
},
|
||||
num_row("Align space:", "4.0", v.align_space, "align_space"),
|
||||
num_row(t!("Align space:"), "4.0", v.align_space, "align_space"),
|
||||
enum_row(
|
||||
"Leader draw order:",
|
||||
t!("Leader draw order:"),
|
||||
opts(&["LeaderHeadFirst", "LeaderTailFirst"]),
|
||||
format!("{:?}", s.leader_draw_order),
|
||||
"leader_draw_order"
|
||||
),
|
||||
enum_row(
|
||||
"Multileader draw order:",
|
||||
t!("Multileader draw order:"),
|
||||
opts(&["ContentFirst", "LeaderFirst"]),
|
||||
format!("{:?}", s.multileader_draw_order),
|
||||
"multileader_draw_order"
|
||||
),
|
||||
chk("Annotative", s.is_annotative, "annotative"),
|
||||
chk(t!("Annotative"), s.is_annotative, "annotative"),
|
||||
// Content
|
||||
section("Content"),
|
||||
section(t!("Content")),
|
||||
enum_row(
|
||||
"Content type:",
|
||||
t!("Content type:"),
|
||||
opts(&["None", "Block", "MText", "Tolerance"]),
|
||||
format!("{:?}", s.content_type),
|
||||
"content_type"
|
||||
),
|
||||
num_row("Default text:", "", v.default_text, "default_text"),
|
||||
num_row(t!("Default text:"), "", v.default_text, "default_text"),
|
||||
handle_row(
|
||||
"Text style:",
|
||||
t!("Text style:"),
|
||||
v.textstyle_opts.clone(),
|
||||
v.text_style_name.clone(),
|
||||
"text_style_handle"
|
||||
),
|
||||
num_row("Text height:", "0.18", v.text_height, "text_height"),
|
||||
color_row("Text color (ACI):", v.text_color, "text_color", v.color_open == Some("text_color")),
|
||||
num_row(t!("Text height:"), "0.18", v.text_height, "text_height"),
|
||||
color_row(t!("Text color (ACI):"), v.text_color, "text_color", v.color_open == Some("text_color")),
|
||||
enum_row(
|
||||
"Text angle:",
|
||||
t!("Text angle:"),
|
||||
opts(&["ParallelToLastLeaderLine", "Horizontal", "Optimized"]),
|
||||
format!("{:?}", s.text_angle_type),
|
||||
"text_angle_type"
|
||||
),
|
||||
enum_row(
|
||||
"Text alignment:",
|
||||
t!("Text alignment:"),
|
||||
opts(&["Left", "Center", "Right"]),
|
||||
format!("{:?}", s.text_alignment),
|
||||
"text_alignment"
|
||||
),
|
||||
enum_row(
|
||||
"Left attachment:",
|
||||
t!("Left attachment:"),
|
||||
opts(&ATTACH_OPTS),
|
||||
format!("{:?}", s.text_left_attachment),
|
||||
"text_left_attachment"
|
||||
),
|
||||
enum_row(
|
||||
"Right attachment:",
|
||||
t!("Right attachment:"),
|
||||
opts(&ATTACH_OPTS),
|
||||
format!("{:?}", s.text_right_attachment),
|
||||
"text_right_attachment"
|
||||
),
|
||||
enum_row(
|
||||
"Top attachment:",
|
||||
t!("Top attachment:"),
|
||||
opts(&ATTACH_OPTS),
|
||||
format!("{:?}", s.text_top_attachment),
|
||||
"text_top_attachment"
|
||||
),
|
||||
enum_row(
|
||||
"Bottom attachment:",
|
||||
t!("Bottom attachment:"),
|
||||
opts(&ATTACH_OPTS),
|
||||
format!("{:?}", s.text_bottom_attachment),
|
||||
"text_bottom_attachment"
|
||||
),
|
||||
enum_row(
|
||||
"Attachment direction:",
|
||||
t!("Attachment direction:"),
|
||||
opts(&["Horizontal", "Vertical"]),
|
||||
format!("{:?}", s.text_attachment_direction),
|
||||
"text_attachment_direction"
|
||||
),
|
||||
chk("Text frame", s.text_frame, "text_frame"),
|
||||
chk("Text always left", s.text_always_left, "text_always_left"),
|
||||
chk(t!("Text frame"), s.text_frame, "text_frame"),
|
||||
chk(t!("Text always left"), s.text_always_left, "text_always_left"),
|
||||
// Block Content
|
||||
section("Block Content"),
|
||||
section(t!("Block Content")),
|
||||
handle_row(
|
||||
"Block:",
|
||||
t!("Block:"),
|
||||
v.block_opts.clone(),
|
||||
v.block_content_name.clone(),
|
||||
"block_content_handle"
|
||||
),
|
||||
color_row("Block color (ACI):", v.block_color, "block_color", v.color_open == Some("block_color")),
|
||||
color_row(t!("Block color (ACI):"), v.block_color, "block_color", v.color_open == Some("block_color")),
|
||||
enum_row(
|
||||
"Block connection:",
|
||||
t!("Block connection:"),
|
||||
opts(&["BlockExtents", "BasePoint"]),
|
||||
format!("{:?}", s.block_content_connection),
|
||||
"block_content_connection"
|
||||
),
|
||||
num_row("Block rotation:", "0", v.block_rotation, "block_rotation"),
|
||||
num_row("Block scale X:", "1.0", v.block_scale_x, "block_scale_x"),
|
||||
num_row("Block scale Y:", "1.0", v.block_scale_y, "block_scale_y"),
|
||||
num_row("Block scale Z:", "1.0", v.block_scale_z, "block_scale_z"),
|
||||
num_row(t!("Block rotation:"), "0", v.block_rotation, "block_rotation"),
|
||||
num_row(t!("Block scale X:"), "1.0", v.block_scale_x, "block_scale_x"),
|
||||
num_row(t!("Block scale Y:"), "1.0", v.block_scale_y, "block_scale_y"),
|
||||
num_row(t!("Block scale Z:"), "1.0", v.block_scale_z, "block_scale_z"),
|
||||
chk(
|
||||
"Enable block scale",
|
||||
t!("Enable block scale"),
|
||||
s.enable_block_scale,
|
||||
"enable_block_scale"
|
||||
),
|
||||
chk(
|
||||
"Enable block rotation",
|
||||
t!("Enable block rotation"),
|
||||
s.enable_block_rotation,
|
||||
"enable_block_rotation"
|
||||
),
|
||||
button(text("Apply").size(11))
|
||||
button(text(t!("Apply")).size(11))
|
||||
.on_press(Message::MLeaderStyleApply)
|
||||
.style(btn_s(true))
|
||||
.padding([4, 14]),
|
||||
|
|
@ -419,7 +434,7 @@ pub fn view_window<'a>(
|
|||
.height(sizing.height)
|
||||
.into()
|
||||
} else {
|
||||
container(text("Select a style to view details.").size(11).style(muted_style))
|
||||
container(text(t!("Select a style to view details.")).size(11).style(muted_style))
|
||||
.padding([12, 12])
|
||||
.into()
|
||||
};
|
||||
|
|
|
|||
|
|
@ -3,6 +3,8 @@
|
|||
use crate::app::Message;
|
||||
use iced::widget::{column, container, row, scrollable, text};
|
||||
use iced::{Element, Theme};
|
||||
use crate::t;
|
||||
use std::borrow::Cow;
|
||||
|
||||
fn muted_style(theme: &Theme) -> iced::widget::text::Style {
|
||||
iced::widget::text::Style {
|
||||
|
|
@ -20,7 +22,7 @@ pub fn view_window<'a>(
|
|||
sizing: crate::ui::modal::ModalSizing,
|
||||
) -> Element<'a, Message> {
|
||||
// ── Right: Details panel ──────────────────────────────────────────────
|
||||
let info_row = |label: &'static str, val: String| -> Element<'_, Message> {
|
||||
let info_row = |label: Cow<'static, str>, val: String| -> Element<'_, Message> {
|
||||
row![
|
||||
text(label).size(11).style(muted_style).width(120),
|
||||
text(val).size(11),
|
||||
|
|
@ -61,9 +63,9 @@ pub fn view_window<'a>(
|
|||
.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")
|
||||
info_row(t!("Name:"), s.name.clone()),
|
||||
info_row(t!("Elements:"), s.elements.len().to_string()),
|
||||
text(t!(" Off Color Ltype"))
|
||||
.size(10)
|
||||
.style(muted_style)
|
||||
.into(),
|
||||
|
|
@ -73,7 +75,7 @@ pub fn view_window<'a>(
|
|||
.height(sizing.height)
|
||||
.into()
|
||||
} else {
|
||||
container(text("Select a style to view details.").size(11).style(muted_style))
|
||||
container(text(t!("Select a style to view details.")).size(11).style(muted_style))
|
||||
.padding([12, 12])
|
||||
.into()
|
||||
};
|
||||
|
|
|
|||
|
|
@ -3,6 +3,8 @@
|
|||
use crate::app::Message;
|
||||
use iced::widget::{button, column, container, row, scrollable, text, text_input, Space};
|
||||
use iced::{Background, Border, Element, Theme};
|
||||
use crate::t;
|
||||
use std::borrow::Cow;
|
||||
|
||||
fn btn_s(accent: bool) -> impl Fn(&Theme, button::Status) -> button::Style {
|
||||
move |theme: &Theme, st| {
|
||||
|
|
@ -89,21 +91,21 @@ pub fn view_window<'a>(
|
|||
sizing: crate::ui::modal::ModalSizing,
|
||||
) -> Element<'a, Message> {
|
||||
let table_name = table
|
||||
.map(|t| t.name.as_str())
|
||||
.unwrap_or("(no table loaded)");
|
||||
.map(|t| t.name.clone())
|
||||
.unwrap_or_else(|| t!("(no table loaded)").into_owned());
|
||||
|
||||
// ── Toolbar ───────────────────────────────────────────────────────────
|
||||
let toolbar = container(
|
||||
row![
|
||||
button(text("Load CTB/STB").size(11))
|
||||
button(text(t!("Load CTB/STB")).size(11))
|
||||
.on_press(Message::PlotStyleLoad)
|
||||
.style(btn_s(false))
|
||||
.padding([4, 10]),
|
||||
button(text("Save As…").size(11))
|
||||
button(text(t!("Save As…")).size(11))
|
||||
.on_press(Message::PlotStylePanelSave)
|
||||
.style(btn_s(false))
|
||||
.padding([4, 10]),
|
||||
button(text("Clear Table").size(11))
|
||||
button(text(t!("Clear Table")).size(11))
|
||||
.on_press(Message::PlotStyleClear)
|
||||
.style(btn_s(false))
|
||||
.padding([4, 10]),
|
||||
|
|
@ -149,7 +151,7 @@ pub fn view_window<'a>(
|
|||
let label = if has_override {
|
||||
format!("{aci:>3} {color_str:<9} {lw_str}")
|
||||
} else {
|
||||
format!("{aci:>3} (default)")
|
||||
format!("{aci:>3} {}", t!("(default)"))
|
||||
};
|
||||
button(text(label).size(10).font(iced::Font::MONOSPACE))
|
||||
.on_press(Message::PlotStylePanelSelectAci(aci))
|
||||
|
|
@ -178,7 +180,7 @@ pub fn view_window<'a>(
|
|||
|
||||
let aci_list = container(
|
||||
column![
|
||||
text("ACI Color Index").size(10).style(muted_style),
|
||||
text(t!("ACI Color Index")).size(10).style(muted_style),
|
||||
container(scrollable(column(aci_items).spacing(1)).height(sizing.height))
|
||||
.style(|theme: &Theme| {
|
||||
let palette = theme.palette();
|
||||
|
|
@ -212,11 +214,11 @@ pub fn view_window<'a>(
|
|||
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());
|
||||
.unwrap_or_else(|| t!("(none)").into());
|
||||
let cur_lw = entry
|
||||
.map(|e| {
|
||||
if e.lineweight == 255 {
|
||||
"object".into()
|
||||
t!("object").into()
|
||||
} else {
|
||||
crate::io::plot_style::LW_TABLE
|
||||
.get(e.lineweight as usize)
|
||||
|
|
@ -229,41 +231,41 @@ pub fn view_window<'a>(
|
|||
.map(|e| format!("{}%", e.screening))
|
||||
.unwrap_or_else(|| "—".into());
|
||||
|
||||
let lbl = |s: &'static str| text(s).size(11).style(muted_style);
|
||||
let lbl = |s: Cow<'static, str>| text(s).size(11).style(muted_style);
|
||||
|
||||
let edit_panel = container(
|
||||
column![
|
||||
row![
|
||||
text("ACI:").size(11).style(muted_style).width(100),
|
||||
text(t!("ACI:")).size(11).style(muted_style).width(100),
|
||||
text(format!("{selected_aci}")).size(11),
|
||||
]
|
||||
.spacing(8)
|
||||
.align_y(iced::Center),
|
||||
lbl("Color override (#RRGGBB):"),
|
||||
text_input("#RRGGBB or blank", color_buf)
|
||||
lbl(t!("Color override (#RRGGBB):")),
|
||||
text_input(t!("#RRGGBB or blank").as_ref(), color_buf)
|
||||
.on_input(Message::PlotStylePanelColorBuf)
|
||||
.style(field_style)
|
||||
.size(11)
|
||||
.padding([4, 8]),
|
||||
lbl("Lineweight index (0-24, 255=obj):"),
|
||||
lbl(t!("Lineweight index (0-24, 255=obj):")),
|
||||
text_input("255", lw_buf)
|
||||
.on_input(Message::PlotStylePanelLwBuf)
|
||||
.style(field_style)
|
||||
.size(11)
|
||||
.padding([4, 8]),
|
||||
lbl("Screening (0-100):"),
|
||||
lbl(t!("Screening (0-100):")),
|
||||
text_input("100", screen_buf)
|
||||
.on_input(Message::PlotStylePanelScreenBuf)
|
||||
.style(field_style)
|
||||
.size(11)
|
||||
.padding([4, 8]),
|
||||
Space::new().height(8),
|
||||
text("Current values:").size(10).style(muted_style),
|
||||
text(format!(" Color: {cur_color}")).size(10),
|
||||
text(format!(" Lineweight: {cur_lw}")).size(10),
|
||||
text(format!(" Screening: {cur_scr}")).size(10),
|
||||
text(t!("Current values:")).size(10).style(muted_style),
|
||||
text(t!(" Color: %{cur_color}", cur_color = cur_color)).size(10),
|
||||
text(t!(" Lineweight: %{cur_lw}", cur_lw = cur_lw)).size(10),
|
||||
text(t!(" Screening: %{cur_scr}", cur_scr = cur_scr)).size(10),
|
||||
Space::new().height(sizing.height),
|
||||
button(text("Apply to ACI").size(11))
|
||||
button(text(t!("Apply to ACI")).size(11))
|
||||
.on_press(Message::PlotStylePanelApply)
|
||||
.style(btn_s(true))
|
||||
.padding([5, 10]),
|
||||
|
|
|
|||
|
|
@ -8,6 +8,8 @@
|
|||
use crate::app::Message;
|
||||
use iced::widget::{button, canvas, column, container, radio, row, text, text_input, Space};
|
||||
use iced::{mouse, Background, Border, Element, Length, Point, Rectangle, Size, Theme};
|
||||
use crate::t;
|
||||
use std::borrow::Cow;
|
||||
|
||||
const CELL_PX: f32 = 44.0;
|
||||
|
||||
|
|
@ -151,7 +153,7 @@ pub fn view_window<'a>(
|
|||
}
|
||||
|
||||
let size_row = row![
|
||||
text("Point Size:").size(13),
|
||||
text(t!("Point Size:")).size(13),
|
||||
Space::new().width(10),
|
||||
text_input("0", size_buf)
|
||||
.on_input(Message::PointStyleSizeInput)
|
||||
|
|
@ -160,13 +162,13 @@ pub fn view_window<'a>(
|
|||
.size(13)
|
||||
.width(110),
|
||||
Space::new().width(6),
|
||||
text(if relative { "%" } else { "units" }).size(12).style(muted_style),
|
||||
text(if relative { Cow::Borrowed("%") } else { t!("units") }).size(12).style(muted_style),
|
||||
]
|
||||
.align_y(iced::Center);
|
||||
|
||||
let radios = column![
|
||||
radio(
|
||||
"Set Size Relative to Screen",
|
||||
t!("Set Size Relative to Screen"),
|
||||
true,
|
||||
Some(relative),
|
||||
Message::PointStyleSizeRelative,
|
||||
|
|
@ -174,7 +176,7 @@ pub fn view_window<'a>(
|
|||
.size(15)
|
||||
.text_size(13),
|
||||
radio(
|
||||
"Set Size in Absolute Units",
|
||||
t!("Set Size in Absolute Units"),
|
||||
false,
|
||||
Some(relative),
|
||||
Message::PointStyleSizeRelative,
|
||||
|
|
@ -184,14 +186,14 @@ pub fn view_window<'a>(
|
|||
]
|
||||
.spacing(6);
|
||||
|
||||
let ok = button(text("OK").size(13))
|
||||
let ok = button(text(t!("OK")).size(13))
|
||||
.padding([5, 22])
|
||||
.on_press(Message::PointStyleOk)
|
||||
.style(button::primary);
|
||||
|
||||
container(
|
||||
column![
|
||||
text("Point Style").size(18),
|
||||
text(t!("Point Style")).size(18),
|
||||
Space::new().height(6),
|
||||
grid,
|
||||
Space::new().height(12),
|
||||
|
|
|
|||
|
|
@ -13,6 +13,8 @@ use iced::widget::{
|
|||
column, container, mouse_area, row, scrollable, text, text_input, Space,
|
||||
};
|
||||
use iced::{Background, Border, Element, Theme};
|
||||
use crate::t;
|
||||
use std::borrow::Cow;
|
||||
|
||||
/// Inline-rename text-input id, so the rename-start handler can focus it.
|
||||
pub fn rename_input_id() -> iced::widget::Id {
|
||||
|
|
@ -54,12 +56,12 @@ pub fn view_window<'a, 'b>(
|
|||
// ── Toolbar: New / Delete | Set Current / Apply ───────────────────────
|
||||
let toolbar = container(
|
||||
row![
|
||||
tb_button("New", Message::ScaleManagerNew, false),
|
||||
tb_button("Copy", Message::ScaleManagerCopy, false),
|
||||
tb_button("Delete", Message::ScaleManagerDelete, false),
|
||||
tb_button(t!("New"), Message::ScaleManagerNew, false),
|
||||
tb_button(t!("Copy"), Message::ScaleManagerCopy, false),
|
||||
tb_button(t!("Delete"), Message::ScaleManagerDelete, false),
|
||||
Space::new().width(sizing.width),
|
||||
tb_button("Set Current", Message::ScaleManagerSetCurrent, false),
|
||||
tb_button("Apply", Message::ScaleManagerApply, true),
|
||||
tb_button(t!("Set Current"), Message::ScaleManagerSetCurrent, false),
|
||||
tb_button(t!("Apply"), Message::ScaleManagerApply, true),
|
||||
]
|
||||
.spacing(4)
|
||||
.align_y(iced::Center),
|
||||
|
|
@ -119,7 +121,7 @@ pub fn view_window<'a, 'b>(
|
|||
|
||||
let list_panel = container(
|
||||
column![
|
||||
text("Scales").size(10).style(muted_text_style),
|
||||
text(t!("Scales")).size(10).style(muted_text_style),
|
||||
container(scrollable(column(rows).spacing(1)).height(sizing.height))
|
||||
.style(|theme: &Theme| {
|
||||
let palette = theme.palette();
|
||||
|
|
@ -151,10 +153,10 @@ pub fn view_window<'a, 'b>(
|
|||
|
||||
// ── Right: editor (name + paper:drawing) ──────────────────────────────
|
||||
let field =
|
||||
|label: &'static str, ph: &'static str, value: &str, on: fn(String) -> Message| {
|
||||
|label: Cow<'static, str>, ph: Cow<'static, str>, value: &str, on: fn(String) -> Message| {
|
||||
row![
|
||||
text(label).size(11).style(muted_text_style).width(96),
|
||||
text_input(ph, value)
|
||||
text_input(ph.as_ref(), value)
|
||||
.on_input(on)
|
||||
.style(field_style)
|
||||
.size(12)
|
||||
|
|
@ -167,11 +169,11 @@ pub fn view_window<'a, 'b>(
|
|||
|
||||
let editor = container(
|
||||
column![
|
||||
text("Scale").size(10).style(muted_text_style),
|
||||
field("Paper units", "1", paper_buf, Message::ScaleManagerPaperBuf),
|
||||
field("Drawing units", "50", drawing_buf, Message::ScaleManagerDrawingBuf),
|
||||
text(Cow::Borrowed("Scale")).size(10).style(muted_text_style),
|
||||
field(t!("Paper units"), Cow::Borrowed("1"), paper_buf, Message::ScaleManagerPaperBuf),
|
||||
field(t!("Drawing units"), Cow::Borrowed("50"), drawing_buf, Message::ScaleManagerDrawingBuf),
|
||||
Space::new().height(6),
|
||||
text("Double-click a scale to rename it; edit its paper : drawing ratio here. New / Copy add a scale. Changes are kept only if you click Apply before closing.")
|
||||
text(t!("Double-click a scale to rename it; edit its paper : drawing ratio here. New / Copy add a scale. Changes are kept only if you click Apply before closing."))
|
||||
.size(10)
|
||||
.style(muted_text_style),
|
||||
]
|
||||
|
|
|
|||
|
|
@ -11,6 +11,8 @@ use crate::app::{Message, StyleKind};
|
|||
use iced::widget::button::{Status, Style};
|
||||
use iced::widget::{button, column, container, row, scrollable, text, Space};
|
||||
use iced::{Background, Border, Element, Theme};
|
||||
use crate::t;
|
||||
use std::borrow::Cow;
|
||||
|
||||
/// Everything the shared frame needs. The per-manager `editor` element is the
|
||||
/// only bespoke part.
|
||||
|
|
@ -51,12 +53,12 @@ pub fn view<'a, 'b>(s: Scaffold<'a, 'b>) -> Element<'a, Message> {
|
|||
let height = s.sizing.height;
|
||||
// ── Toolbar: New / Copy / Delete | … | Set Current / Apply ────────────
|
||||
let bar = row![
|
||||
tb_button("New", s.on_new, false),
|
||||
tb_button("Copy", s.on_copy, false),
|
||||
tb_button("Delete", s.on_delete, false),
|
||||
tb_button(t!("New"), s.on_new, false),
|
||||
tb_button(t!("Copy"), s.on_copy, false),
|
||||
tb_button(t!("Delete"), s.on_delete, false),
|
||||
Space::new().width(width),
|
||||
tb_button("Set Current", s.on_set_current, false),
|
||||
tb_button("Apply", s.on_apply, true),
|
||||
tb_button(t!("Set Current"), s.on_set_current, false),
|
||||
tb_button(t!("Apply"), s.on_apply, true),
|
||||
]
|
||||
.spacing(4)
|
||||
.align_y(iced::Center);
|
||||
|
|
@ -91,7 +93,7 @@ pub fn view<'a, 'b>(s: Scaffold<'a, 'b>) -> Element<'a, Message> {
|
|||
|
||||
let list_panel = container(
|
||||
column![
|
||||
text("Styles").size(10).style(muted_text_style),
|
||||
text(t!("Styles")).size(10).style(muted_text_style),
|
||||
container(scrollable(column(rows).spacing(1)).height(height))
|
||||
.style(|theme: &Theme| {
|
||||
let palette = theme.palette();
|
||||
|
|
@ -137,9 +139,13 @@ pub fn view<'a, 'b>(s: Scaffold<'a, 'b>) -> Element<'a, Message> {
|
|||
|
||||
// ── Shared chrome ──────────────────────────────────────────────────────────
|
||||
|
||||
pub(crate) fn tb_button<'a>(label: &'a str, msg: Message, accent: bool) -> Element<'a, Message> {
|
||||
pub(crate) fn tb_button(
|
||||
label: impl Into<Cow<'static, str>>,
|
||||
msg: Message,
|
||||
accent: bool,
|
||||
) -> Element<'static, Message> {
|
||||
let pad = if accent { [4, 14] } else { [4, 10] };
|
||||
button(text(label).size(11))
|
||||
button(text(label.into()).size(11))
|
||||
.on_press(msg)
|
||||
.style(btn_s(accent))
|
||||
.padding(pad)
|
||||
|
|
|
|||
|
|
@ -5,6 +5,22 @@ use iced::widget::{
|
|||
button, checkbox, column, container, row, scrollable, text, text_input, Column,
|
||||
};
|
||||
use iced::{Background, Border, Element, Theme};
|
||||
use crate::t;
|
||||
use std::borrow::Cow;
|
||||
use std::fmt;
|
||||
|
||||
#[derive(Clone, Debug, Eq, PartialEq)]
|
||||
struct EnumChoice(String);
|
||||
|
||||
impl fmt::Display for EnumChoice {
|
||||
fn fmt(&self, formatter: &mut fmt::Formatter<'_>) -> fmt::Result {
|
||||
formatter.write_str(crate::i18n::translate(&self.0).as_ref())
|
||||
}
|
||||
}
|
||||
|
||||
fn enum_choices(values: &[&str]) -> Vec<EnumChoice> {
|
||||
values.iter().map(|value| EnumChoice((*value).to_string())).collect()
|
||||
}
|
||||
|
||||
fn btn_s(accent: bool) -> impl Fn(&Theme, button::Status) -> button::Style {
|
||||
move |theme: &Theme, st| {
|
||||
|
|
@ -66,7 +82,7 @@ pub fn view_window<'a>(
|
|||
sizing: crate::ui::modal::ModalSizing,
|
||||
) -> Element<'a, Message> {
|
||||
// ── Right: Details panel ──────────────────────────────────────────────
|
||||
let info_row = |label: &'static str, val: String| -> Element<'_, Message> {
|
||||
let info_row = |label: Cow<'static, str>, val: String| -> Element<'_, Message> {
|
||||
row![
|
||||
text(label).size(11).style(muted_style).width(160),
|
||||
text(val).size(11),
|
||||
|
|
@ -76,12 +92,12 @@ pub fn view_window<'a>(
|
|||
.into()
|
||||
};
|
||||
|
||||
let cell_editor = |row_label: &'static str,
|
||||
let cell_editor = |row_label: Cow<'static, str>,
|
||||
row: u8,
|
||||
rs: &acadrust::objects::RowCellStyle|
|
||||
-> Element<'a, Message> {
|
||||
let r = row as usize;
|
||||
let cell_in = |label: &'static str,
|
||||
let cell_in = |label: Cow<'static, str>,
|
||||
placeholder: &'static str,
|
||||
value: &'a str,
|
||||
field: &'static str|
|
||||
|
|
@ -103,7 +119,7 @@ pub fn view_window<'a>(
|
|||
};
|
||||
// Shared colour selector — sends the chosen colour through the same
|
||||
// cell edit as an ACI string.
|
||||
let cell_color = |label: &'static str, value: &'a str, field: &'static str| -> Element<'a, Message> {
|
||||
let cell_color = |label: Cow<'static, str>, value: &'a str, field: &'static str| -> Element<'a, Message> {
|
||||
let cur = crate::ui::color_select::aci_string_to_color(value);
|
||||
let open = color_open == Some((row, field));
|
||||
let selector = crate::ui::color_select::color_selector(
|
||||
|
|
@ -133,20 +149,20 @@ pub fn view_window<'a>(
|
|||
.spacing(3)
|
||||
.push(text(row_label).size(11).style(primary_style))
|
||||
.push(cell_in(
|
||||
" Text style:",
|
||||
t!(" Text style:"),
|
||||
"Standard",
|
||||
&cell_textstyle[r],
|
||||
"textstyle",
|
||||
))
|
||||
.push(cell_in(" Text height:", "0.18", &cell_height[r], "height"))
|
||||
.push(cell_color(" Text color:", &cell_textcolor[r], "textcolor"))
|
||||
.push(cell_color(" Fill color:", &cell_fillcolor[r], "fillcolor"))
|
||||
.push(cell_in(t!(" Text height:"), "0.18", &cell_height[r], "height"))
|
||||
.push(cell_color(t!(" Text color:"), &cell_textcolor[r], "textcolor"))
|
||||
.push(cell_color(t!(" Fill color:"), &cell_fillcolor[r], "fillcolor"))
|
||||
.push(
|
||||
row![
|
||||
text(" Alignment:").size(11).style(muted_style).width(150),
|
||||
text(t!(" Alignment:")).size(11).style(muted_style).width(150),
|
||||
iced::widget::pick_list(
|
||||
Some(format!("{:?}", rs.alignment)),
|
||||
[
|
||||
Some(EnumChoice(format!("{:?}", rs.alignment))),
|
||||
enum_choices(&[
|
||||
"TopLeft",
|
||||
"TopCenter",
|
||||
"TopRight",
|
||||
|
|
@ -156,13 +172,13 @@ pub fn view_window<'a>(
|
|||
"BottomLeft",
|
||||
"BottomCenter",
|
||||
"BottomRight",
|
||||
]
|
||||
.iter()
|
||||
.map(|s| s.to_string())
|
||||
.collect::<Vec<_>>(),
|
||||
]),
|
||||
|value| value.to_string(),
|
||||
)
|
||||
.on_select(move |value| Message::TableStyleCellSetAlign { row, value })
|
||||
.on_select(move |choice| Message::TableStyleCellSetAlign {
|
||||
row,
|
||||
value: choice.0,
|
||||
})
|
||||
.text_size(11)
|
||||
.width(140),
|
||||
]
|
||||
|
|
@ -171,16 +187,16 @@ pub fn view_window<'a>(
|
|||
)
|
||||
.push(
|
||||
checkbox(rs.fill_enabled)
|
||||
.label(" Background fill enabled")
|
||||
.label(t!(" Background fill enabled"))
|
||||
.on_toggle(move |_| Message::TableStyleCellToggleFill(row))
|
||||
.size(14)
|
||||
.text_size(11),
|
||||
)
|
||||
.push(cell_in(" Data type:", "0", &cell_datatype[r], "datatype"))
|
||||
.push(cell_in(" Unit type:", "0", &cell_unittype[r], "unittype"))
|
||||
.push(cell_in(" Format string:", "", &cell_format[r], "format"))
|
||||
.push(cell_in(t!(" Data type:"), "0", &cell_datatype[r], "datatype"))
|
||||
.push(cell_in(t!(" Unit type:"), "0", &cell_unittype[r], "unittype"))
|
||||
.push(cell_in(t!(" Format string:"), "", &cell_format[r], "format"))
|
||||
.push(
|
||||
text(" Borders (type / weight / color / spacing / hidden)")
|
||||
text(t!(" Borders (type / weight / color / spacing / hidden)"))
|
||||
.size(10)
|
||||
.style(muted_style),
|
||||
);
|
||||
|
|
@ -199,21 +215,18 @@ pub fn view_window<'a>(
|
|||
row![
|
||||
text(format!(" {bname}")).size(11).style(muted_style).width(28),
|
||||
iced::widget::pick_list(
|
||||
Some(format!("{:?}", bd.border_type)),
|
||||
["Single", "Double"]
|
||||
.iter()
|
||||
.map(|s| s.to_string())
|
||||
.collect::<Vec<_>>(),
|
||||
Some(EnumChoice(format!("{:?}", bd.border_type))),
|
||||
enum_choices(&["Single", "Double"]),
|
||||
|value| value.to_string(),
|
||||
)
|
||||
.on_select(move |value| Message::TableStyleBorderSetType {
|
||||
.on_select(move |choice| Message::TableStyleBorderSetType {
|
||||
cell: row,
|
||||
border: bu,
|
||||
value
|
||||
value: choice.0,
|
||||
})
|
||||
.text_size(10)
|
||||
.width(74),
|
||||
text_input("wt", &border_lw[r][b])
|
||||
text_input("", &border_lw[r][b])
|
||||
.on_input(move |v| Message::TableStyleBorderEdit {
|
||||
cell: row,
|
||||
border: bu,
|
||||
|
|
@ -222,7 +235,7 @@ pub fn view_window<'a>(
|
|||
})
|
||||
.size(10)
|
||||
.width(46),
|
||||
text_input("clr", &border_color[r][b])
|
||||
text_input("", &border_color[r][b])
|
||||
.on_input(move |v| Message::TableStyleBorderEdit {
|
||||
cell: row,
|
||||
border: bu,
|
||||
|
|
@ -231,7 +244,7 @@ pub fn view_window<'a>(
|
|||
})
|
||||
.size(10)
|
||||
.width(46),
|
||||
text_input("gap", &border_spacing[r][b])
|
||||
text_input("", &border_spacing[r][b])
|
||||
.on_input(move |v| Message::TableStyleBorderEdit {
|
||||
cell: row,
|
||||
border: bu,
|
||||
|
|
@ -253,7 +266,7 @@ pub fn view_window<'a>(
|
|||
}
|
||||
|
||||
col.push(
|
||||
button(text("Apply cell").size(11))
|
||||
button(text(t!("Apply cell")).size(11))
|
||||
.on_press(Message::TableStyleCellApply(row))
|
||||
.style(btn_s(true))
|
||||
.padding([4, 12]),
|
||||
|
|
@ -264,9 +277,9 @@ pub fn view_window<'a>(
|
|||
let details: Element<'_, Message> = if let Some(s) = selected_style {
|
||||
scrollable(
|
||||
column![
|
||||
info_row("Name:", s.name.clone()),
|
||||
info_row(t!("Name:"), s.name.clone()),
|
||||
row![
|
||||
text("Description:").size(11).style(muted_style).width(160),
|
||||
text(t!("Description:")).size(11).style(muted_style).width(160),
|
||||
text_input("", description_buf)
|
||||
.on_input(|v| Message::TableStyleEdit {
|
||||
field: "description",
|
||||
|
|
@ -278,23 +291,20 @@ pub fn view_window<'a>(
|
|||
.spacing(8)
|
||||
.align_y(iced::Center),
|
||||
row![
|
||||
text("Flow direction:").size(11).style(muted_style).width(160),
|
||||
text(t!("Flow direction:")).size(11).style(muted_style).width(160),
|
||||
iced::widget::pick_list(
|
||||
Some(format!("{:?}", s.flow_direction)),
|
||||
["Down", "Up"]
|
||||
.iter()
|
||||
.map(|s| s.to_string())
|
||||
.collect::<Vec<_>>(),
|
||||
Some(EnumChoice(format!("{:?}", s.flow_direction))),
|
||||
enum_choices(&["Down", "Up"]),
|
||||
|value| value.to_string(),
|
||||
)
|
||||
.on_select(Message::TableStyleSetFlow)
|
||||
.on_select(|choice| Message::TableStyleSetFlow(choice.0))
|
||||
.text_size(11)
|
||||
.width(100),
|
||||
]
|
||||
.spacing(8)
|
||||
.align_y(iced::Center),
|
||||
row![
|
||||
text("H Margin:").size(11).style(muted_style).width(160),
|
||||
text(t!("H Margin:")).size(11).style(muted_style).width(160),
|
||||
text_input("1.5", hmargin_buf)
|
||||
.on_input(|v| Message::TableStyleEdit {
|
||||
field: "hmargin",
|
||||
|
|
@ -306,7 +316,7 @@ pub fn view_window<'a>(
|
|||
.spacing(8)
|
||||
.align_y(iced::Center),
|
||||
row![
|
||||
text("V Margin:").size(11).style(muted_style).width(160),
|
||||
text(t!("V Margin:")).size(11).style(muted_style).width(160),
|
||||
text_input("1.5", vmargin_buf)
|
||||
.on_input(|v| Message::TableStyleEdit {
|
||||
field: "vmargin",
|
||||
|
|
@ -318,22 +328,22 @@ pub fn view_window<'a>(
|
|||
.spacing(8)
|
||||
.align_y(iced::Center),
|
||||
checkbox(s.title_suppressed)
|
||||
.label("Title row suppressed")
|
||||
.label(t!("Title row suppressed"))
|
||||
.on_toggle(|_| Message::TableStyleToggle("title_sup"))
|
||||
.size(14)
|
||||
.text_size(11),
|
||||
checkbox(s.header_suppressed)
|
||||
.label("Header row suppressed")
|
||||
.label(t!("Header row suppressed"))
|
||||
.on_toggle(|_| Message::TableStyleToggle("header_sup"))
|
||||
.size(14)
|
||||
.text_size(11),
|
||||
button(text("Apply margins").size(11))
|
||||
button(text(t!("Apply margins")).size(11))
|
||||
.on_press(Message::TableStyleApply)
|
||||
.style(btn_s(true))
|
||||
.padding([4, 12]),
|
||||
cell_editor("Data Row:", 0, &s.data_row_style),
|
||||
cell_editor("Header Row:", 1, &s.header_row_style),
|
||||
cell_editor("Title Row:", 2, &s.title_row_style),
|
||||
cell_editor(t!("Data Row:"), 0, &s.data_row_style),
|
||||
cell_editor(t!("Header Row:"), 1, &s.header_row_style),
|
||||
cell_editor(t!("Title Row:"), 2, &s.title_row_style),
|
||||
]
|
||||
.spacing(6)
|
||||
.padding([12, 12]),
|
||||
|
|
@ -342,7 +352,7 @@ pub fn view_window<'a>(
|
|||
.height(sizing.height)
|
||||
.into()
|
||||
} else {
|
||||
container(text("Select a style to view details.").size(11).style(muted_style))
|
||||
container(text(t!("Select a style to view details.")).size(11).style(muted_style))
|
||||
.padding([12, 12])
|
||||
.into()
|
||||
};
|
||||
|
|
|
|||
|
|
@ -6,6 +6,8 @@ use iced::widget::{
|
|||
button, canvas, checkbox, column, container, row, scrollable, text, text_input, Space,
|
||||
};
|
||||
use iced::{mouse, Background, Border, Element, Length, Point, Rectangle, Theme};
|
||||
use crate::t;
|
||||
use std::borrow::Cow;
|
||||
|
||||
/// View-model for the Text Style editor window.
|
||||
pub struct TextStyleView<'a> {
|
||||
|
|
@ -217,7 +219,7 @@ pub fn view_window<'a>(
|
|||
|
||||
let font_panel = container(
|
||||
column![
|
||||
text("Font File").size(10).style(muted_style),
|
||||
text(t!("Font File")).size(10).style(muted_style),
|
||||
container(scrollable(column(font_items).spacing(1)).height(sizing.height))
|
||||
.style(|theme: &Theme| {
|
||||
let palette = theme.palette();
|
||||
|
|
@ -234,7 +236,7 @@ pub fn view_window<'a>(
|
|||
.width(sizing.width)
|
||||
.height(sizing.height)
|
||||
.padding(2),
|
||||
text_input("font file…", font_buf)
|
||||
text_input(t!("font file…").as_ref(), font_buf)
|
||||
.on_input(|v| Message::TextStyleEdit {
|
||||
field: "font",
|
||||
value: v
|
||||
|
|
@ -252,14 +254,14 @@ pub fn view_window<'a>(
|
|||
|
||||
// Labeled numeric/text field row → TextStyleEdit { field, value }.
|
||||
fn frow<'a>(
|
||||
label: &'a str,
|
||||
ph: &'a str,
|
||||
label: impl Into<Cow<'static, str>>,
|
||||
ph: impl Into<Cow<'static, str>>,
|
||||
buf: &'a str,
|
||||
field: &'static str,
|
||||
) -> Element<'a, Message> {
|
||||
row![
|
||||
text(label).size(11).style(muted_style).width(120),
|
||||
text_input(ph, buf)
|
||||
text(label.into()).size(11).style(muted_style).width(120),
|
||||
text_input(ph.into().as_ref(), buf)
|
||||
.on_input(move |v| Message::TextStyleEdit { field, value: v })
|
||||
.style(field_style)
|
||||
.size(11)
|
||||
|
|
@ -295,41 +297,41 @@ pub fn view_window<'a>(
|
|||
// ── Right: Properties ─────────────────────────────────────────────────
|
||||
let props_panel = container(
|
||||
column![
|
||||
text("Properties").size(11).style(primary_style),
|
||||
frow("Big Font:", "big-font file…", bigfont_buf, "bigfont"),
|
||||
frow("TrueType Font:", "e.g. Arial", ttf_buf, "ttf"),
|
||||
text(t!("Properties")).size(11).style(primary_style),
|
||||
frow(t!("Big Font:"), t!("big-font file…"), bigfont_buf, "bigfont"),
|
||||
frow(t!("TrueType Font:"), t!("e.g. Arial"), ttf_buf, "ttf"),
|
||||
frow(
|
||||
if annotative {
|
||||
"Paper Text Height:"
|
||||
t!("Paper Text Height:")
|
||||
} else {
|
||||
"Fixed Height:"
|
||||
t!("Fixed Height:")
|
||||
},
|
||||
"0 = variable",
|
||||
t!("0 = variable"),
|
||||
height_buf,
|
||||
"height",
|
||||
),
|
||||
frow("Width Factor:", "1.0", width_buf, "width"),
|
||||
frow("Oblique (°):", "0.0", oblique_buf, "oblique"),
|
||||
frow(t!("Width Factor:"), "1.0", width_buf, "width"),
|
||||
frow(t!("Oblique (°):"), "0.0", oblique_buf, "oblique"),
|
||||
row![
|
||||
checkbox(backward)
|
||||
.label("Backward")
|
||||
.label(t!("Backward"))
|
||||
.on_toggle(|_| Message::TextStyleToggle("backward"))
|
||||
.size(15)
|
||||
.text_size(11),
|
||||
checkbox(upside_down)
|
||||
.label("Upside down")
|
||||
.label(t!("Upside down"))
|
||||
.on_toggle(|_| Message::TextStyleToggle("upside_down"))
|
||||
.size(15)
|
||||
.text_size(11),
|
||||
]
|
||||
.spacing(16),
|
||||
checkbox(annotative)
|
||||
.label("Annotative")
|
||||
.label(t!("Annotative"))
|
||||
.on_toggle(|_| Message::TextStyleToggle("annotative"))
|
||||
.size(15)
|
||||
.text_size(11),
|
||||
Space::new().height(8),
|
||||
text("Preview").size(10).style(muted_style),
|
||||
text(t!("Preview")).size(10).style(muted_style),
|
||||
container(preview)
|
||||
.style(|theme: &Theme| {
|
||||
let palette = theme.palette();
|
||||
|
|
@ -377,7 +379,7 @@ pub fn view_window<'a>(
|
|||
|
||||
let ttf_panel = container(
|
||||
column![
|
||||
text("TrueType (system)").size(10).style(muted_style),
|
||||
text(t!("TrueType (system)")).size(10).style(muted_style),
|
||||
container(scrollable(column(ttf_items).spacing(1)).height(sizing.height))
|
||||
.style(|theme: &Theme| {
|
||||
let palette = theme.palette();
|
||||
|
|
@ -394,7 +396,7 @@ pub fn view_window<'a>(
|
|||
.width(sizing.width)
|
||||
.height(sizing.height)
|
||||
.padding(2),
|
||||
text_input("TrueType font…", ttf_buf)
|
||||
text_input(t!("TrueType font…").as_ref(), ttf_buf)
|
||||
.on_input(|v| Message::TextStyleEdit {
|
||||
field: "ttf",
|
||||
value: v
|
||||
|
|
|
|||
|
|
@ -1,8 +1,12 @@
|
|||
use crate::app::Message;
|
||||
use iced::widget::{button, column, container, row, text};
|
||||
use iced::{Background, Element, Theme};
|
||||
use crate::t;
|
||||
|
||||
fn info_row<'a>(label: &'static str, value: String) -> Element<'a, Message> {
|
||||
fn info_row<'a>(
|
||||
label: std::borrow::Cow<'static, str>,
|
||||
value: String,
|
||||
) -> Element<'a, Message> {
|
||||
row![
|
||||
text(label)
|
||||
.size(11)
|
||||
|
|
@ -30,7 +34,7 @@ pub fn view_window<'a>() -> Element<'a, Message> {
|
|||
.style(|theme: &Theme| iced::widget::text::Style {
|
||||
color: Some(theme.palette().primary.base.color),
|
||||
}),
|
||||
text("CAD application for Architecture & Engineering")
|
||||
text(t!("CAD application for Architecture & Engineering"))
|
||||
.size(11)
|
||||
.style(|theme: &Theme| iced::widget::text::Style {
|
||||
color: Some(theme.palette().background.base.text.scale_alpha(0.68)),
|
||||
|
|
@ -49,16 +53,16 @@ pub fn view_window<'a>() -> Element<'a, Message> {
|
|||
|
||||
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(t!("Version"), format!("v{}", version)),
|
||||
info_row(t!("Platform"), os.to_string()),
|
||||
info_row(t!("Arch"), arch.to_string()),
|
||||
]
|
||||
.spacing(2)
|
||||
.padding([12, 16]),
|
||||
)
|
||||
.style(container::bordered_box);
|
||||
|
||||
let copy_btn = button(text("Copy Info").size(11))
|
||||
let copy_btn = button(text(t!("Copy Info")).size(11))
|
||||
.on_press(Message::AboutCopyInfo)
|
||||
.style(button::primary)
|
||||
.padding([6, 16]);
|
||||
|
|
|
|||
|
|
@ -7,6 +7,7 @@
|
|||
use crate::app::Message;
|
||||
use iced::widget::{button, column, container, row, scrollable, text, text_input, Space};
|
||||
use iced::{Background, Element, Length, Theme};
|
||||
use crate::t;
|
||||
|
||||
/// Which column of an alias row a text edit targets.
|
||||
#[derive(Clone, Copy, Debug)]
|
||||
|
|
@ -29,11 +30,10 @@ pub fn view_window(
|
|||
rows: &[(String, String)],
|
||||
sizing: crate::ui::modal::ModalSizing,
|
||||
) -> Element<'_, Message> {
|
||||
let title = text("Command Aliases").size(15);
|
||||
let hint = text(
|
||||
"Type an alias and the command it runs (e.g. L → LINE). \
|
||||
Apply to save to ocad.pgp; closing discards unapplied edits.",
|
||||
)
|
||||
let title = text(t!("Command Aliases")).size(15);
|
||||
let hint = text(t!(
|
||||
"Type an alias and the command it runs (e.g. L → LINE). Apply to save to ocad.pgp; closing discards unapplied edits."
|
||||
))
|
||||
.size(11)
|
||||
.style(muted_style);
|
||||
|
||||
|
|
@ -44,8 +44,8 @@ pub fn view_window(
|
|||
|
||||
let head = container(
|
||||
row![
|
||||
container(text("Alias").size(11).style(muted_style)).width(Length::Fixed(120.0)),
|
||||
container(text("Command").size(11).style(muted_style)).width(sizing.width),
|
||||
container(text(t!("Alias")).size(11).style(muted_style)).width(Length::Fixed(120.0)),
|
||||
container(text(t!("Command")).size(11).style(muted_style)).width(sizing.width),
|
||||
Space::new().width(Length::Fixed(30.0)),
|
||||
]
|
||||
.spacing(8),
|
||||
|
|
@ -54,12 +54,12 @@ pub fn view_window(
|
|||
|
||||
let mut list = column![].spacing(3);
|
||||
for (idx, (alias, cmd)) in rows.iter().enumerate() {
|
||||
let alias_box = text_input("alias", alias)
|
||||
let alias_box = text_input(t!("alias").as_ref(), alias)
|
||||
.on_input(move |v| Message::AliasEditorInput { idx, field: AliasField::Alias, value: v })
|
||||
.size(13)
|
||||
.padding([3, 6])
|
||||
.width(Length::Fixed(120.0));
|
||||
let cmd_box = text_input("command", cmd)
|
||||
let cmd_box = text_input(t!("command").as_ref(), cmd)
|
||||
.on_input(move |v| Message::AliasEditorInput { idx, field: AliasField::Command, value: v })
|
||||
.size(13)
|
||||
.padding([3, 6])
|
||||
|
|
@ -75,13 +75,13 @@ pub fn view_window(
|
|||
);
|
||||
}
|
||||
|
||||
let add = button(text("+ Add alias").size(12))
|
||||
let add = button(text(t!("+ Add alias")).size(12))
|
||||
.on_press(Message::AliasEditorAdd)
|
||||
.padding([4, 10])
|
||||
.style(button::secondary);
|
||||
|
||||
// Apply — primary action; commits the rows to ocad.pgp and stays open.
|
||||
let apply = button(text("Apply").size(12))
|
||||
let apply = button(text(t!("Apply")).size(12))
|
||||
.on_press(Message::AliasEditorApply)
|
||||
.padding([4, 16])
|
||||
.style(button::primary);
|
||||
|
|
|
|||
|
|
@ -20,6 +20,9 @@ use iced::widget::{
|
|||
button, checkbox, column, container, row, scrollable, text, text_input, Space,
|
||||
};
|
||||
use iced::{Background, Border, Element, Length, Theme};
|
||||
use crate::t;
|
||||
use std::borrow::Cow;
|
||||
use std::fmt;
|
||||
|
||||
use crate::ui::properties::{lw_options, LwItem};
|
||||
|
||||
|
|
@ -59,7 +62,8 @@ pub struct AttrRow {
|
|||
}
|
||||
|
||||
/// Combined justification options (horizontal × vertical), matching the
|
||||
/// standard text-justification list.
|
||||
/// standard text-justification list. The stored labels are the English source
|
||||
/// texts; `justify_label` / `justify_from_label` translate both directions.
|
||||
pub const JUSTIFY: &[(&str, HorizontalAlignment, VerticalAlignment)] = &[
|
||||
("Left", HorizontalAlignment::Left, VerticalAlignment::Baseline),
|
||||
("Center", HorizontalAlignment::Center, VerticalAlignment::Baseline),
|
||||
|
|
@ -78,13 +82,15 @@ pub const JUSTIFY: &[(&str, HorizontalAlignment, VerticalAlignment)] = &[
|
|||
("Bottom Right", HorizontalAlignment::Right, VerticalAlignment::Bottom),
|
||||
];
|
||||
|
||||
/// The justification label for an (h, v) pair (defaults to "Left").
|
||||
pub fn justify_label(h: HorizontalAlignment, v: VerticalAlignment) -> &'static str {
|
||||
/// The justification label for an (h, v) pair (defaults to "Left"). Kept in
|
||||
/// English (like the colour names) — the labels are standard CAD terms and
|
||||
/// their keys would collide with ribbon tool labels such as "Right".
|
||||
pub fn justify_label(h: HorizontalAlignment, v: VerticalAlignment) -> Cow<'static, str> {
|
||||
JUSTIFY
|
||||
.iter()
|
||||
.find(|(_, jh, jv)| *jh == h && *jv == v)
|
||||
.map(|(l, _, _)| *l)
|
||||
.unwrap_or("Left")
|
||||
.map(|(l, _, _)| Cow::Borrowed(*l))
|
||||
.unwrap_or(Cow::Borrowed("Left"))
|
||||
}
|
||||
|
||||
/// Resolve a justification label back to its (h, v) pair.
|
||||
|
|
@ -171,7 +177,7 @@ fn hdivider<'a>(width: Length) -> Element<'a, Message> {
|
|||
|
||||
/// One `label : widget` row with a fixed-width label column.
|
||||
fn field_row<'a>(
|
||||
label: &'a str,
|
||||
label: Cow<'static, str>,
|
||||
widget: Element<'a, Message>,
|
||||
width: Length,
|
||||
) -> Element<'a, Message> {
|
||||
|
|
@ -187,7 +193,7 @@ fn field_row<'a>(
|
|||
|
||||
/// A labelled editable text field for one of the selected row's string buffers.
|
||||
fn edit_field<'a>(
|
||||
label: &'a str,
|
||||
label: Cow<'static, str>,
|
||||
value: &'a str,
|
||||
on_input: impl Fn(String) -> Message + 'a,
|
||||
width: Length,
|
||||
|
|
@ -204,7 +210,7 @@ fn edit_field<'a>(
|
|||
|
||||
/// A labelled pick_list of owned string options.
|
||||
fn pick_field<'a>(
|
||||
label: &'a str,
|
||||
label: Cow<'static, str>,
|
||||
options: Vec<String>,
|
||||
selected: Option<String>,
|
||||
on_select: impl Fn(String) -> Message + 'a,
|
||||
|
|
@ -218,7 +224,47 @@ fn pick_field<'a>(
|
|||
field_row(label, pl.into(), width)
|
||||
}
|
||||
|
||||
fn tab_button<'a>(label: &'a str, this: AttrTab, active: AttrTab) -> Element<'a, Message> {
|
||||
#[derive(Clone, Debug, PartialEq, Eq)]
|
||||
struct LocalizedChoice {
|
||||
raw: String,
|
||||
label: String,
|
||||
}
|
||||
|
||||
impl LocalizedChoice {
|
||||
fn new(raw: String) -> Self {
|
||||
let label = raw
|
||||
.strip_prefix("Color ")
|
||||
.map(|number| crate::tf!("Color {}", number).into_owned())
|
||||
.unwrap_or_else(|| crate::i18n::translate(&raw).into_owned());
|
||||
Self { raw, label }
|
||||
}
|
||||
}
|
||||
|
||||
impl fmt::Display for LocalizedChoice {
|
||||
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
|
||||
f.write_str(&self.label)
|
||||
}
|
||||
}
|
||||
|
||||
/// A translated pick list which keeps the stored and emitted value unchanged.
|
||||
fn localized_pick_field<'a>(
|
||||
label: Cow<'static, str>,
|
||||
options: Vec<String>,
|
||||
selected: Option<String>,
|
||||
on_select: impl Fn(String) -> Message + 'a,
|
||||
width: Length,
|
||||
) -> Element<'a, Message> {
|
||||
let options = options.into_iter().map(LocalizedChoice::new).collect::<Vec<_>>();
|
||||
let selected = selected.map(LocalizedChoice::new);
|
||||
let pl = iced::widget::pick_list(selected, options, |value| value.to_string())
|
||||
.on_select(move |choice: LocalizedChoice| on_select(choice.raw))
|
||||
.text_size(13)
|
||||
.padding([3, 6])
|
||||
.width(width);
|
||||
field_row(label, pl.into(), width)
|
||||
}
|
||||
|
||||
fn tab_button<'a>(label: Cow<'static, str>, this: AttrTab, active: AttrTab) -> Element<'a, Message> {
|
||||
let is_active = this == active;
|
||||
button(text(label).size(11))
|
||||
.padding([4, 12])
|
||||
|
|
@ -264,13 +310,13 @@ pub fn view_window<'a>(
|
|||
let height = sizing.height;
|
||||
// ── Top toolbar: block name on the left, Apply on the right ───────────
|
||||
// Mirrors the style-manager windows (actions left, primary action right).
|
||||
let apply = button(text("Apply").size(11))
|
||||
let apply = button(text(t!("Apply")).size(11))
|
||||
.padding([4, 14])
|
||||
.on_press(Message::AttrEditorApply)
|
||||
.style(button::primary);
|
||||
let toolbar = container(
|
||||
row![
|
||||
text(format!("Block: {block}")).size(12).style(muted_style),
|
||||
text(t!("Block: %{block}", block = block)).size(12).style(muted_style),
|
||||
Space::new().width(width),
|
||||
apply,
|
||||
]
|
||||
|
|
@ -286,14 +332,14 @@ pub fn view_window<'a>(
|
|||
.padding([5, 8]);
|
||||
|
||||
let tabs = row![
|
||||
tab_button("Attribute", AttrTab::Attribute, tab),
|
||||
tab_button("Text Options", AttrTab::TextOptions, tab),
|
||||
tab_button("Properties", AttrTab::Properties, tab),
|
||||
tab_button(t!("Attribute"), AttrTab::Attribute, tab),
|
||||
tab_button(t!("Text Options"), AttrTab::TextOptions, tab),
|
||||
tab_button(t!("Properties"), AttrTab::Properties, tab),
|
||||
]
|
||||
.spacing(2);
|
||||
|
||||
let body: Element<'_, Message> = if rows.is_empty() {
|
||||
text("This block has no attributes.").size(13).style(muted_style).into()
|
||||
text(t!("This block has no attributes.")).size(13).style(muted_style).into()
|
||||
} else {
|
||||
match tab {
|
||||
AttrTab::Attribute => attribute_tab(rows, selected, width, height),
|
||||
|
|
@ -337,9 +383,9 @@ fn attribute_tab<'a>(
|
|||
height: Length,
|
||||
) -> Element<'a, Message> {
|
||||
let head = row![
|
||||
container(text("Tag").size(11).style(muted_style)).width(130),
|
||||
container(text("Prompt").size(11).style(muted_style)).width(width),
|
||||
container(text("Value").size(11).style(muted_style)).width(140),
|
||||
container(text(t!("Tag")).size(11).style(muted_style)).width(130),
|
||||
container(text(t!("Prompt")).size(11).style(muted_style)).width(width),
|
||||
container(text(t!("Value")).size(11).style(muted_style)).width(140),
|
||||
]
|
||||
.spacing(6);
|
||||
|
||||
|
|
@ -389,7 +435,7 @@ fn attribute_tab<'a>(
|
|||
head,
|
||||
scrollable(list).width(width).height(height),
|
||||
Space::new().height(8),
|
||||
field_row("Value:", value_box.into(), width),
|
||||
field_row(t!("Value:"), value_box.into(), width),
|
||||
]
|
||||
.spacing(6)
|
||||
.width(width)
|
||||
|
|
@ -409,24 +455,24 @@ fn text_options_tab<'a>(
|
|||
} else {
|
||||
Some(r.text_style.clone())
|
||||
};
|
||||
let justify_opts: Vec<String> = JUSTIFY.iter().map(|(l, _, _)| l.to_string()).collect();
|
||||
let justify_opts: Vec<String> = JUSTIFY.iter().map(|(l, _, _)| (*l).to_string()).collect();
|
||||
let justify_sel = Some(justify_label(r.h_align, r.v_align).to_string());
|
||||
|
||||
column![
|
||||
pick_field("Text Style", styles, style_sel, |s| {
|
||||
pick_field(t!("Text Style"), styles, style_sel, |s| {
|
||||
Message::AttrEditorTextStyle(s)
|
||||
}, width),
|
||||
pick_field("Justification", justify_opts, justify_sel, |s| {
|
||||
localized_pick_field(t!("Justification"), justify_opts, justify_sel, |s| {
|
||||
Message::AttrEditorJustify(s)
|
||||
}, width),
|
||||
edit_field("Height", &r.height, Message::AttrEditorHeight, width),
|
||||
edit_field("Rotation", &r.rotation, Message::AttrEditorRotation, width),
|
||||
edit_field("Width Factor", &r.width_factor, Message::AttrEditorWidth, width),
|
||||
edit_field("Oblique Angle", &r.oblique, Message::AttrEditorOblique, width),
|
||||
edit_field(t!("Height"), &r.height, Message::AttrEditorHeight, width),
|
||||
edit_field(t!("Rotation"), &r.rotation, Message::AttrEditorRotation, width),
|
||||
edit_field(t!("Width Factor"), &r.width_factor, Message::AttrEditorWidth, width),
|
||||
edit_field(t!("Oblique Angle"), &r.oblique, Message::AttrEditorOblique, width),
|
||||
field_row(
|
||||
"",
|
||||
Cow::Borrowed(""),
|
||||
checkbox(r.backwards)
|
||||
.label("Backwards")
|
||||
.label(t!("Backwards"))
|
||||
.on_toggle(Message::AttrEditorBackwards)
|
||||
.size(15)
|
||||
.text_size(12)
|
||||
|
|
@ -434,9 +480,9 @@ fn text_options_tab<'a>(
|
|||
width,
|
||||
),
|
||||
field_row(
|
||||
"",
|
||||
Cow::Borrowed(""),
|
||||
checkbox(r.upside_down)
|
||||
.label("Upside down")
|
||||
.label(t!("Upside down"))
|
||||
.on_toggle(Message::AttrEditorUpsideDown)
|
||||
.size(15)
|
||||
.text_size(12)
|
||||
|
|
@ -483,12 +529,12 @@ fn properties_tab<'a>(
|
|||
.width(width);
|
||||
|
||||
column![
|
||||
pick_field("Layer", layers, layer_sel, Message::AttrEditorLayer, width),
|
||||
pick_field("Linetype", linetypes, lt_sel, Message::AttrEditorLinetype, width),
|
||||
pick_field("Color", color_opts, color_sel, |s| {
|
||||
pick_field(t!("Layer"), layers, layer_sel, Message::AttrEditorLayer, width),
|
||||
localized_pick_field(t!("Linetype"), linetypes, lt_sel, Message::AttrEditorLinetype, width),
|
||||
localized_pick_field(t!("Color"), color_opts, color_sel, |s| {
|
||||
Message::AttrEditorColor(s)
|
||||
}, width),
|
||||
field_row("Lineweight", lw.into(), width),
|
||||
field_row(t!("Lineweight"), lw.into(), width),
|
||||
]
|
||||
.spacing(8)
|
||||
.width(width)
|
||||
|
|
|
|||
|
|
@ -1,6 +1,8 @@
|
|||
use crate::app::Message;
|
||||
use crate::t;
|
||||
use iced::widget::{button, column, row, text, text_input, Space};
|
||||
use iced::{Element, Fill, Length, Shrink};
|
||||
use std::borrow::Cow;
|
||||
|
||||
pub const FIND_INPUT_ID: &str = "find-replace-search";
|
||||
|
||||
|
|
@ -15,19 +17,19 @@ pub fn view_window<'a>(
|
|||
} else {
|
||||
Shrink
|
||||
};
|
||||
let find_input = text_input("Text to find", search)
|
||||
let find_input = text_input("", search)
|
||||
.id(iced::widget::Id::new(FIND_INPUT_ID))
|
||||
.on_input(Message::FindReplaceSearchChanged)
|
||||
.on_submit(Message::FindReplaceNext)
|
||||
.padding([6, 8])
|
||||
.size(13);
|
||||
let replacement_input = text_input("Replacement text", replacement)
|
||||
let replacement_input = text_input("", replacement)
|
||||
.on_input(Message::FindReplaceReplacementChanged)
|
||||
.padding([6, 8])
|
||||
.size(13);
|
||||
|
||||
let enabled = !search.trim().is_empty();
|
||||
let action = |label: &'static str, message: Message| {
|
||||
let action = |label: Cow<'static, str>, message: Message| {
|
||||
let button = button(text(label).size(12)).padding([6, 12]);
|
||||
if enabled {
|
||||
button.on_press(message)
|
||||
|
|
@ -38,29 +40,29 @@ pub fn view_window<'a>(
|
|||
|
||||
column![
|
||||
row![
|
||||
text("Find:").size(12).width(90),
|
||||
text(t!("Find:")).size(12).width(90),
|
||||
find_input.width(field_width),
|
||||
]
|
||||
.spacing(8)
|
||||
.align_y(iced::Center),
|
||||
row![
|
||||
text("Replace with:").size(12).width(90),
|
||||
text(t!("Replace with:")).size(12).width(90),
|
||||
replacement_input.width(field_width),
|
||||
]
|
||||
.spacing(8)
|
||||
.align_y(iced::Center),
|
||||
text("Searches Text, MText, Attribute Definitions, and block attribute values.")
|
||||
text(t!("Searches Text, MText, Attribute Definitions, and block attribute values."))
|
||||
.size(11),
|
||||
text(status).size(11),
|
||||
row![
|
||||
Space::new().width(field_width),
|
||||
button(text("Close").size(12))
|
||||
button(text(t!("Close")).size(12))
|
||||
.on_press(Message::CloseModal)
|
||||
.padding([6, 12])
|
||||
.style(button::secondary),
|
||||
action("Replace", Message::FindReplaceOne).style(button::secondary),
|
||||
action("Replace All", Message::FindReplaceAll).style(button::danger),
|
||||
action("Find Next", Message::FindReplaceNext).style(button::primary),
|
||||
action(t!("Replace"), Message::FindReplaceOne).style(button::secondary),
|
||||
action(t!("Replace All"), Message::FindReplaceAll).style(button::danger),
|
||||
action(t!("Find Next"), Message::FindReplaceNext).style(button::primary),
|
||||
]
|
||||
.spacing(8)
|
||||
.align_y(iced::Center),
|
||||
|
|
|
|||
|
|
@ -7,6 +7,8 @@ use iced::widget::{
|
|||
button, checkbox, column, container, row, scrollable, text, text_input, Space,
|
||||
};
|
||||
use iced::{Background, Border, Element, Fill, Length, Theme};
|
||||
use crate::t;
|
||||
use std::borrow::Cow;
|
||||
use std::fmt;
|
||||
|
||||
#[derive(Clone, PartialEq, Debug)]
|
||||
|
|
@ -15,7 +17,7 @@ struct TransparencyItem(Option<acadrust::types::Transparency>);
|
|||
impl fmt::Display for TransparencyItem {
|
||||
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
|
||||
match self.0 {
|
||||
None => write!(f, "Not set"),
|
||||
None => write!(f, "{}", t!("Not set")),
|
||||
Some(value) => write!(f, "{}%", (value.as_percent() * 100.0).round() as u8),
|
||||
}
|
||||
}
|
||||
|
|
@ -96,7 +98,7 @@ fn mask_summary(mask: LayerStateMask) -> String {
|
|||
];
|
||||
properties
|
||||
.into_iter()
|
||||
.filter_map(|(flag, label)| mask.contains(flag).then_some(label))
|
||||
.filter_map(|(flag, label)| mask.contains(flag).then(|| t!(label)))
|
||||
.collect::<Vec<_>>()
|
||||
.join(", ")
|
||||
}
|
||||
|
|
@ -126,7 +128,7 @@ pub fn view_window<'a>(
|
|||
let is_selected =
|
||||
selected.is_some_and(|selected| state.name.eq_ignore_ascii_case(selected));
|
||||
let subtitle = if state.description.is_empty() {
|
||||
format!("{} layers", state.layers.len())
|
||||
t!("%{n} layers", n = state.layers.len()).to_string()
|
||||
} else {
|
||||
state.description.clone()
|
||||
};
|
||||
|
|
@ -148,13 +150,13 @@ pub fn view_window<'a>(
|
|||
let empty: Element<'_, Message> = container(
|
||||
column![
|
||||
text(if states.is_empty() {
|
||||
"No layer states in this drawing"
|
||||
t!("No layer states in this drawing")
|
||||
} else {
|
||||
"No matching layer states"
|
||||
t!("No matching layer states")
|
||||
})
|
||||
.size(11)
|
||||
.style(muted),
|
||||
text("Choose New to capture the current layer settings.")
|
||||
text(t!("Choose New to capture the current layer settings."))
|
||||
.size(10)
|
||||
.style(muted),
|
||||
]
|
||||
|
|
@ -173,7 +175,7 @@ pub fn view_window<'a>(
|
|||
|
||||
let left = container(
|
||||
column![
|
||||
text_input("Search layer states…", filter)
|
||||
text_input(t!("Search layer states…").as_ref(), filter)
|
||||
.on_input(Message::LayerStateManagerFilter)
|
||||
.size(11)
|
||||
.padding([5, 8]),
|
||||
|
|
@ -204,14 +206,14 @@ pub fn view_window<'a>(
|
|||
|
||||
let details = if let Some(state) = selected_state {
|
||||
column![
|
||||
text("Saved state details").size(13),
|
||||
text(t!("Saved state details")).size(13),
|
||||
row![
|
||||
text("Layers").size(10).style(muted).width(92),
|
||||
text(t!("Layers")).size(10).style(muted).width(92),
|
||||
text(state.layers.len().to_string()).size(11),
|
||||
]
|
||||
.spacing(8),
|
||||
row![
|
||||
text("Current layer").size(10).style(muted).width(92),
|
||||
text(t!("Current layer")).size(10).style(muted).width(92),
|
||||
text(if state.current_layer.is_empty() {
|
||||
"—".to_string()
|
||||
} else {
|
||||
|
|
@ -220,14 +222,14 @@ pub fn view_window<'a>(
|
|||
.size(11),
|
||||
]
|
||||
.spacing(8),
|
||||
text("Restored properties").size(10).style(muted),
|
||||
text(t!("Restored properties")).size(10).style(muted),
|
||||
text(mask_summary(state.mask)).size(11),
|
||||
]
|
||||
.spacing(7)
|
||||
} else {
|
||||
column![
|
||||
text("New layer state").size(13),
|
||||
text("Save captures the current settings of every layer in the drawing.")
|
||||
text(t!("New layer state")).size(13),
|
||||
text(t!("Save captures the current settings of every layer in the drawing."))
|
||||
.size(11)
|
||||
.style(muted),
|
||||
]
|
||||
|
|
@ -235,31 +237,31 @@ pub fn view_window<'a>(
|
|||
};
|
||||
|
||||
let restore = if selected_state.is_some() {
|
||||
button(text("Restore").size(11))
|
||||
button(text(t!("Restore")).size(11))
|
||||
.on_press(Message::LayerStateManagerRestore)
|
||||
.style(button_style(true))
|
||||
} else {
|
||||
button(text("Restore").size(11)).style(button_style(true))
|
||||
button(text(t!("Restore")).size(11)).style(button_style(true))
|
||||
};
|
||||
let delete = if selected_state.is_some() {
|
||||
button(text("Delete").size(11))
|
||||
button(text(t!("Delete")).size(11))
|
||||
.on_press(Message::LayerStateManagerDelete)
|
||||
.style(button::danger)
|
||||
} else {
|
||||
button(text("Delete").size(11)).style(button::danger)
|
||||
button(text(t!("Delete")).size(11)).style(button::danger)
|
||||
};
|
||||
let edit = if selected_state.is_some() {
|
||||
button(text("Edit").size(11))
|
||||
button(text(t!("Edit")).size(11))
|
||||
.on_press(Message::LayerStateManagerEdit)
|
||||
.style(button_style(false))
|
||||
} else {
|
||||
button(text("Edit").size(11)).style(button_style(false))
|
||||
button(text(t!("Edit")).size(11)).style(button_style(false))
|
||||
};
|
||||
|
||||
let right = container(
|
||||
column![
|
||||
row![
|
||||
button(text("New").size(11))
|
||||
button(text(t!("New")).size(11))
|
||||
.on_press(Message::LayerStateManagerNew)
|
||||
.style(button_style(false))
|
||||
.padding([5, 12]),
|
||||
|
|
@ -271,27 +273,27 @@ pub fn view_window<'a>(
|
|||
.spacing(6)
|
||||
.align_y(iced::Center),
|
||||
divider(sizing.width),
|
||||
text("Name").size(10).style(muted),
|
||||
text_input("Layer state name", name)
|
||||
text(t!("Name")).size(10).style(muted),
|
||||
text_input(t!("Layer state name").as_ref(), name)
|
||||
.on_input(Message::LayerStateManagerName)
|
||||
.on_submit(Message::LayerStateManagerSave)
|
||||
.size(11)
|
||||
.padding([5, 8]),
|
||||
text("Description").size(10).style(muted),
|
||||
text_input("Optional description", description)
|
||||
text(t!("Description")).size(10).style(muted),
|
||||
text_input(t!("Optional description").as_ref(), description)
|
||||
.on_input(Message::LayerStateManagerDescription)
|
||||
.size(11)
|
||||
.padding([5, 8]),
|
||||
Space::new().height(8),
|
||||
details,
|
||||
Space::new().height(sizing.height),
|
||||
text("Layer states are stored inside the drawing and remain available after reopening it.")
|
||||
text(t!("Layer states are stored inside the drawing and remain available after reopening it."))
|
||||
.size(10)
|
||||
.style(muted),
|
||||
button(text(if selected_state.is_some() {
|
||||
"Update from Drawing"
|
||||
t!("Update from Drawing")
|
||||
} else {
|
||||
"Save New State"
|
||||
t!("Save New State")
|
||||
})
|
||||
.size(11))
|
||||
.on_press(Message::LayerStateManagerSave)
|
||||
|
|
@ -328,7 +330,7 @@ fn mask_for(property: LayerStateProperty) -> LayerStateMask {
|
|||
|
||||
fn mask_button<'a>(
|
||||
state: &LayerState,
|
||||
label: &'a str,
|
||||
label: Cow<'static, str>,
|
||||
property: LayerStateProperty,
|
||||
) -> Element<'a, Message> {
|
||||
let enabled = state.mask.contains(mask_for(property));
|
||||
|
|
@ -365,17 +367,17 @@ fn bool_cell<'a>(
|
|||
fn editor_header<'a>() -> Element<'a, Message> {
|
||||
container(
|
||||
row![
|
||||
text("Layer").size(10).style(muted).width(Length::Fixed(170.0)),
|
||||
text("On").size(10).style(muted).width(Length::Fixed(44.0)),
|
||||
text("Freeze").size(10).style(muted).width(Length::Fixed(54.0)),
|
||||
text("Lock").size(10).style(muted).width(Length::Fixed(44.0)),
|
||||
text("Plot").size(10).style(muted).width(Length::Fixed(44.0)),
|
||||
text("New VP").size(10).style(muted).width(Length::Fixed(54.0)),
|
||||
text("Color").size(10).style(muted).width(Length::Fixed(135.0)),
|
||||
text("Linetype").size(10).style(muted).width(Length::Fixed(150.0)),
|
||||
text("Lineweight").size(10).style(muted).width(Length::Fixed(115.0)),
|
||||
text("Plot style").size(10).style(muted).width(Length::Fixed(135.0)),
|
||||
text("Transparency").size(10).style(muted).width(Length::Fixed(105.0)),
|
||||
text(t!("Layer")).size(10).style(muted).width(Length::Fixed(170.0)),
|
||||
text(t!("On")).size(10).style(muted).width(Length::Fixed(44.0)),
|
||||
text(t!("Freeze")).size(10).style(muted).width(Length::Fixed(54.0)),
|
||||
text(t!("Lock")).size(10).style(muted).width(Length::Fixed(44.0)),
|
||||
text(t!("Plot")).size(10).style(muted).width(Length::Fixed(44.0)),
|
||||
text(t!("New VP")).size(10).style(muted).width(Length::Fixed(54.0)),
|
||||
text(t!("Color")).size(10).style(muted).width(Length::Fixed(135.0)),
|
||||
text(t!("Linetype")).size(10).style(muted).width(Length::Fixed(150.0)),
|
||||
text(t!("Lineweight")).size(10).style(muted).width(Length::Fixed(115.0)),
|
||||
text(t!("Plot style")).size(10).style(muted).width(Length::Fixed(135.0)),
|
||||
text(t!("Transparency")).size(10).style(muted).width(Length::Fixed(105.0)),
|
||||
]
|
||||
.spacing(4)
|
||||
.align_y(iced::Center),
|
||||
|
|
@ -455,7 +457,7 @@ fn editor_layer_row<'a>(
|
|||
.text_size(11)
|
||||
.padding([3, 5])
|
||||
.width(Length::Fixed(115.0)),
|
||||
text_input("Default", &layer.plot_style)
|
||||
text_input(t!("Default").as_ref(), &layer.plot_style)
|
||||
.on_input(move |value| Message::LayerStateEditorLayerPlotStyle(index, value))
|
||||
.size(11)
|
||||
.padding([3, 5])
|
||||
|
|
@ -492,16 +494,16 @@ pub fn view_editor<'a>(
|
|||
sizing: crate::ui::modal::ModalSizing,
|
||||
) -> Element<'a, Message> {
|
||||
let properties = [
|
||||
("On / Off", LayerStateProperty::On),
|
||||
("Freeze", LayerStateProperty::Frozen),
|
||||
("Lock", LayerStateProperty::Locked),
|
||||
("Plot", LayerStateProperty::Plot),
|
||||
("New VP", LayerStateProperty::NewViewport),
|
||||
("Color", LayerStateProperty::Color),
|
||||
("Linetype", LayerStateProperty::LineType),
|
||||
("Lineweight", LayerStateProperty::LineWeight),
|
||||
("Plot style", LayerStateProperty::PlotStyle),
|
||||
("Transparency", LayerStateProperty::Transparency),
|
||||
(t!("On / Off"), LayerStateProperty::On),
|
||||
(t!("Freeze"), LayerStateProperty::Frozen),
|
||||
(t!("Lock"), LayerStateProperty::Locked),
|
||||
(t!("Plot"), LayerStateProperty::Plot),
|
||||
(t!("New VP"), LayerStateProperty::NewViewport),
|
||||
(t!("Color"), LayerStateProperty::Color),
|
||||
(t!("Linetype"), LayerStateProperty::LineType),
|
||||
(t!("Lineweight"), LayerStateProperty::LineWeight),
|
||||
(t!("Plot style"), LayerStateProperty::PlotStyle),
|
||||
(t!("Transparency"), LayerStateProperty::Transparency),
|
||||
];
|
||||
let mask_controls = properties
|
||||
.into_iter()
|
||||
|
|
@ -534,14 +536,14 @@ pub fn view_editor<'a>(
|
|||
container(
|
||||
column![
|
||||
row![
|
||||
text("Name").size(10).style(muted),
|
||||
text_input("Layer state name", &state.name)
|
||||
text(t!("Name")).size(10).style(muted),
|
||||
text_input(t!("Layer state name").as_ref(), &state.name)
|
||||
.on_input(Message::LayerStateEditorName)
|
||||
.size(11)
|
||||
.padding([3, 6])
|
||||
.width(Length::Fixed(220.0)),
|
||||
text("Description").size(10).style(muted),
|
||||
text_input("Optional description", &state.description)
|
||||
text(t!("Description")).size(10).style(muted),
|
||||
text_input(t!("Optional description").as_ref(), &state.description)
|
||||
.on_input(Message::LayerStateEditorDescription)
|
||||
.size(11)
|
||||
.padding([3, 6])
|
||||
|
|
@ -550,11 +552,11 @@ pub fn view_editor<'a>(
|
|||
.spacing(8)
|
||||
.align_y(iced::Center),
|
||||
row![
|
||||
text(format!("{} saved layers", state.layers.len()))
|
||||
text(t!("%{n} saved layers", n = state.layers.len()))
|
||||
.size(10)
|
||||
.style(muted),
|
||||
Space::new().width(sizing.width),
|
||||
text("Current layer").size(10).style(muted),
|
||||
text(t!("Current layer")).size(10).style(muted),
|
||||
iced::widget::pick_list(
|
||||
Some(state.current_layer.clone()),
|
||||
layer_names,
|
||||
|
|
@ -568,12 +570,12 @@ pub fn view_editor<'a>(
|
|||
.spacing(8)
|
||||
.align_y(iced::Center),
|
||||
divider(sizing.width),
|
||||
text("Properties restored by this state").size(10).style(muted),
|
||||
text(t!("Properties restored by this state")).size(10).style(muted),
|
||||
mask_controls,
|
||||
row![
|
||||
text("Saved layer values").size(12),
|
||||
text(t!("Saved layer values")).size(12),
|
||||
Space::new().width(sizing.width),
|
||||
text_input("Search layers…", filter)
|
||||
text_input(t!("Search layers…").as_ref(), filter)
|
||||
.on_input(Message::LayerStateEditorFilter)
|
||||
.size(11)
|
||||
.padding([4, 7])
|
||||
|
|
@ -595,15 +597,15 @@ pub fn view_editor<'a>(
|
|||
..Default::default()
|
||||
}),
|
||||
row![
|
||||
text("Changes affect the saved state only; the drawing is unchanged until Restore.")
|
||||
text(t!("Changes affect the saved state only; the drawing is unchanged until Restore."))
|
||||
.size(10)
|
||||
.style(muted),
|
||||
Space::new().width(sizing.width),
|
||||
button(text("Cancel").size(11))
|
||||
button(text(t!("Cancel")).size(11))
|
||||
.on_press(Message::LayerStateEditorCancel)
|
||||
.style(button_style(false))
|
||||
.padding([5, 12]),
|
||||
button(text("Save Changes").size(11))
|
||||
button(text(t!("Save Changes")).size(11))
|
||||
.on_press(Message::LayerStateEditorSave)
|
||||
.style(button_style(true))
|
||||
.padding([5, 12]),
|
||||
|
|
|
|||
|
|
@ -13,6 +13,8 @@ use iced::widget::{
|
|||
};
|
||||
use iced::Padding;
|
||||
use iced::{Background, Border, Color, Element, Fill, Length, Theme};
|
||||
use crate::t;
|
||||
use std::borrow::Cow;
|
||||
|
||||
// ── Per-viewport column descriptor ───────────────────────────────────────────
|
||||
|
||||
|
|
@ -330,22 +332,22 @@ impl LayerPanel {
|
|||
// ── Toolbar ───────────────────────────────────────────────────────
|
||||
let toolbar = container(
|
||||
row![
|
||||
toolbar_btn(crate::ui::icons::PLUS, "New", Message::LayerNew),
|
||||
toolbar_btn(crate::ui::icons::PLUS, t!("New"), Message::LayerNew),
|
||||
toolbar_btn_cond(
|
||||
crate::ui::icons::TRASH,
|
||||
"Delete",
|
||||
t!("Delete"),
|
||||
Message::LayerDelete,
|
||||
has_sel && !sel_is_zero,
|
||||
),
|
||||
toolbar_btn_cond(
|
||||
crate::ui::icons::CHECK,
|
||||
"Set Current",
|
||||
t!("Set Current"),
|
||||
Message::LayerSetCurrent,
|
||||
has_sel,
|
||||
),
|
||||
iced::widget::Space::new().width(sizing.width),
|
||||
// Search box: filters rows by name as the user types (#343).
|
||||
text_input("Search…", &self.filter)
|
||||
text_input(t!("Search…").as_ref(), &self.filter)
|
||||
.on_input(Message::LayerManagerFilterChanged)
|
||||
.size(FONT_SZ)
|
||||
.padding([3, 6])
|
||||
|
|
@ -368,8 +370,8 @@ impl LayerPanel {
|
|||
let sc = self.sort_col;
|
||||
let sa = self.sort_asc;
|
||||
let mut header_row = row![
|
||||
text("Status").size(10).style(muted_style).width(50),
|
||||
sortable_header("Name", LayerSortCol::Name, Length::Fixed(name_col_w), sc, sa),
|
||||
text(t!("Status")).size(10).style(muted_style).width(50),
|
||||
sortable_header(t!("Name"), LayerSortCol::Name, Length::Fixed(name_col_w), sc, sa),
|
||||
// Draggable divider: adjusts the Name column width (#359).
|
||||
iced::widget::mouse_area(
|
||||
container(iced::widget::Space::new().width(2).height(14)).style(
|
||||
|
|
@ -383,14 +385,14 @@ impl LayerPanel {
|
|||
)
|
||||
.on_press(Message::LayerNameColGrab)
|
||||
.interaction(iced::mouse::Interaction::ResizingHorizontally),
|
||||
sortable_header("On", LayerSortCol::On, Length::Fixed(COL_ICON), sc, sa),
|
||||
sortable_header("Freeze", LayerSortCol::Freeze, Length::Fixed(COL_ICON), sc, sa),
|
||||
sortable_header("Lock", LayerSortCol::Lock, Length::Fixed(COL_ICON), sc, sa),
|
||||
sortable_header("Color", LayerSortCol::Color, Length::Fixed(COL_COLOR), sc, sa),
|
||||
sortable_header("Linetype", LayerSortCol::Linetype, Length::Fixed(COL_LT), sc, sa),
|
||||
sortable_header("Lineweight", LayerSortCol::Lineweight, Length::Fixed(COL_LW), sc, sa),
|
||||
sortable_header(t!("On"), LayerSortCol::On, Length::Fixed(COL_ICON), sc, sa),
|
||||
sortable_header(t!("Freeze"), LayerSortCol::Freeze, Length::Fixed(COL_ICON), sc, sa),
|
||||
sortable_header(t!("Lock"), LayerSortCol::Lock, Length::Fixed(COL_ICON), sc, sa),
|
||||
sortable_header(t!("Color"), LayerSortCol::Color, Length::Fixed(COL_COLOR), sc, sa),
|
||||
sortable_header(t!("Linetype"), LayerSortCol::Linetype, Length::Fixed(COL_LT), sc, sa),
|
||||
sortable_header(t!("Lineweight"), LayerSortCol::Lineweight, Length::Fixed(COL_LW), sc, sa),
|
||||
sortable_header(
|
||||
"Transparency",
|
||||
t!("Transparency"),
|
||||
LayerSortCol::Transparency,
|
||||
Length::Fixed(COL_TRANS),
|
||||
sc,
|
||||
|
|
@ -535,7 +537,7 @@ fn color_sort_key(c: AcadColor) -> u32 {
|
|||
/// A clickable column header that sorts the table by `col`. Shows an up/down
|
||||
/// SVG arrow when it is the active sort column (#133).
|
||||
fn sortable_header<'a>(
|
||||
label: &'a str,
|
||||
label: Cow<'static, str>,
|
||||
col: LayerSortCol,
|
||||
width: Length,
|
||||
active: Option<LayerSortCol>,
|
||||
|
|
@ -566,7 +568,7 @@ fn sortable_header<'a>(
|
|||
|
||||
// ── Toolbar buttons ───────────────────────────────────────────────────────
|
||||
|
||||
fn toolbar_btn<'a>(icon: &'static [u8], label: &'a str, msg: Message) -> Element<'a, Message> {
|
||||
fn toolbar_btn<'a>(icon: &'static [u8], label: Cow<'static, str>, msg: Message) -> Element<'a, Message> {
|
||||
button(
|
||||
row![
|
||||
crate::ui::icons::themed(icon, 12.0),
|
||||
|
|
@ -601,7 +603,7 @@ fn toolbar_btn<'a>(icon: &'static [u8], label: &'a str, msg: Message) -> Element
|
|||
|
||||
fn toolbar_btn_cond<'a>(
|
||||
icon: &'static [u8],
|
||||
label: &'a str,
|
||||
label: Cow<'static, str>,
|
||||
msg: Message,
|
||||
enabled: bool,
|
||||
) -> Element<'a, Message> {
|
||||
|
|
@ -791,7 +793,7 @@ fn layer_row<'a>(
|
|||
let lt_cell: Element<'_, Message> = if let Some(state) = lt_combo {
|
||||
combo_box(
|
||||
state,
|
||||
"linetype",
|
||||
t!("linetype").as_ref(),
|
||||
Some(&cur_lt_item),
|
||||
|item: LinetypeItem| Message::LayerLinetypeSet(item.name),
|
||||
)
|
||||
|
|
@ -816,7 +818,7 @@ fn layer_row<'a>(
|
|||
// Lineweight cell
|
||||
let cur_lw_item = LwItem(layer.lineweight);
|
||||
let lw_cell: Element<'_, Message> = if let Some(state) = lw_combo_state {
|
||||
combo_box(state, "lineweight", Some(&cur_lw_item), |item: LwItem| {
|
||||
combo_box(state, t!("lineweight").as_ref(), Some(&cur_lw_item), |item: LwItem| {
|
||||
Message::LayerLineweightSet(item.0)
|
||||
})
|
||||
.size(FONT_SZ)
|
||||
|
|
|
|||
|
|
@ -3,6 +3,7 @@
|
|||
use crate::app::Message;
|
||||
use iced::widget::{button, column, container, row, scrollable, text, text_input, Space};
|
||||
use iced::{Background, Element, Theme};
|
||||
use crate::t;
|
||||
|
||||
fn muted_style(theme: &Theme) -> iced::widget::text::Style {
|
||||
iced::widget::text::Style {
|
||||
|
|
@ -74,11 +75,11 @@ pub fn view_window<'a>(
|
|||
// ── Toolbar ───────────────────────────────────────────────────────────
|
||||
let toolbar = container(
|
||||
row![
|
||||
button(text("New Layout").size(11))
|
||||
button(text(t!("New Layout")).size(11))
|
||||
.on_press(Message::LayoutManagerNew)
|
||||
.style(btn_s(false))
|
||||
.padding([4, 10]),
|
||||
button(text("Delete").size(11))
|
||||
button(text(t!("Delete")).size(11))
|
||||
.on_press(Message::LayoutManagerDelete)
|
||||
.style(move |theme: &Theme, status| {
|
||||
if is_model {
|
||||
|
|
@ -92,7 +93,7 @@ pub fn view_window<'a>(
|
|||
button(
|
||||
row![
|
||||
crate::ui::icons::themed_arrow_left(9.0),
|
||||
text("Move Left").size(11),
|
||||
text(t!("Move Left")).size(11),
|
||||
]
|
||||
.spacing(4)
|
||||
.align_y(iced::Center),
|
||||
|
|
@ -102,7 +103,7 @@ pub fn view_window<'a>(
|
|||
.padding([4, 8]),
|
||||
button(
|
||||
row![
|
||||
text("Move Right").size(11),
|
||||
text(t!("Move Right")).size(11),
|
||||
crate::ui::icons::themed_arrow_right(9.0),
|
||||
]
|
||||
.spacing(4)
|
||||
|
|
@ -111,7 +112,7 @@ pub fn view_window<'a>(
|
|||
.on_press(Message::LayoutManagerMoveRight)
|
||||
.style(btn_s(false))
|
||||
.padding([4, 8]),
|
||||
button(text("Set Current").size(11))
|
||||
button(text(t!("Set Current")).size(11))
|
||||
.on_press(Message::LayoutManagerSetCurrent)
|
||||
.style(btn_s(true))
|
||||
.padding([4, 10]),
|
||||
|
|
@ -151,7 +152,7 @@ pub fn view_window<'a>(
|
|||
|
||||
let layout_list = container(
|
||||
column![
|
||||
text("Layouts").size(10).style(muted_style),
|
||||
text(t!("Layouts")).size(10).style(muted_style),
|
||||
container(scrollable(column(list_items).spacing(2)).height(sizing.height))
|
||||
.style(container::bordered_box)
|
||||
.width(sizing.width)
|
||||
|
|
@ -174,24 +175,24 @@ pub fn view_window<'a>(
|
|||
let details = container(
|
||||
column![
|
||||
text(if is_model {
|
||||
"Model Space"
|
||||
t!("Model Space")
|
||||
} else {
|
||||
"Paper Space Layout"
|
||||
t!("Paper Space Layout")
|
||||
})
|
||||
.size(13),
|
||||
Space::new().height(8),
|
||||
row![
|
||||
text("Name:").size(11).style(muted_style).width(80),
|
||||
text(t!("Name:")).size(11).style(muted_style).width(80),
|
||||
text(selected).size(11),
|
||||
]
|
||||
.spacing(8)
|
||||
.align_y(iced::Center),
|
||||
row![
|
||||
text("Status:").size(11).style(muted_style).width(80),
|
||||
text(t!("Status:")).size(11).style(muted_style).width(80),
|
||||
text(if selected == current.as_str() {
|
||||
"Active"
|
||||
t!("Active")
|
||||
} else {
|
||||
"Inactive"
|
||||
t!("Inactive")
|
||||
})
|
||||
.size(11)
|
||||
.style(move |theme: &Theme| {
|
||||
|
|
@ -205,14 +206,14 @@ pub fn view_window<'a>(
|
|||
.spacing(8)
|
||||
.align_y(iced::Center),
|
||||
Space::new().height(16),
|
||||
text("Rename").size(10).style(muted_style),
|
||||
text(t!("Rename")).size(10).style(muted_style),
|
||||
row![
|
||||
text_input("New name…", rename_buf)
|
||||
text_input(t!("New name…").as_ref(), rename_buf)
|
||||
.on_input(Message::LayoutManagerRenameBuf)
|
||||
.on_submit(Message::LayoutManagerRenameCommit)
|
||||
.size(11)
|
||||
.padding([4, 8]),
|
||||
button(text("OK").size(11))
|
||||
button(text(t!("OK")).size(11))
|
||||
.on_press(Message::LayoutManagerRenameCommit)
|
||||
.style(btn_s(true))
|
||||
.padding([4, 10]),
|
||||
|
|
|
|||
|
|
@ -6,6 +6,8 @@
|
|||
use iced::time::Instant;
|
||||
use iced::widget::{button, column, container, row, stack, text, Space};
|
||||
use iced::{Background, Border, Element, Fill, Length, Theme};
|
||||
use crate::t;
|
||||
use std::borrow::Cow;
|
||||
use std::sync::atomic::Ordering;
|
||||
|
||||
use crate::app::{
|
||||
|
|
@ -17,14 +19,14 @@ const CARD_WIDTH: f32 = 420.0;
|
|||
const BAR_TRACK_WIDTH: f32 = 380.0;
|
||||
const BAR_TRACK_HEIGHT: f32 = 6.0;
|
||||
|
||||
fn phase_label(phase: u8) -> &'static str {
|
||||
fn phase_label(phase: u8) -> Cow<'static, str> {
|
||||
match phase {
|
||||
OPEN_PHASE_READING => "Reading file…",
|
||||
OPEN_PHASE_PARSING => "Parsing entities…",
|
||||
OPEN_PHASE_XREF => "Loading references…",
|
||||
OPEN_PHASE_CACHING => "Building scene caches…",
|
||||
OPEN_PHASE_FINALIZING => "Finalizing…",
|
||||
_ => "Working…",
|
||||
OPEN_PHASE_READING => t!("Reading file…"),
|
||||
OPEN_PHASE_PARSING => t!("Parsing entities…"),
|
||||
OPEN_PHASE_XREF => t!("Loading references…"),
|
||||
OPEN_PHASE_CACHING => t!("Building scene caches…"),
|
||||
OPEN_PHASE_FINALIZING => t!("Finalizing…"),
|
||||
_ => t!("Working…"),
|
||||
}
|
||||
}
|
||||
|
||||
|
|
@ -101,7 +103,7 @@ pub fn view<'a>(progress: &'a OpenProgress, _now: Instant) -> Element<'a, Messag
|
|||
.into();
|
||||
|
||||
// ── Card body ────────────────────────────────────────────────────────
|
||||
let title = text("Opening file").size(15);
|
||||
let title = text(t!("Opening file")).size(15);
|
||||
|
||||
let name_line = text(format!(
|
||||
"{} ({})",
|
||||
|
|
@ -123,7 +125,7 @@ pub fn view<'a>(progress: &'a OpenProgress, _now: Instant) -> Element<'a, Messag
|
|||
color: Some(theme.palette().primary.base.color),
|
||||
});
|
||||
|
||||
let cancel_btn: Element<'_, Message> = button(text("Cancel").size(12))
|
||||
let cancel_btn: Element<'_, Message> = button(text(t!("Cancel")).size(12))
|
||||
.on_press(Message::OpenCancel)
|
||||
.style(button::danger)
|
||||
.padding([4, 14])
|
||||
|
|
|
|||
|
|
@ -4,6 +4,22 @@ use iced::widget::{
|
|||
button, column, container, row, scrollable, text, text_input, Space,
|
||||
};
|
||||
use iced::{Background, Border, Element, Theme};
|
||||
use std::fmt;
|
||||
|
||||
#[derive(Clone, Debug, PartialEq, Eq)]
|
||||
struct ThemeChoice(String);
|
||||
|
||||
impl fmt::Display for ThemeChoice {
|
||||
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
|
||||
let label = match self.0.as_str() {
|
||||
"Light" => crate::t!("Light"),
|
||||
"Dark" => crate::t!("Dark"),
|
||||
"Custom" => crate::t!("Custom"),
|
||||
_ => std::borrow::Cow::Borrowed(self.0.as_str()),
|
||||
};
|
||||
f.write_str(label.as_ref())
|
||||
}
|
||||
}
|
||||
|
||||
pub fn view_window<'a>(
|
||||
default_save_format: &'a str,
|
||||
|
|
@ -21,8 +37,9 @@ pub fn view_window<'a>(
|
|||
.iter()
|
||||
.map(ToString::to_string)
|
||||
.chain(std::iter::once("Custom".to_string()))
|
||||
.map(ThemeChoice)
|
||||
.collect::<Vec<_>>();
|
||||
let selected_theme = Some(ui_theme.name.clone());
|
||||
let selected_theme = Some(ThemeChoice(ui_theme.name.clone()));
|
||||
|
||||
let palette = ui_theme.palette.to_iced();
|
||||
let colors = [
|
||||
|
|
@ -110,7 +127,7 @@ pub fn view_window<'a>(
|
|||
theme_options,
|
||||
|value| value.to_string(),
|
||||
)
|
||||
.on_select(Message::OptionsThemeChanged)
|
||||
.on_select(|choice| Message::OptionsThemeChanged(choice.0))
|
||||
.width(sizing.width),
|
||||
]
|
||||
.spacing(12)
|
||||
|
|
|
|||
|
|
@ -11,6 +11,9 @@ use iced::widget::{
|
|||
Space,
|
||||
};
|
||||
use iced::{Background, Border, Element, Fit, Length, Theme};
|
||||
use crate::t;
|
||||
use std::borrow::Cow;
|
||||
use std::fmt;
|
||||
|
||||
/// Sentinel entries in the printer dropdown (not real printer names).
|
||||
pub const OUT_DEFAULT: &str = "System default printer";
|
||||
|
|
@ -22,6 +25,35 @@ pub const SETUP_NONE: &str = "<none>";
|
|||
pub const SETUP_PREV: &str = "<previous>";
|
||||
pub const STYLE_NONE: &str = "<none>";
|
||||
|
||||
/// A plot dropdown value keeps its persisted/raw value separate from the
|
||||
/// localized label shown by Iced. Printer names, scale names, paper sizes, and
|
||||
/// style-table file names remain verbatim; built-in choices use the catalog.
|
||||
#[derive(Clone, Debug, Eq, PartialEq)]
|
||||
struct PlotChoice {
|
||||
raw: String,
|
||||
localized: bool,
|
||||
}
|
||||
|
||||
impl PlotChoice {
|
||||
fn raw(value: impl Into<String>) -> Self {
|
||||
Self { raw: value.into(), localized: false }
|
||||
}
|
||||
|
||||
fn localized(value: impl Into<String>) -> Self {
|
||||
Self { raw: value.into(), localized: true }
|
||||
}
|
||||
}
|
||||
|
||||
impl fmt::Display for PlotChoice {
|
||||
fn fmt(&self, formatter: &mut fmt::Formatter<'_>) -> fmt::Result {
|
||||
if self.localized {
|
||||
formatter.write_str(crate::i18n::translate(&self.raw).as_ref())
|
||||
} else {
|
||||
formatter.write_str(&self.raw)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// One of the many boolean plot options (folded into a single message so the
|
||||
/// dialog needn't carry a variant per checkbox).
|
||||
#[derive(Debug, Clone, Copy)]
|
||||
|
|
@ -280,7 +312,7 @@ fn hdivider<'a>(width: Length) -> Element<'a, Message> {
|
|||
.into()
|
||||
}
|
||||
|
||||
fn section_label<'a>(s: &'static str) -> Element<'a, Message> {
|
||||
fn section_label<'a>(s: Cow<'static, str>) -> Element<'a, Message> {
|
||||
text(s).size(11).style(muted_style).into()
|
||||
}
|
||||
|
||||
|
|
@ -314,7 +346,12 @@ fn setup_row<'a>(
|
|||
.into();
|
||||
}
|
||||
let is_selected = name == selected;
|
||||
let cell = container(text(name.to_string()).size(11))
|
||||
let display_name = if name == SETUP_NONE || name == SETUP_PREV {
|
||||
crate::i18n::translate(name)
|
||||
} else {
|
||||
Cow::Owned(name.to_string())
|
||||
};
|
||||
let cell = container(text(display_name).size(11))
|
||||
.padding([4, 8])
|
||||
.width(Fit)
|
||||
.style(move |theme: &Theme| {
|
||||
|
|
@ -334,14 +371,14 @@ fn setup_row<'a>(
|
|||
/// A `label : dropdown` row. `ctor` turns the picked string into a dialog
|
||||
/// message.
|
||||
fn drop_row<'a>(
|
||||
label: &'a str,
|
||||
options: Vec<String>,
|
||||
selected: Option<String>,
|
||||
label: Cow<'static, str>,
|
||||
options: Vec<PlotChoice>,
|
||||
selected: Option<PlotChoice>,
|
||||
ctor: fn(String) -> PlotDlgMsg,
|
||||
width: Length,
|
||||
) -> Element<'a, Message> {
|
||||
let pl = iced::widget::pick_list(selected, options, |value| value.to_string())
|
||||
.on_select(move |s| Message::PlotDlg(ctor(s)))
|
||||
.on_select(move |choice| Message::PlotDlg(ctor(choice.raw)))
|
||||
.text_size(12)
|
||||
.padding([3, 6])
|
||||
.width(width);
|
||||
|
|
@ -352,9 +389,9 @@ fn drop_row<'a>(
|
|||
}
|
||||
|
||||
fn drop_row_enabled<'a>(
|
||||
label: &'a str,
|
||||
options: Vec<String>,
|
||||
selected: Option<String>,
|
||||
label: Cow<'static, str>,
|
||||
options: Vec<PlotChoice>,
|
||||
selected: Option<PlotChoice>,
|
||||
ctor: fn(String) -> PlotDlgMsg,
|
||||
width: Length,
|
||||
enabled: bool,
|
||||
|
|
@ -364,7 +401,7 @@ fn drop_row_enabled<'a>(
|
|||
}
|
||||
row![
|
||||
text(label).size(11).style(muted_style).width(92),
|
||||
container(text(selected.unwrap_or_default()).size(12).style(muted_style))
|
||||
container(text(selected.map(|choice| choice.to_string()).unwrap_or_default()).size(12).style(muted_style))
|
||||
.padding([4, 7])
|
||||
.width(width),
|
||||
]
|
||||
|
|
@ -375,7 +412,7 @@ fn drop_row_enabled<'a>(
|
|||
|
||||
/// A `label : text field` row.
|
||||
fn field_row<'a>(
|
||||
label: &'a str,
|
||||
label: Cow<'static, str>,
|
||||
value: &'a str,
|
||||
ctor: fn(String) -> PlotDlgMsg,
|
||||
width: u16,
|
||||
|
|
@ -394,7 +431,7 @@ fn field_row<'a>(
|
|||
}
|
||||
|
||||
fn field_row_enabled<'a>(
|
||||
label: &'a str,
|
||||
label: Cow<'static, str>,
|
||||
value: &'a str,
|
||||
ctor: fn(String) -> PlotDlgMsg,
|
||||
width: u16,
|
||||
|
|
@ -415,7 +452,7 @@ fn field_row_enabled<'a>(
|
|||
}
|
||||
|
||||
/// A single option checkbox bound to a `PlotFlag`.
|
||||
fn check<'a>(label: &'a str, on: bool, flag: PlotFlag) -> Element<'a, Message> {
|
||||
fn check<'a>(label: Cow<'static, str>, on: bool, flag: PlotFlag) -> Element<'a, Message> {
|
||||
checkbox(on)
|
||||
.label(label)
|
||||
.on_toggle(move |_| Message::PlotDlg(PlotDlgMsg::Flag(flag)))
|
||||
|
|
@ -425,7 +462,7 @@ fn check<'a>(label: &'a str, on: bool, flag: PlotFlag) -> Element<'a, Message> {
|
|||
}
|
||||
|
||||
fn check_enabled<'a>(
|
||||
label: &'a str,
|
||||
label: Cow<'static, str>,
|
||||
on: bool,
|
||||
flag: PlotFlag,
|
||||
enabled: bool,
|
||||
|
|
@ -447,8 +484,8 @@ fn panel<'a>(content: impl Into<Element<'a, Message>>) -> Element<'a, Message> {
|
|||
.into()
|
||||
}
|
||||
|
||||
fn strs(items: &[&str]) -> Vec<String> {
|
||||
items.iter().map(|s| s.to_string()).collect()
|
||||
fn choices(items: &[&str]) -> Vec<PlotChoice> {
|
||||
items.iter().map(|&value| PlotChoice::localized(value)).collect()
|
||||
}
|
||||
|
||||
pub fn view_window(
|
||||
|
|
@ -457,7 +494,7 @@ pub fn view_window(
|
|||
) -> Element<'_, Message> {
|
||||
let width = sizing.width;
|
||||
let height = sizing.height;
|
||||
let action = if s.to_file { "Export PDF" } else { "Print" };
|
||||
let action = if s.to_file { t!("Export PDF") } else { t!("Print") };
|
||||
let is_special = s.selected_setup == SETUP_NONE || s.selected_setup == SETUP_PREV;
|
||||
let sel_is_layout = s.selected_setup.len() >= 2
|
||||
&& s.selected_setup.starts_with('*')
|
||||
|
|
@ -478,7 +515,7 @@ pub fn view_window(
|
|||
.map(|name| setup_row(name, &s.selected_setup, renaming, rename_buf))
|
||||
.collect();
|
||||
let list_body: Element<'_, Message> = if rows.is_empty() {
|
||||
container(text("(no page setups)").size(11).style(muted_style))
|
||||
container(text(t!("(no page setups)")).size(11).style(muted_style))
|
||||
.padding([6, 8])
|
||||
.into()
|
||||
} else {
|
||||
|
|
@ -494,7 +531,7 @@ pub fn view_window(
|
|||
.size(11)
|
||||
.padding([4, 8])
|
||||
.width(Length::Fill),
|
||||
button(text("Save").size(11))
|
||||
button(text(t!("Save")).size(11))
|
||||
.on_press(Message::PlotDlg(PlotDlgMsg::NameCommit))
|
||||
.style(btn(true))
|
||||
.padding([4, 8]),
|
||||
|
|
@ -510,7 +547,7 @@ pub fn view_window(
|
|||
};
|
||||
let list_panel = container(
|
||||
column![
|
||||
text("Page setups").size(10).style(muted_style),
|
||||
text(t!("Page setups")).size(10).style(muted_style),
|
||||
new_name,
|
||||
container(list_body)
|
||||
.style(|theme: &Theme| {
|
||||
|
|
@ -541,20 +578,20 @@ pub fn view_window(
|
|||
left: 12.0,
|
||||
});
|
||||
|
||||
let mut copy_button = button(text("Copy").size(11))
|
||||
let mut copy_button = button(text(t!("Copy")).size(11))
|
||||
.style(btn(false))
|
||||
.padding([4, 12]);
|
||||
if can_copy {
|
||||
copy_button = copy_button.on_press(Message::PlotDlg(PlotDlgMsg::CopySetup));
|
||||
}
|
||||
let mut delete_button = button(text("Delete").size(11))
|
||||
let mut delete_button = button(text(t!("Delete")).size(11))
|
||||
.style(btn(false))
|
||||
.padding([4, 12]);
|
||||
if is_named {
|
||||
delete_button = delete_button.on_press(Message::PlotDlg(PlotDlgMsg::DeleteSetup));
|
||||
}
|
||||
let left_bar = row![
|
||||
button(text("New").size(11))
|
||||
button(text(t!("New")).size(11))
|
||||
.on_press(Message::PlotDlg(PlotDlgMsg::NewSetup))
|
||||
.style(btn(false))
|
||||
.padding([4, 12]),
|
||||
|
|
@ -564,17 +601,20 @@ pub fn view_window(
|
|||
.spacing(4);
|
||||
|
||||
// ── Printer / plotter ─────────────────────────────────────────────────
|
||||
let mut printer_opts = vec![OUT_DEFAULT.to_string()];
|
||||
printer_opts.extend(s.printers.iter().cloned());
|
||||
printer_opts.push(OUT_PDF.to_string());
|
||||
let mut printer_opts = vec![PlotChoice::localized(OUT_DEFAULT)];
|
||||
printer_opts.extend(s.printers.iter().cloned().map(PlotChoice::raw));
|
||||
printer_opts.push(PlotChoice::localized(OUT_PDF));
|
||||
let printer_sel = if s.to_file {
|
||||
Some(OUT_PDF.to_string())
|
||||
Some(PlotChoice::localized(OUT_PDF))
|
||||
} else {
|
||||
Some(s.printer.clone().unwrap_or_else(|| OUT_DEFAULT.to_string()))
|
||||
Some(match &s.printer {
|
||||
Some(printer) => PlotChoice::raw(printer.clone()),
|
||||
None => PlotChoice::localized(OUT_DEFAULT),
|
||||
})
|
||||
};
|
||||
let paper_opts: Vec<String> = PaperSize::ALL.iter().map(|p| p.label().to_string()).collect();
|
||||
let paper_note: Element<'_, Message> = if s.area == "Layout" {
|
||||
text("Layout plots the current sheet using the selected paper size.")
|
||||
text(t!("Layout plots the current sheet using the selected paper size."))
|
||||
.size(10)
|
||||
.style(muted_style)
|
||||
.width(width)
|
||||
|
|
@ -583,9 +623,9 @@ pub fn view_window(
|
|||
Space::new().height(0).into()
|
||||
};
|
||||
let mut output_row = row![
|
||||
text("Output").size(11).style(muted_style).width(92),
|
||||
text(t!("Output")).size(11).style(muted_style).width(92),
|
||||
iced::widget::pick_list(printer_sel, printer_opts, |value| value.to_string())
|
||||
.on_select(|value| Message::PlotDlg(PlotDlgMsg::Printer(value)))
|
||||
.on_select(|choice| Message::PlotDlg(PlotDlgMsg::Printer(choice.raw)))
|
||||
.text_size(12)
|
||||
.padding([3, 6])
|
||||
.width(Length::Fill),
|
||||
|
|
@ -594,7 +634,7 @@ pub fn view_window(
|
|||
.align_y(iced::Center);
|
||||
if !s.to_file {
|
||||
output_row = output_row.push(
|
||||
button(text("Properties…").size(11))
|
||||
button(text(t!("Properties…")).size(11))
|
||||
.on_press(Message::PlotDlg(PlotDlgMsg::PrinterProperties))
|
||||
.style(btn(false))
|
||||
.padding([4, 8]),
|
||||
|
|
@ -603,11 +643,11 @@ pub fn view_window(
|
|||
let copies_row: Element<'_, Message> = if s.to_file {
|
||||
Space::new().height(0).into()
|
||||
} else {
|
||||
field_row("Copies", &s.copies, PlotDlgMsg::Copies, 60)
|
||||
field_row(t!("Copies"), &s.copies, PlotDlgMsg::Copies, 60)
|
||||
};
|
||||
let printer_panel = panel(
|
||||
column![
|
||||
section_label("Printer / plotter"),
|
||||
section_label(t!("Printer / plotter")),
|
||||
output_row,
|
||||
copies_row,
|
||||
]
|
||||
|
|
@ -616,19 +656,29 @@ pub fn view_window(
|
|||
|
||||
// ── Paper, area, offset, scale ────────────────────────────────────────
|
||||
let paper_panel = panel(column![
|
||||
section_label("Paper"),
|
||||
drop_row("Size", paper_opts, Some(s.paper.clone()), PlotDlgMsg::Paper, width),
|
||||
section_label(t!("Paper")),
|
||||
drop_row(
|
||||
t!("Size"),
|
||||
paper_opts.into_iter().map(PlotChoice::raw).collect(),
|
||||
Some(PlotChoice::raw(s.paper.clone())),
|
||||
PlotDlgMsg::Paper,
|
||||
width,
|
||||
),
|
||||
paper_note,
|
||||
].spacing(7));
|
||||
|
||||
let mut area_options = strs(&["Extents", "Display", "Window"]);
|
||||
let mut area_options = choices(&["Extents", "Display", "Window"]);
|
||||
if s.paper_space {
|
||||
area_options.insert(0, "Layout".to_string());
|
||||
area_options.insert(0, PlotChoice::localized("Layout"));
|
||||
}
|
||||
let mut area_row = row![
|
||||
text("What to plot").size(11).style(muted_style).width(92),
|
||||
iced::widget::pick_list(Some(s.area.clone()), area_options, |value| value.to_string())
|
||||
.on_select(|value| Message::PlotDlg(PlotDlgMsg::Area(value)))
|
||||
text(t!("What to plot")).size(11).style(muted_style).width(92),
|
||||
iced::widget::pick_list(
|
||||
Some(PlotChoice::localized(s.area.clone())),
|
||||
area_options,
|
||||
|value| value.to_string(),
|
||||
)
|
||||
.on_select(|choice| Message::PlotDlg(PlotDlgMsg::Area(choice.raw)))
|
||||
.text_size(12)
|
||||
.padding([3, 6])
|
||||
.width(Length::Fill),
|
||||
|
|
@ -637,7 +687,7 @@ pub fn view_window(
|
|||
.align_y(iced::Center);
|
||||
if s.area == "Window" {
|
||||
area_row = area_row.push(
|
||||
button(text("Pick…").size(11))
|
||||
button(text(t!("Pick…")).size(11))
|
||||
.on_press(Message::PlotDlg(PlotDlgMsg::PickWindow))
|
||||
.style(btn(false))
|
||||
.padding([4, 10]),
|
||||
|
|
@ -645,35 +695,41 @@ pub fn view_window(
|
|||
}
|
||||
let common_area = s.area != "Layout";
|
||||
let area_panel = panel(column![
|
||||
section_label("Plot area"),
|
||||
section_label(t!("Plot area")),
|
||||
area_row,
|
||||
section_label("Plot offset"),
|
||||
section_label(t!("Plot offset")),
|
||||
column![
|
||||
field_row_enabled("X (mm)", &s.offset_x, PlotDlgMsg::OffsetX, 70, common_area && !s.center),
|
||||
field_row_enabled("Y (mm)", &s.offset_y, PlotDlgMsg::OffsetY, 70, common_area && !s.center),
|
||||
field_row_enabled(t!("X (mm)"), &s.offset_x, PlotDlgMsg::OffsetX, 70, common_area && !s.center),
|
||||
field_row_enabled(t!("Y (mm)"), &s.offset_y, PlotDlgMsg::OffsetY, 70, common_area && !s.center),
|
||||
]
|
||||
.spacing(7),
|
||||
check_enabled("Center the plot", s.center, PlotFlag::Center, common_area),
|
||||
check_enabled(t!("Center the plot"), s.center, PlotFlag::Center, common_area),
|
||||
].spacing(7));
|
||||
let scale_options = s.scales.iter().map(|(name, _)| name.clone()).collect();
|
||||
let scale_options = s
|
||||
.scales
|
||||
.iter()
|
||||
.map(|(name, _)| PlotChoice::raw(name.clone()))
|
||||
.collect();
|
||||
let scale_panel = panel(column![
|
||||
section_label("Plot scale"),
|
||||
section_label(t!("Plot scale")),
|
||||
check_enabled(
|
||||
"Fit to paper",
|
||||
t!("Fit to paper"),
|
||||
s.fit_to_paper,
|
||||
PlotFlag::FitToPaper,
|
||||
common_area,
|
||||
),
|
||||
// "Scale" intentionally stays untranslated: it would clash with the
|
||||
// ribbon's zoom tool label under the same lookup key.
|
||||
drop_row_enabled(
|
||||
"Scale",
|
||||
Cow::Borrowed("Scale"),
|
||||
scale_options,
|
||||
Some(s.scale.clone()),
|
||||
Some(PlotChoice::raw(s.scale.clone())),
|
||||
PlotDlgMsg::Scale,
|
||||
width,
|
||||
common_area && !s.fit_to_paper,
|
||||
),
|
||||
check_enabled(
|
||||
"Scale lineweights",
|
||||
t!("Scale lineweights"),
|
||||
s.scale_lw && !s.fit_to_paper,
|
||||
PlotFlag::ScaleLw,
|
||||
!s.fit_to_paper,
|
||||
|
|
@ -681,35 +737,35 @@ pub fn view_window(
|
|||
].spacing(7));
|
||||
|
||||
// ── Style and shaded viewport settings ───────────────────────────────
|
||||
let mut style_options = vec![STYLE_NONE.to_string()];
|
||||
style_options.extend(s.plot_styles.iter().cloned());
|
||||
let mut style_options = vec![PlotChoice::localized(STYLE_NONE)];
|
||||
style_options.extend(s.plot_styles.iter().cloned().map(PlotChoice::raw));
|
||||
if !s.style_name.is_empty()
|
||||
&& !style_options
|
||||
.iter()
|
||||
.any(|name| name.eq_ignore_ascii_case(&s.style_name))
|
||||
.any(|choice| choice.raw.eq_ignore_ascii_case(&s.style_name))
|
||||
{
|
||||
style_options.push(s.style_name.clone());
|
||||
style_options.push(PlotChoice::raw(s.style_name.clone()));
|
||||
}
|
||||
let style_selected = if s.style_name.is_empty() {
|
||||
STYLE_NONE.to_string()
|
||||
PlotChoice::localized(STYLE_NONE)
|
||||
} else {
|
||||
s.style_name.clone()
|
||||
PlotChoice::raw(s.style_name.clone())
|
||||
};
|
||||
let style_panel = panel(column![
|
||||
section_label("Plot style table (pen assignments)"),
|
||||
section_label(t!("Plot style table (pen assignments)")),
|
||||
drop_row(
|
||||
"Table",
|
||||
t!("Table"),
|
||||
style_options,
|
||||
Some(style_selected),
|
||||
PlotDlgMsg::Style,
|
||||
width,
|
||||
),
|
||||
row![
|
||||
button(text("Load…").size(11))
|
||||
button(text(t!("Load…")).size(11))
|
||||
.on_press(Message::PlotDlg(PlotDlgMsg::LoadStyle))
|
||||
.style(btn(false))
|
||||
.padding([4, 10]),
|
||||
button(text("Save…").size(11))
|
||||
button(text(t!("Save…")).size(11))
|
||||
.on_press(Message::PlotDlg(PlotDlgMsg::SaveStyle))
|
||||
.style(btn(false))
|
||||
.padding([4, 10]),
|
||||
|
|
@ -719,10 +775,10 @@ pub fn view_window(
|
|||
].spacing(7));
|
||||
|
||||
let shaded_panel = panel(column![
|
||||
section_label("Shaded viewport options"),
|
||||
section_label(t!("Shaded viewport options")),
|
||||
drop_row(
|
||||
"Shade plot",
|
||||
strs(&[
|
||||
t!("Shade plot"),
|
||||
choices(&[
|
||||
"As displayed",
|
||||
"2D Wireframe",
|
||||
"3D Wireframe",
|
||||
|
|
@ -732,14 +788,14 @@ pub fn view_window(
|
|||
"Flat Shaded + Edges",
|
||||
"Gouraud Shaded + Edges",
|
||||
]),
|
||||
Some(s.shade.clone()),
|
||||
Some(PlotChoice::localized(s.shade.clone())),
|
||||
PlotDlgMsg::Shade,
|
||||
width,
|
||||
),
|
||||
drop_row(
|
||||
"Quality",
|
||||
strs(&["Low", "Normal", "High"]),
|
||||
Some(s.quality.clone()),
|
||||
t!("Quality"),
|
||||
choices(&["Low", "Normal", "High"]),
|
||||
Some(PlotChoice::localized(s.quality.clone())),
|
||||
PlotDlgMsg::Quality,
|
||||
width,
|
||||
),
|
||||
|
|
@ -748,7 +804,7 @@ pub fn view_window(
|
|||
// ── Output options and orientation ────────────────────────────────────
|
||||
let paper_order_option: Element<'_, Message> = if s.paper_space {
|
||||
check(
|
||||
"Paper space last",
|
||||
t!("Paper space last"),
|
||||
s.paperspace_last,
|
||||
PlotFlag::PaperspaceLast,
|
||||
)
|
||||
|
|
@ -756,19 +812,19 @@ pub fn view_window(
|
|||
Space::new().height(0).into()
|
||||
};
|
||||
let options_panel = panel(column![
|
||||
section_label("Plot options"),
|
||||
section_label(t!("Plot options")),
|
||||
row![
|
||||
column![
|
||||
check("Plot in background", s.background, PlotFlag::Background),
|
||||
check("Object lineweights", s.lineweights, PlotFlag::Lineweights),
|
||||
check("Plot transparency", s.transparency, PlotFlag::Transparency),
|
||||
check(t!("Plot in background"), s.background, PlotFlag::Background),
|
||||
check(t!("Object lineweights"), s.lineweights, PlotFlag::Lineweights),
|
||||
check(t!("Plot transparency"), s.transparency, PlotFlag::Transparency),
|
||||
]
|
||||
.spacing(6)
|
||||
.width(width),
|
||||
column![
|
||||
paper_order_option,
|
||||
check("Merge overlapping lines", s.merge_lines, PlotFlag::MergeLines),
|
||||
check("Plot stamp", s.stamp, PlotFlag::Stamp),
|
||||
check(t!("Merge overlapping lines"), s.merge_lines, PlotFlag::MergeLines),
|
||||
check(t!("Plot stamp"), s.stamp, PlotFlag::Stamp),
|
||||
]
|
||||
.spacing(6)
|
||||
.width(width),
|
||||
|
|
@ -778,13 +834,13 @@ pub fn view_window(
|
|||
|
||||
let orientation_panel = panel(column![
|
||||
drop_row(
|
||||
"Orientation",
|
||||
strs(&["Portrait", "Landscape"]),
|
||||
Some(s.orientation.clone()),
|
||||
t!("Orientation"),
|
||||
choices(&["Portrait", "Landscape"]),
|
||||
Some(PlotChoice::localized(s.orientation.clone())),
|
||||
PlotDlgMsg::Orientation,
|
||||
width,
|
||||
),
|
||||
check("Plot upside-down", s.upside_down, PlotFlag::UpsideDown),
|
||||
check(t!("Plot upside-down"), s.upside_down, PlotFlag::UpsideDown),
|
||||
].spacing(7));
|
||||
|
||||
let left = column![
|
||||
|
|
@ -820,12 +876,12 @@ pub fn view_window(
|
|||
row![
|
||||
left_bar,
|
||||
Space::new().width(width),
|
||||
button(text("Set current").size(11))
|
||||
button(text(t!("Set current")).size(11))
|
||||
.on_press(Message::PlotDlg(PlotDlgMsg::SetCurrent))
|
||||
.style(btn(false))
|
||||
.padding([4, 12]),
|
||||
Space::new().width(6),
|
||||
button(text("Preview").size(11))
|
||||
button(text(t!("Preview")).size(11))
|
||||
.on_press(Message::PlotDlg(PlotDlgMsg::Preview))
|
||||
.style(btn(false))
|
||||
.padding([4, 12]),
|
||||
|
|
|
|||
|
|
@ -12,7 +12,9 @@ use iced::widget::{
|
|||
button, column, container, markdown, row, rule, scrollable, text, text_input, Space,
|
||||
};
|
||||
use iced::{Background, Border, Element, Fill, Length, Theme};
|
||||
use crate::t;
|
||||
use rustc_hash::{FxHashMap, FxHashSet};
|
||||
use std::borrow::Cow;
|
||||
|
||||
/// Empty lane kept at the right edge of scroll content so the vertical
|
||||
/// scrollbar never covers card controls.
|
||||
|
|
@ -79,9 +81,9 @@ fn badge<'a>(label: String) -> Element<'a, Message> {
|
|||
fn toggle_button<'a>(id: &str, disabled: bool) -> Element<'a, Message> {
|
||||
// Label shows the action the click performs.
|
||||
let label = if disabled {
|
||||
"Enable"
|
||||
t!("Enable")
|
||||
} else {
|
||||
"Disable"
|
||||
t!("Disable")
|
||||
};
|
||||
let want_enabled = disabled; // clicking flips the state
|
||||
let id_owned = id.to_string();
|
||||
|
|
@ -101,8 +103,8 @@ enum StatusKind {
|
|||
}
|
||||
|
||||
/// Coloured status pill for a discovered external package.
|
||||
fn status_badge<'a>(label: &str, kind: StatusKind) -> Element<'a, Message> {
|
||||
container(text(label.to_string()).size(11))
|
||||
fn status_badge<'a>(label: Cow<'static, str>, kind: StatusKind) -> Element<'a, Message> {
|
||||
container(text(label).size(11))
|
||||
.padding([2, 8])
|
||||
.style(move |theme: &Theme| {
|
||||
let palette = theme.palette();
|
||||
|
|
@ -219,17 +221,17 @@ fn external_card<'a>(
|
|||
let failed_old_api =
|
||||
load_error.is_some() && p.api_version != ocs_plugin_api::API_VERSION;
|
||||
let (status, kind) = if loaded && disabled {
|
||||
("Disabled", StatusKind::Muted)
|
||||
(t!("Disabled"), StatusKind::Muted)
|
||||
} else if loaded {
|
||||
("Loaded", StatusKind::Success)
|
||||
(t!("Loaded"), StatusKind::Success)
|
||||
} else if !p.api_compatible() || failed_old_api {
|
||||
("API incompatible", StatusKind::Danger)
|
||||
(t!("API incompatible"), StatusKind::Danger)
|
||||
} else if load_error.is_some() {
|
||||
("Load failed", StatusKind::Danger)
|
||||
(t!("Load failed"), StatusKind::Danger)
|
||||
} else if !p.lib_present {
|
||||
("No library", StatusKind::Warning)
|
||||
(t!("No library"), StatusKind::Warning)
|
||||
} else {
|
||||
("Restart to load", StatusKind::Warning)
|
||||
(t!("Restart to load"), StatusKind::Warning)
|
||||
};
|
||||
let header = row![
|
||||
text(p.name.clone()).size(15),
|
||||
|
|
@ -249,21 +251,23 @@ fn external_card<'a>(
|
|||
}
|
||||
if !p.command_prefixes.is_empty() {
|
||||
info_body = info_body.push(
|
||||
text(format!("Commands: {}", p.command_prefixes.join(", ")))
|
||||
.size(11)
|
||||
.style(muted_style),
|
||||
text(t!(
|
||||
"Commands: %{cmds}",
|
||||
cmds = p.command_prefixes.join(", ")
|
||||
))
|
||||
.size(11)
|
||||
.style(muted_style),
|
||||
);
|
||||
}
|
||||
if let Some(error) = load_error {
|
||||
let detail = if failed_old_api {
|
||||
format!(
|
||||
"Plugin API {} is incompatible with this build's API {} ribbon ABI. \
|
||||
Update or rebuild the plugin.",
|
||||
p.api_version,
|
||||
ocs_plugin_api::API_VERSION,
|
||||
t!(
|
||||
"Plugin API %{plugin} is incompatible with this build's API %{build} ribbon ABI. Update or rebuild the plugin.",
|
||||
plugin = p.api_version,
|
||||
build = ocs_plugin_api::API_VERSION,
|
||||
)
|
||||
} else {
|
||||
format!("Load failed: {error}")
|
||||
t!("Load failed: %{error}", error = error)
|
||||
};
|
||||
info_body = info_body.push(
|
||||
text(detail)
|
||||
|
|
@ -288,9 +292,8 @@ fn external_card<'a>(
|
|||
|
||||
let mut actions = row![Space::new().width(Fill)].align_y(iced::Center);
|
||||
if let (Some(repo), Some(tag)) = (repository, update_tag) {
|
||||
let label = format!("Update to {tag}");
|
||||
actions = actions.push(pill_button(
|
||||
&label,
|
||||
t!("Update to %{tag}", tag = tag),
|
||||
Message::PluginUpdate(repo, tag),
|
||||
button::primary,
|
||||
));
|
||||
|
|
@ -302,7 +305,7 @@ fn external_card<'a>(
|
|||
actions = actions.push(Space::new().width(6));
|
||||
}
|
||||
actions = actions.push(pill_button(
|
||||
"Uninstall",
|
||||
t!("Uninstall"),
|
||||
Message::PluginUninstall(p.id.clone()),
|
||||
button::danger,
|
||||
));
|
||||
|
|
@ -314,11 +317,11 @@ fn external_card<'a>(
|
|||
}
|
||||
|
||||
fn pill_button<'a>(
|
||||
label: &str,
|
||||
label: impl Into<Cow<'static, str>>,
|
||||
msg: Message,
|
||||
style: fn(&Theme, button::Status) -> button::Style,
|
||||
) -> Element<'a, Message> {
|
||||
button(text(label.to_string()).size(12))
|
||||
button(text(label.into()).size(12))
|
||||
.padding([4, 12])
|
||||
.on_press(msg)
|
||||
.style(style)
|
||||
|
|
@ -357,7 +360,7 @@ fn install_controls<'a>(
|
|||
.map(|release| release.api_version)
|
||||
});
|
||||
let picker: Element<'_, Message> = if tags.is_empty() {
|
||||
text("no releases").size(11).style(muted_style).into()
|
||||
text(t!("no releases")).size(11).style(muted_style).into()
|
||||
} else {
|
||||
let r = repo_s.clone();
|
||||
iced::widget::pick_list(selected, tags, |value| value.to_string())
|
||||
|
|
@ -368,13 +371,13 @@ fn install_controls<'a>(
|
|||
let action = match selected_api {
|
||||
Some(api_version) if api_version == ocs_plugin_api::API_VERSION => {
|
||||
pill_button(
|
||||
"Install",
|
||||
t!("Install"),
|
||||
Message::PluginInstall(repo_s.clone()),
|
||||
button::success,
|
||||
)
|
||||
}
|
||||
Some(_) => status_badge("Incompatible", StatusKind::Danger),
|
||||
None => status_badge("Unavailable", StatusKind::Muted),
|
||||
Some(_) => status_badge(t!("Incompatible"), StatusKind::Danger),
|
||||
None => status_badge(t!("Unavailable"), StatusKind::Muted),
|
||||
};
|
||||
let mut controls = row![picker, Space::new().width(8), action]
|
||||
.align_y(iced::Center)
|
||||
|
|
@ -424,14 +427,14 @@ fn repository_display_name(repository: &str) -> String {
|
|||
|
||||
fn add_repository_card<'a>(m: &MarketView) -> Element<'a, Message> {
|
||||
let form = row![
|
||||
text_input("GitHub URL or owner/repository", m.input)
|
||||
text_input(t!("GitHub URL or owner/repository").as_ref(), m.input)
|
||||
.on_input(Message::PluginRepoInput)
|
||||
.on_submit(Message::PluginRepoAdd)
|
||||
.size(13)
|
||||
.width(Fill),
|
||||
Space::new().width(8),
|
||||
pill_button(
|
||||
"Add repository",
|
||||
t!("Add repository"),
|
||||
Message::PluginRepoAdd,
|
||||
button::primary,
|
||||
),
|
||||
|
|
@ -440,11 +443,10 @@ fn add_repository_card<'a>(m: &MarketView) -> Element<'a, Message> {
|
|||
|
||||
market_card(
|
||||
column![
|
||||
text("Add from GitHub").size(14),
|
||||
text(
|
||||
"Paste a public repository URL. Compatible releases and the README \
|
||||
are detected automatically.",
|
||||
)
|
||||
text(t!("Add from GitHub")).size(14),
|
||||
text(t!(
|
||||
"Paste a public repository URL. Compatible releases and the README are detected automatically."
|
||||
))
|
||||
.size(11)
|
||||
.style(muted_style),
|
||||
Space::new().height(3),
|
||||
|
|
@ -455,28 +457,31 @@ fn add_repository_card<'a>(m: &MarketView) -> Element<'a, Message> {
|
|||
)
|
||||
}
|
||||
|
||||
fn registry_error_message(error: &str) -> (&'static str, &'static str) {
|
||||
fn registry_error_message(error: &str) -> (Cow<'static, str>, Cow<'static, str>) {
|
||||
let error = error.to_ascii_lowercase();
|
||||
if error.contains("certificate")
|
||||
|| error.contains("unknownissuer")
|
||||
|| error.contains("unknown issuer")
|
||||
{
|
||||
(
|
||||
"Unable to verify the server certificate",
|
||||
"Open CAD Studio could not trust the certificate presented for the plugin registry. \
|
||||
Check your system certificate and proxy settings, then retry.",
|
||||
t!("Unable to verify the server certificate"),
|
||||
t!(
|
||||
"Open CAD Studio could not trust the certificate presented for the plugin registry. Check your system certificate and proxy settings, then retry."
|
||||
),
|
||||
)
|
||||
} else if error.contains("timed out") || error.contains("timeout") {
|
||||
(
|
||||
"Plugin registry request timed out",
|
||||
"Check your internet or proxy connection, then retry. Manually added repositories \
|
||||
remain available.",
|
||||
t!("Plugin registry request timed out"),
|
||||
t!(
|
||||
"Check your internet or proxy connection, then retry. Manually added repositories remain available."
|
||||
),
|
||||
)
|
||||
} else {
|
||||
(
|
||||
"Unable to load the plugin registry",
|
||||
"Check your internet or proxy connection, then retry. Manually added repositories \
|
||||
remain available.",
|
||||
t!("Unable to load the plugin registry"),
|
||||
t!(
|
||||
"Check your internet or proxy connection, then retry. Manually added repositories remain available."
|
||||
),
|
||||
)
|
||||
}
|
||||
}
|
||||
|
|
@ -486,23 +491,23 @@ fn registry_notice<'a>(m: &MarketView) -> Option<Element<'a, Message>> {
|
|||
let (title, message) = registry_error_message(error);
|
||||
let actions = row![
|
||||
pill_button(
|
||||
"Retry",
|
||||
t!("Retry"),
|
||||
Message::PluginRegistryRetry,
|
||||
button::primary,
|
||||
),
|
||||
Space::new().width(6),
|
||||
pill_button(
|
||||
if m.registry_error_details_open {
|
||||
"Hide details"
|
||||
t!("Hide details")
|
||||
} else {
|
||||
"Show details"
|
||||
t!("Show details")
|
||||
},
|
||||
Message::PluginRegistryErrorDetailsToggle,
|
||||
button::secondary,
|
||||
),
|
||||
Space::new().width(6),
|
||||
pill_button(
|
||||
"Copy details",
|
||||
t!("Copy details"),
|
||||
Message::PluginRegistryCopyDiagnostics,
|
||||
button::secondary,
|
||||
),
|
||||
|
|
@ -545,8 +550,8 @@ fn registry_notice<'a>(m: &MarketView) -> Option<Element<'a, Message>> {
|
|||
(m.registry_loading && m.registry.is_empty()).then(|| {
|
||||
container(
|
||||
column![
|
||||
text("Loading plugin catalog…").size(13),
|
||||
text("Connecting securely using your system certificate settings.")
|
||||
text(t!("Loading plugin catalog…")).size(13),
|
||||
text(t!("Connecting securely using your system certificate settings."))
|
||||
.size(11)
|
||||
.style(muted_style),
|
||||
]
|
||||
|
|
@ -563,7 +568,7 @@ fn marketplace_section<'a>(
|
|||
m: &MarketView,
|
||||
externals: &[ExternalPlugin],
|
||||
) -> Element<'a, Message> {
|
||||
let mut col = column![text("Available plugins").size(13).style(primary_style)].spacing(6);
|
||||
let mut col = column![text(t!("Available plugins")).size(13).style(primary_style)].spacing(6);
|
||||
let mut visible = 0usize;
|
||||
if let Some(notice) = registry_notice(m) {
|
||||
col = col.push(notice);
|
||||
|
|
@ -617,7 +622,7 @@ fn marketplace_section<'a>(
|
|||
let info = button(
|
||||
column![
|
||||
text(display_name).size(14),
|
||||
text("Added from GitHub").size(11).style(muted_style),
|
||||
text(t!("Added from GitHub")).size(11).style(muted_style),
|
||||
]
|
||||
.spacing(4)
|
||||
.width(Fill),
|
||||
|
|
@ -642,9 +647,9 @@ fn marketplace_section<'a>(
|
|||
&& !(m.registry_loading && m.registry.is_empty())
|
||||
{
|
||||
let message = if m.search.trim().is_empty() {
|
||||
"No additional plugins are available."
|
||||
t!("No additional plugins are available.")
|
||||
} else {
|
||||
"No available plugins match your search."
|
||||
t!("No available plugins match your search.")
|
||||
};
|
||||
col = col.push(text(message).size(12).style(muted_style));
|
||||
}
|
||||
|
|
@ -684,8 +689,8 @@ fn readme_panel<'a>(
|
|||
let Some(repo) = market.selected_repo else {
|
||||
return container(
|
||||
column![
|
||||
text("Plugin details").size(16),
|
||||
text("Select a plugin to read its GitHub README.")
|
||||
text(t!("Plugin details")).size(16),
|
||||
text(t!("Select a plugin to read its GitHub README."))
|
||||
.size(12)
|
||||
.style(muted_style),
|
||||
]
|
||||
|
|
@ -712,7 +717,7 @@ fn readme_panel<'a>(
|
|||
.spacing(3)
|
||||
.width(width),
|
||||
pill_button(
|
||||
"View on GitHub",
|
||||
t!("View on GitHub"),
|
||||
Message::OpenUrl(format!("https://github.com/{repo}")),
|
||||
button::secondary,
|
||||
),
|
||||
|
|
@ -722,8 +727,8 @@ fn readme_panel<'a>(
|
|||
let content: Element<'a, Message> = if market.readme_loading.contains(repo) {
|
||||
container(
|
||||
column![
|
||||
text("Loading README…").size(13),
|
||||
text("Fetching the default branch from GitHub.")
|
||||
text(t!("Loading README…")).size(13),
|
||||
text(t!("Fetching the default branch from GitHub."))
|
||||
.size(11)
|
||||
.style(muted_style),
|
||||
]
|
||||
|
|
@ -748,11 +753,11 @@ fn readme_panel<'a>(
|
|||
}
|
||||
Some(Err(error)) => container(
|
||||
column![
|
||||
text("README could not be loaded").size(14),
|
||||
text(t!("README could not be loaded")).size(14),
|
||||
text(error.clone()).size(11).style(muted_style),
|
||||
Space::new().height(4),
|
||||
pill_button(
|
||||
"Retry",
|
||||
t!("Retry"),
|
||||
Message::PluginReadmeSelect(repo.to_string()),
|
||||
button::secondary,
|
||||
),
|
||||
|
|
@ -765,7 +770,7 @@ fn readme_panel<'a>(
|
|||
.height(height)
|
||||
.into(),
|
||||
None => container(
|
||||
text("Select the plugin again to load its README.")
|
||||
text(t!("Select the plugin again to load its README."))
|
||||
.size(12)
|
||||
.style(muted_style),
|
||||
)
|
||||
|
|
@ -811,15 +816,16 @@ pub fn view_window<'a>(
|
|||
) -> Element<'a, Message> {
|
||||
let width = sizing.width;
|
||||
let height = sizing.height;
|
||||
let title = text("Plugins").size(20);
|
||||
let subtitle = text("Browse, install, and manage add-ons. Select one to view its README.")
|
||||
.size(12)
|
||||
.style(muted_style);
|
||||
let title = text(t!("Plugins")).size(20);
|
||||
let subtitle =
|
||||
text(t!("Browse, install, and manage add-ons. Select one to view its README."))
|
||||
.size(12)
|
||||
.style(muted_style);
|
||||
|
||||
let mut list = column![].spacing(10);
|
||||
// Installed external packages (from the plugins folder).
|
||||
if externals.is_empty() {
|
||||
list = list.push(text("No plugins installed yet.").size(13).style(muted_style));
|
||||
list = list.push(text(t!("No plugins installed yet.")).size(13).style(muted_style));
|
||||
} else {
|
||||
let mut visible_installed = 0usize;
|
||||
for p in externals {
|
||||
|
|
@ -828,7 +834,7 @@ pub fn view_window<'a>(
|
|||
continue;
|
||||
}
|
||||
if visible_installed == 0 {
|
||||
list = list.push(text("Installed").size(13).style(primary_style));
|
||||
list = list.push(text(t!("Installed")).size(13).style(primary_style));
|
||||
}
|
||||
visible_installed += 1;
|
||||
let selected = repository.as_deref() == market.selected_repo;
|
||||
|
|
@ -857,7 +863,7 @@ pub fn view_window<'a>(
|
|||
}
|
||||
if visible_installed == 0 && !market.search.trim().is_empty() {
|
||||
list = list.push(
|
||||
text("No installed plugins match your search.")
|
||||
text(t!("No installed plugins match your search."))
|
||||
.size(12)
|
||||
.style(muted_style),
|
||||
);
|
||||
|
|
@ -875,7 +881,7 @@ pub fn view_window<'a>(
|
|||
let catalog = scrollable(container(list.width(Fill)).padding(gutter))
|
||||
.height(height)
|
||||
.width(Fill);
|
||||
let search = text_input("Search plugins…", market.search)
|
||||
let search = text_input(t!("Search plugins…").as_ref(), market.search)
|
||||
.on_input(Message::PluginSearchInput)
|
||||
.size(13)
|
||||
.padding([7, 10])
|
||||
|
|
@ -916,7 +922,7 @@ pub fn view_window<'a>(
|
|||
/// Browser replacement for the native Plugin Manager.
|
||||
#[cfg(target_arch = "wasm32")]
|
||||
pub fn view_web_notice<'a>() -> Element<'a, Message> {
|
||||
let download = button(text("Download desktop app").size(13))
|
||||
let download = button(text(t!("Download desktop app")).size(13))
|
||||
.on_press(Message::OpenUrl(DESKTOP_DOWNLOAD_URL.to_string()))
|
||||
.padding([9, 18])
|
||||
.style(|theme: &Theme, status| {
|
||||
|
|
@ -944,14 +950,13 @@ pub fn view_web_notice<'a>() -> Element<'a, Message> {
|
|||
let notice = container(
|
||||
column![
|
||||
icon,
|
||||
text("Plugins are available in the desktop app")
|
||||
text(t!("Plugins are available in the desktop app"))
|
||||
.size(20)
|
||||
.width(Length::Fit)
|
||||
.align_x(iced::alignment::Horizontal::Center),
|
||||
text(
|
||||
"Open CAD Studio plugins are native packages and cannot run inside a browser. \
|
||||
Download the desktop app to browse, install, and use plugins.",
|
||||
)
|
||||
text(t!(
|
||||
"Open CAD Studio plugins are native packages and cannot run inside a browser. Download the desktop app to browse, install, and use plugins."
|
||||
))
|
||||
.size(13)
|
||||
.width(Length::Fit)
|
||||
.align_x(iced::alignment::Horizontal::Center)
|
||||
|
|
@ -998,6 +1003,10 @@ mod tests {
|
|||
|
||||
#[test]
|
||||
fn certificate_errors_get_user_friendly_copy() {
|
||||
// The registry message is localised; pin English so the assertion
|
||||
// does not depend on the ambient process locale.
|
||||
crate::i18n::set_language(crate::i18n::Language::EnUs)
|
||||
.expect("English locale must be available");
|
||||
let (title, message) =
|
||||
registry_error_message("io: invalid peer certificate: UnknownIssuer");
|
||||
|
||||
|
|
|
|||
|
|
@ -3,6 +3,7 @@
|
|||
use crate::app::Message;
|
||||
use iced::widget::{column, container, row, scrollable, text, Space};
|
||||
use iced::{Background, Element, Theme};
|
||||
use crate::t;
|
||||
use std::borrow::Cow;
|
||||
|
||||
/// Display name of the primary accelerator modifier on this platform.
|
||||
|
|
@ -41,7 +42,7 @@ fn hdivider<'a>(width: iced::Length) -> Element<'a, Message> {
|
|||
|
||||
fn shortcut_row<'a>(
|
||||
key: impl Into<Cow<'static, str>>,
|
||||
action: &'static str,
|
||||
action: Cow<'static, str>,
|
||||
) -> Element<'a, Message> {
|
||||
row![
|
||||
text(key.into())
|
||||
|
|
@ -75,7 +76,7 @@ pub fn view_window<'a>(
|
|||
// ── Toolbar ───────────────────────────────────────────────────────────
|
||||
let toolbar = container(
|
||||
row![
|
||||
text("Type SHORTCUTS SET <key> <cmd> to add custom shortcuts.")
|
||||
text(t!("Type SHORTCUTS SET <key> <cmd> to add custom shortcuts."))
|
||||
.size(10)
|
||||
.style(muted_style),
|
||||
]
|
||||
|
|
@ -92,41 +93,42 @@ 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"),
|
||||
section(format!(
|
||||
"── {MOD} Shortcuts ──────────────────────────────────────"
|
||||
section(t!("── Function Keys ──────────────────────────────────────")),
|
||||
shortcut_row("F3", t!("Toggle Object Snap")),
|
||||
shortcut_row("F7", t!("Toggle Grid")),
|
||||
shortcut_row("F8", t!("Toggle Ortho")),
|
||||
shortcut_row("F9", t!("Toggle Grid Snap")),
|
||||
shortcut_row("F10", t!("Toggle Polar Tracking")),
|
||||
shortcut_row("F11", t!("Toggle Object Snap Tracking")),
|
||||
shortcut_row("F12", t!("Toggle Dynamic Input")),
|
||||
section(t!(
|
||||
"── %{MOD} Shortcuts ──────────────────────────────────────",
|
||||
MOD = MOD
|
||||
)),
|
||||
shortcut_row(format!("{MOD}+N"), "New Drawing"),
|
||||
shortcut_row(format!("{MOD}+O"), "Open File"),
|
||||
shortcut_row(format!("{MOD}+S"), "Save"),
|
||||
shortcut_row(format!("{MOD}+Shift+S"), "Save As"),
|
||||
shortcut_row(format!("{MOD}+Z"), "Undo"),
|
||||
shortcut_row(format!("{MOD}+Shift+Z / {MOD}+Y"), "Redo"),
|
||||
shortcut_row(format!("{MOD}+F / {MOD}+H"), "Find and Replace"),
|
||||
shortcut_row(format!("{MOD}+C"), "Copy to Clipboard"),
|
||||
shortcut_row(format!("{MOD}+X"), "Cut to Clipboard"),
|
||||
shortcut_row(format!("{MOD}+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(format!("{MOD}+N"), t!("New Drawing")),
|
||||
shortcut_row(format!("{MOD}+O"), t!("Open File")),
|
||||
shortcut_row(format!("{MOD}+S"), t!("Save")),
|
||||
shortcut_row(format!("{MOD}+Shift+S"), t!("Save As")),
|
||||
shortcut_row(format!("{MOD}+Z"), t!("Undo")),
|
||||
shortcut_row(format!("{MOD}+Shift+Z / {MOD}+Y"), t!("Redo")),
|
||||
shortcut_row(format!("{MOD}+F / {MOD}+H"), t!("Find and Replace")),
|
||||
shortcut_row(format!("{MOD}+C"), t!("Copy to Clipboard")),
|
||||
shortcut_row(format!("{MOD}+X"), t!("Cut to Clipboard")),
|
||||
shortcut_row(format!("{MOD}+V"), t!("Paste from Clipboard")),
|
||||
section(t!("── Other Keys ──────────────────────────────────────────")),
|
||||
shortcut_row("Enter / Space", t!("Finalize command / Repeat last")),
|
||||
shortcut_row("Escape", t!("Cancel active command")),
|
||||
shortcut_row("Delete", t!("Delete selected entities")),
|
||||
shortcut_row("↑ / ↓", t!("Command history navigation")),
|
||||
];
|
||||
|
||||
// Custom overrides section
|
||||
rows.push(section(
|
||||
"── Custom Overrides (SHORTCUTS SET) ──────────────────",
|
||||
t!("── Custom Overrides (SHORTCUTS SET) ──────────────────"),
|
||||
));
|
||||
if overrides.is_empty() {
|
||||
rows.push(
|
||||
text(" (none — use: SHORTCUTS SET <key> <command>)")
|
||||
text(t!(" (none — use: SHORTCUTS SET <key> <command>)"))
|
||||
.size(11)
|
||||
.style(muted_style)
|
||||
.into(),
|
||||
|
|
@ -160,8 +162,8 @@ pub fn view_window<'a>(
|
|||
// ── Header row with accent ────────────────────────────────────────────
|
||||
let header = container(
|
||||
row![
|
||||
text("Key").size(10).style(primary_style).width(160),
|
||||
text("Action").size(10).style(primary_style),
|
||||
text(t!("Key")).size(10).style(primary_style).width(160),
|
||||
text(t!("Action")).size(10).style(primary_style),
|
||||
]
|
||||
.spacing(8)
|
||||
.padding([4, 16]),
|
||||
|
|
|
|||
|
|
@ -1,6 +1,8 @@
|
|||
use crate::app::Message;
|
||||
use iced::widget::{button, column, container, row, scrollable, text, Space};
|
||||
use iced::{Background, Border, Element, Theme};
|
||||
use crate::t;
|
||||
use std::borrow::Cow;
|
||||
|
||||
fn muted_style(theme: &Theme) -> iced::widget::text::Style {
|
||||
iced::widget::text::Style {
|
||||
|
|
@ -18,7 +20,7 @@ fn primary_style(theme: &Theme) -> iced::widget::text::Style {
|
|||
/// flag tints the border + label with the accent colour, making the new
|
||||
/// version the visual anchor of the row.
|
||||
fn version_card<'a>(
|
||||
label: &'static str,
|
||||
label: Cow<'static, str>,
|
||||
value: String,
|
||||
highlight: bool,
|
||||
width: iced::Length,
|
||||
|
|
@ -132,8 +134,8 @@ pub fn view_window<'a>(
|
|||
) -> Element<'a, Message> {
|
||||
let header = container(
|
||||
column![
|
||||
text("New Release Available").size(20).style(primary_style),
|
||||
text("A newer Open CAD Studio version is published on GitHub.")
|
||||
text(t!("New Release Available")).size(20).style(primary_style),
|
||||
text(t!("A newer Open CAD Studio version is published on GitHub."))
|
||||
.size(11)
|
||||
.style(muted_style),
|
||||
]
|
||||
|
|
@ -153,12 +155,12 @@ pub fn view_window<'a>(
|
|||
// one accent-tinted to draw the eye. Replaces the previous label/value
|
||||
// row layout. The arrow between them is purely decorative.
|
||||
let installed = version_card(
|
||||
"Installed",
|
||||
t!("Installed"),
|
||||
format!("v{}", env!("CARGO_PKG_VERSION")),
|
||||
false,
|
||||
sizing.width,
|
||||
);
|
||||
let latest_card = version_card("Latest", format!("v{}", latest), true, sizing.width);
|
||||
let latest_card = version_card(t!("Latest"), format!("v{}", latest), true, sizing.width);
|
||||
let arrow = container(crate::ui::icons::themed_secondary(
|
||||
crate::ui::icons::ARROW_LONG_RIGHT,
|
||||
20.0,
|
||||
|
|
@ -172,12 +174,12 @@ pub fn view_window<'a>(
|
|||
.align_y(iced::Center)
|
||||
.width(sizing.width);
|
||||
|
||||
let later_btn = button(text("Later").size(11))
|
||||
let later_btn = button(text(t!("Later")).size(11))
|
||||
.on_press(Message::UpdateNoticeClose)
|
||||
.style(button::secondary)
|
||||
.padding([6, 16]);
|
||||
|
||||
let open_btn = button(text("Open Release Page").size(11))
|
||||
let open_btn = button(text(t!("Open Release Page")).size(11))
|
||||
.on_press(Message::UpdateNoticeOpenRelease)
|
||||
.style(button::primary)
|
||||
.padding([6, 16]);
|
||||
|
|
@ -195,7 +197,7 @@ pub fn view_window<'a>(
|
|||
// Release notes panel. Rendered as a light-markdown column inside a
|
||||
// bordered scrollable so long bodies stay contained and don't
|
||||
// explode the window. Empty body → "No release notes provided."
|
||||
let notes_heading = container(text("What's new").size(11).style(muted_style))
|
||||
let notes_heading = container(text(t!("What's new")).size(11).style(muted_style))
|
||||
.padding(iced::Padding {
|
||||
top: 10.0,
|
||||
right: 0.0,
|
||||
|
|
@ -204,7 +206,7 @@ pub fn view_window<'a>(
|
|||
});
|
||||
|
||||
let notes_body: Element<'a, Message> = if body.trim().is_empty() {
|
||||
text("No release notes provided.")
|
||||
text(t!("No release notes provided."))
|
||||
.size(11)
|
||||
.style(muted_style)
|
||||
.into()
|
||||
|
|
|
|||
Loading…
Reference in a new issue