forked from 1b-agent/opencadstudio-upstream
Merge pull request #1811 from ramox81/codex/txt2mtxt
TXT2MTXT: combine text into MText with a settings dialog # Conflicts: # locales/ar-SA/opencadstudio.ftl # locales/bg-BG/opencadstudio.ftl # locales/cs-CZ/opencadstudio.ftl # locales/de-DE/opencadstudio.ftl # locales/el-GR/opencadstudio.ftl # locales/en-US/opencadstudio.ftl # locales/es-ES/opencadstudio.ftl # locales/fi-FI/opencadstudio.ftl # locales/fr-FR/opencadstudio.ftl # locales/hi-IN/opencadstudio.ftl # locales/hu-HU/opencadstudio.ftl # locales/it-IT/opencadstudio.ftl # locales/ja-JP/opencadstudio.ftl # locales/ko-KR/opencadstudio.ftl # locales/lo-LA/opencadstudio.ftl # locales/nl-NL/opencadstudio.ftl # locales/pl-PL/opencadstudio.ftl # locales/pt-BR/opencadstudio.ftl # locales/ru-RU/opencadstudio.ftl # locales/tr-TR/opencadstudio.ftl # locales/zh-CN/opencadstudio.ftl # locales/zh-TW/opencadstudio.ftl
This commit is contained in:
commit
aa618b8327
40 files changed
+1222
-4
No files matched your search
@@ -445,6 +445,7 @@ impl OpenCADStudio {
|
||||
} => {
|
||||
self.finish_qselect_sample(*state, tab_id, handle);
|
||||
}
|
||||
CmdResult::OpenTxt2MtxtSettings => self.open_txt2mtxt_settings(),
|
||||
CmdResult::OpenAutoConstrainSettings => {
|
||||
self.handle_open_auto_constrain_settings();
|
||||
}
|
||||
|
||||
@@ -1335,6 +1335,30 @@ impl OpenCADStudio {
|
||||
}
|
||||
}
|
||||
|
||||
// TXT2MTXT — TEXT / MTEXT combined into MTEXT. A preselection is
|
||||
// used at once; otherwise the selection prompt with SEttings.
|
||||
"TXT2MTXT" => {
|
||||
if !self.tabs[i].scene.selected.is_empty() {
|
||||
return Some(self.dispatch_command("_TXT2MTXTSEL"));
|
||||
}
|
||||
use crate::modules::draw::select::SelectObjectsCommand;
|
||||
self.command_line
|
||||
.push_output(crate::t!("Select text objects to combine ...").as_ref());
|
||||
let command = SelectObjectsCommand::with_settings(
|
||||
"TXT2MTXT",
|
||||
"_TXT2MTXTSEL",
|
||||
"Select objects or [SEttings]:",
|
||||
|| crate::command::CmdResult::OpenTxt2MtxtSettings,
|
||||
);
|
||||
self.command_line.push_info(&command.prompt());
|
||||
self.command_line.set_step_options(command.options());
|
||||
self.tabs[i].active_cmd = Some(Box::new(command));
|
||||
}
|
||||
"_TXT2MTXTSEL" => {
|
||||
self.apply_txt2mtxt(i);
|
||||
return Some(self.finish_dispatch("TXT2MTXT"));
|
||||
}
|
||||
|
||||
"OFFSET" => {
|
||||
use crate::modules::draw::modify::offset::{is_offsettable, OffsetCommand};
|
||||
let all_entities: Vec<_> = self.tabs[i]
|
||||
@@ -1546,3 +1570,52 @@ fn title_case(s: &str) -> String {
|
||||
.collect::<Vec<_>>()
|
||||
.join(" ")
|
||||
}
|
||||
|
||||
impl OpenCADStudio {
|
||||
/// TXT2MTXT on the selection (in selection order), as one undo step.
|
||||
fn apply_txt2mtxt(&mut self, i: usize) {
|
||||
let scene = &self.tabs[i].scene;
|
||||
let handles: Vec<codec::Handle> = scene
|
||||
.selected_handles_in_order()
|
||||
.into_iter()
|
||||
.filter(|handle| !scene.is_layer_locked(*handle))
|
||||
.collect();
|
||||
let plan = {
|
||||
let selected: Vec<(codec::Handle, &codec::EntityType)> = handles
|
||||
.iter()
|
||||
.filter_map(|h| scene.document.get_entity(*h).map(|e| (*h, e)))
|
||||
.collect();
|
||||
crate::modules::annotate::txt2mtxt::plan(
|
||||
&selected,
|
||||
&scene.document,
|
||||
self.txt2mtxt_settings,
|
||||
)
|
||||
};
|
||||
self.tabs[i].scene.deselect_all();
|
||||
// Enter on an empty selection ends quietly.
|
||||
if handles.is_empty() {
|
||||
return;
|
||||
}
|
||||
let Some(plan) = plan else {
|
||||
self.command_line
|
||||
.push_output(" 0 Text objects removed, 0 MText objects added.");
|
||||
return;
|
||||
};
|
||||
self.push_undo_snapshot(i, "TXT2MTXT");
|
||||
self.tabs[i].scene.erase_entities(&plan.erase);
|
||||
let added = plan.add.len();
|
||||
for entity in plan.add {
|
||||
self.tabs[i].scene.add_entity(entity);
|
||||
}
|
||||
self.tabs[i].dirty = true;
|
||||
self.refresh_properties();
|
||||
// Every selected object counts as removed text, the way the
|
||||
// reference reports it.
|
||||
let removed = handles.len();
|
||||
self.command_line.push_output(&if added == 1 {
|
||||
format!(" {removed} Text objects removed, 1 MText object added.")
|
||||
} else {
|
||||
format!(" {removed} Text objects removed, {added} MText objects added.")
|
||||
});
|
||||
}
|
||||
}
|
||||
@@ -779,6 +779,10 @@ inventory::submit!(crate::command::CommandRegistration {
|
||||
"UCSDETECT",
|
||||
"SURFACEASSOCIATIVITY",
|
||||
"LAYOUTCREATEVIEWPORT",
|
||||
"TXT2MTXTCOMBINEMTEXT",
|
||||
"TXT2MTXTTEXTORDER",
|
||||
"TXT2MTXTWORDWRAP",
|
||||
"TXT2MTXTLINESPACING",
|
||||
"FIELDEVAL",
|
||||
"PDFIMPORTMODE",
|
||||
"PDFIMPORTFILTER",
|
||||
|
||||
@@ -348,6 +348,12 @@ impl OpenCADStudio {
|
||||
self.active_modal = Some(crate::app::ModalKind::PdfImportSettings);
|
||||
}
|
||||
|
||||
/// TXT2MTXT's SEttings option: the dialog over the running command.
|
||||
pub(in crate::app) fn open_txt2mtxt_settings(&mut self) {
|
||||
self.txt2mtxt_dialog = Some(self.txt2mtxt_settings);
|
||||
self.active_modal = Some(crate::app::ModalKind::Txt2MtxtSettings);
|
||||
}
|
||||
|
||||
/// PDFIMPORT's File option after the file is chosen.
|
||||
pub(in crate::app) fn open_pdf_import_file(&mut self, path: &str) {
|
||||
self.pdf_import_file = Some(PdfImportFileState {
|
||||
@@ -442,6 +448,7 @@ impl OpenCADStudio {
|
||||
crate::t!("Sets the color schemes and ranges a point cloud is colored by intensity or elevation.")
|
||||
}
|
||||
"layers" => crate::t!("Turns the layers of a PDF underlay on or off."),
|
||||
"txt2mtxt" => std::borrow::Cow::Owned(crate::tr!("txt2mtxt", "help")),
|
||||
_ => crate::t!("Imports the geometry, fills, raster images and text of a PDF file as drawing objects."),
|
||||
};
|
||||
self.command_line.push_info(text.as_ref());
|
||||
@@ -726,6 +733,37 @@ impl OpenCADStudio {
|
||||
self.close_active_modal();
|
||||
}
|
||||
|
||||
// ── Text to MText Settings ────────────────────────────────────
|
||||
PdfDialogMsg::T2mCombine(on) => {
|
||||
if let Some(s) = self.txt2mtxt_dialog.as_mut() {
|
||||
s.combine = on;
|
||||
}
|
||||
}
|
||||
PdfDialogMsg::T2mSelectOrder(on) => {
|
||||
if let Some(s) = self.txt2mtxt_dialog.as_mut() {
|
||||
s.select_order = on;
|
||||
}
|
||||
}
|
||||
PdfDialogMsg::T2mWrap(on) => {
|
||||
if let Some(s) = self.txt2mtxt_dialog.as_mut() {
|
||||
s.word_wrap = on;
|
||||
}
|
||||
}
|
||||
PdfDialogMsg::T2mUniform(on) => {
|
||||
if let Some(s) = self.txt2mtxt_dialog.as_mut() {
|
||||
s.uniform_spacing = on;
|
||||
}
|
||||
}
|
||||
PdfDialogMsg::T2mOk => {
|
||||
if let Some(s) = self.txt2mtxt_dialog {
|
||||
if s != self.txt2mtxt_settings {
|
||||
self.txt2mtxt_settings = s;
|
||||
self.save_config();
|
||||
}
|
||||
}
|
||||
self.close_active_modal();
|
||||
}
|
||||
|
||||
// ── Import PDF ────────────────────────────────────────────────
|
||||
PdfDialogMsg::ImportBrowse => return Task::done(Message::PdfImportPick),
|
||||
PdfDialogMsg::ImportPage(page) => {
|
||||
|
||||
@@ -1282,6 +1282,10 @@ impl OpenCADStudio {
|
||||
| "DYNCONSTRAINTDISPLAY"
|
||||
| "CCONSTRAINTFORM"
|
||||
| "DYNMODE"
|
||||
| "TXT2MTXTCOMBINEMTEXT"
|
||||
| "TXT2MTXTTEXTORDER"
|
||||
| "TXT2MTXTWORDWRAP"
|
||||
| "TXT2MTXTLINESPACING"
|
||||
) =>
|
||||
{
|
||||
return self.dispatch_styleprops(&format!("SETVAR {cmd}"), i);
|
||||
@@ -1578,6 +1582,50 @@ impl OpenCADStudio {
|
||||
}
|
||||
return Some(self.finish_dispatch(cmd));
|
||||
}
|
||||
if let Some(field) = match name.as_str() {
|
||||
"TXT2MTXTCOMBINEMTEXT" => Some(0),
|
||||
"TXT2MTXTTEXTORDER" => Some(1),
|
||||
"TXT2MTXTWORDWRAP" => Some(2),
|
||||
"TXT2MTXTLINESPACING" => Some(3),
|
||||
_ => None,
|
||||
} {
|
||||
// TXT2MTXT's profile settings: 0 / 1 (TEXTORDER 1 =
|
||||
// selection order).
|
||||
let s = &mut self.txt2mtxt_settings;
|
||||
let slot = match field {
|
||||
0 => &mut s.combine,
|
||||
1 => &mut s.select_order,
|
||||
2 => &mut s.word_wrap,
|
||||
_ => &mut s.uniform_spacing,
|
||||
};
|
||||
let current = i16::from(*slot);
|
||||
match value
|
||||
.as_deref()
|
||||
.map(|v| v.trim().parse::<i16>().ok().filter(|v| (0..=1).contains(v)))
|
||||
{
|
||||
Some(Some(mode)) => {
|
||||
if current != mode {
|
||||
*slot = mode == 1;
|
||||
self.save_config();
|
||||
}
|
||||
}
|
||||
Some(None) => {
|
||||
self.command_line
|
||||
.push_error(crate::t!("Requires 0 or 1 only.").as_ref());
|
||||
self.command_line.push_output(&format!(
|
||||
"Enter new value for {name} <{current}>:"
|
||||
));
|
||||
self.pending_setvar = Some(name.clone());
|
||||
}
|
||||
None => {
|
||||
self.command_line.push_output(&format!(
|
||||
"Enter new value for {name} <{current}>:"
|
||||
));
|
||||
self.pending_setvar = Some(name.clone());
|
||||
}
|
||||
}
|
||||
return Some(self.finish_dispatch(cmd));
|
||||
}
|
||||
if name == "FIELDDISPLAY" {
|
||||
let current = i16::from(crate::entities::field::display());
|
||||
match value
|
||||
|
||||
@@ -105,6 +105,7 @@ pub(super) const NAMES: &[&str] = &[
|
||||
#[cfg(not(target_arch = "wasm32"))]
|
||||
"external_change_cancel",
|
||||
"pdf_dialog_ok",
|
||||
"txt2mtxt_settings",
|
||||
"view_background",
|
||||
"image_adjust",
|
||||
"refedit_dialog",
|
||||
@@ -2202,6 +2203,27 @@ impl OpenCADStudio {
|
||||
_ => return Err(bad()),
|
||||
}
|
||||
}
|
||||
// Text to MText Settings (TXT2MTXT SEttings): "combine=0",
|
||||
// "order=select" / "order=top", "wrap=0", "uniform=1", "ok",
|
||||
// "cancel".
|
||||
"txt2mtxt_settings" => {
|
||||
use crate::ui::window::pdf_dialogs::PdfDialogMsg as M;
|
||||
if self.active_modal != Some(crate::app::ModalKind::Txt2MtxtSettings) {
|
||||
return Err(failure("no_dialog", "Text to MText Settings is not open"));
|
||||
}
|
||||
let value = string(req, "value")?;
|
||||
let (key, arg) = value.split_once('=').unwrap_or((value, ""));
|
||||
let on = arg != "0";
|
||||
match key {
|
||||
"combine" => Message::PdfDialog(M::T2mCombine(on)),
|
||||
"order" => Message::PdfDialog(M::T2mSelectOrder(arg == "select")),
|
||||
"wrap" => Message::PdfDialog(M::T2mWrap(on)),
|
||||
"uniform" => Message::PdfDialog(M::T2mUniform(on)),
|
||||
"ok" => Message::PdfDialog(M::T2mOk),
|
||||
"cancel" => Message::CloseModal,
|
||||
_ => return Err(failure("invalid_value", "Unknown setting")),
|
||||
}
|
||||
}
|
||||
"pdf_dialog_ok" => {
|
||||
use crate::ui::window::pdf_dialogs::PdfDialogMsg;
|
||||
Message::PdfDialog(match self.active_modal {
|
||||
|
||||
@@ -381,6 +381,7 @@ fn command_selection_policy(name: &str) -> &'static str {
|
||||
"ERASE",
|
||||
"EXPLODE",
|
||||
"TXTEXP",
|
||||
"TXT2MTXT",
|
||||
"GROUP",
|
||||
"LAYFRZ",
|
||||
"LAYLCK",
|
||||
|
||||
@@ -787,6 +787,10 @@ pub(super) struct OpenCADStudio {
|
||||
/// pill shows only the glyph.
|
||||
pub show_constraint_values: bool,
|
||||
pub auto_constrain_settings: settings::AutoConstrainSettings,
|
||||
/// TXT2MTXT settings (profile).
|
||||
pub txt2mtxt_settings: settings::Txt2MtxtSettings,
|
||||
/// The Text to MText Settings dialog's working copy.
|
||||
txt2mtxt_dialog: Option<settings::Txt2MtxtSettings>,
|
||||
auto_constrain_saved: Option<settings::AutoConstrainSettings>,
|
||||
auto_constrain_selected_row: usize,
|
||||
auto_constrain_distance_input: String,
|
||||
@@ -2205,6 +2209,8 @@ pub enum ModalKind {
|
||||
RefEdit,
|
||||
UnderlayLayers,
|
||||
PdfImportSettings,
|
||||
/// TXT2MTXT's Text to MText Settings, over the running command.
|
||||
Txt2MtxtSettings,
|
||||
PdfImportFile,
|
||||
XrefAttach,
|
||||
WriteBlock,
|
||||
@@ -4738,6 +4744,8 @@ impl OpenCADStudio {
|
||||
file_assoc_enabled: true,
|
||||
show_constraint_values: true,
|
||||
auto_constrain_settings: settings::AutoConstrainSettings::default(),
|
||||
txt2mtxt_settings: settings::Txt2MtxtSettings::default(),
|
||||
txt2mtxt_dialog: None,
|
||||
auto_constrain_saved: None,
|
||||
auto_constrain_selected_row: 0,
|
||||
auto_constrain_distance_input: "0.05".to_string(),
|
||||
|
||||
@@ -108,6 +108,34 @@ impl AutoConstrainSettings {
|
||||
}
|
||||
}
|
||||
|
||||
/// TXT2MTXT (Combine Text) settings, the profile values
|
||||
/// TXT2MTXTCOMBINEMTEXT, TXT2MTXTTEXTORDER, TXT2MTXTWORDWRAP and
|
||||
/// TXT2MTXTLINESPACING.
|
||||
#[derive(Clone, Copy, Debug, PartialEq, Eq, Serialize, Deserialize)]
|
||||
#[serde(default)]
|
||||
pub struct Txt2MtxtSettings {
|
||||
/// Combine everything into one MTEXT (else each TEXT becomes its own).
|
||||
pub combine: bool,
|
||||
/// Keep the selection order (else sort top-down).
|
||||
pub select_order: bool,
|
||||
/// Run the lines on separated by spaces in a computed width (else one
|
||||
/// paragraph per object).
|
||||
pub word_wrap: bool,
|
||||
/// Force uniform line spacing (always on while word-wrap is).
|
||||
pub uniform_spacing: bool,
|
||||
}
|
||||
|
||||
impl Default for Txt2MtxtSettings {
|
||||
fn default() -> Self {
|
||||
Self {
|
||||
combine: true,
|
||||
select_order: false,
|
||||
word_wrap: true,
|
||||
uniform_spacing: true,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// Cursor shown over the drawing viewport.
|
||||
#[derive(Clone, Copy, Debug, Default, PartialEq, Eq, Serialize, Deserialize)]
|
||||
pub enum CursorType {
|
||||
@@ -599,6 +627,9 @@ pub struct UserSettings {
|
||||
/// the Auto Constrain command.
|
||||
#[serde(default)]
|
||||
pub auto_constrain: AutoConstrainSettings,
|
||||
/// TXT2MTXT settings (Text to MText Settings dialog).
|
||||
#[serde(default)]
|
||||
pub txt2mtxt: Txt2MtxtSettings,
|
||||
/// Keep existing geometry size while solving after a constraint edit.
|
||||
#[serde(default = "default_constraint_solve_mode")]
|
||||
pub constraint_solve_mode: bool,
|
||||
@@ -995,6 +1026,7 @@ impl Default for UserSettings {
|
||||
file_assoc_enabled: true,
|
||||
show_constraint_values: true,
|
||||
auto_constrain: AutoConstrainSettings::default(),
|
||||
txt2mtxt: Txt2MtxtSettings::default(),
|
||||
constraint_solve_mode: true,
|
||||
constraint_infer: false,
|
||||
constraint_bar_display: 3,
|
||||
|
||||
@@ -900,6 +900,7 @@ impl OpenCADStudio {
|
||||
file_assoc_enabled: self.file_assoc_enabled,
|
||||
show_constraint_values: self.show_constraint_values,
|
||||
auto_constrain: self.auto_constrain_settings.clone(),
|
||||
txt2mtxt: self.txt2mtxt_settings,
|
||||
constraint_solve_mode: self.constraint_solve_mode,
|
||||
constraint_infer: self.constraint_infer,
|
||||
constraint_bar_display: self.constraint_bar_display,
|
||||
@@ -1071,6 +1072,7 @@ impl OpenCADStudio {
|
||||
self.file_assoc_enabled = s.file_assoc_enabled;
|
||||
self.show_constraint_values = s.show_constraint_values;
|
||||
self.auto_constrain_settings = s.auto_constrain.clone();
|
||||
self.txt2mtxt_settings = s.txt2mtxt;
|
||||
self.auto_constrain_settings.sanitize();
|
||||
self.auto_constrain_distance_input =
|
||||
format!("{}", self.auto_constrain_settings.distance_tolerance);
|
||||
|
||||
@@ -239,6 +239,17 @@ impl OpenCADStudio {
|
||||
Some(PdfAttach) => self.pdf_attach = None,
|
||||
Some(UnderlayLayers) => self.underlay_layers = None,
|
||||
Some(PdfImportSettings) => self.pdf_import_settings = None,
|
||||
// OK or Cancel: TXT2MTXT asks for its selection again.
|
||||
Some(Txt2MtxtSettings) => {
|
||||
self.txt2mtxt_dialog = None;
|
||||
if let Some(prompt) = self.tabs[self.active_tab]
|
||||
.active_cmd
|
||||
.as_ref()
|
||||
.map(|command| command.prompt())
|
||||
{
|
||||
self.command_line.push_info(&prompt);
|
||||
}
|
||||
}
|
||||
Some(PdfImportFile) => self.pdf_import_file = None,
|
||||
Some(XrefAttach) => self.xref_attach = None,
|
||||
Some(WriteBlock) => self.wblock = None,
|
||||
|
||||
@@ -46,6 +46,7 @@ impl OpenCADStudio {
|
||||
Some(K::RefEdit) => crate::tr!("ref-edit", "dialog-title"),
|
||||
Some(K::UnderlayLayers) => crate::t!("Underlay Layers").into_owned(),
|
||||
Some(K::PdfImportSettings) => crate::t!("PDF Import Settings").into_owned(),
|
||||
Some(K::Txt2MtxtSettings) => crate::tr!("txt2mtxt", "title"),
|
||||
Some(K::PdfImportFile) => crate::t!("Import PDF").into_owned(),
|
||||
Some(K::XrefAttach) => crate::t!("Attach External Reference").into_owned(),
|
||||
Some(K::WriteBlock) => crate::t!("Write Block").into_owned(),
|
||||
@@ -577,6 +578,12 @@ impl OpenCADStudio {
|
||||
crate::ui::window::pdf_dialogs::view_import_settings(settings, flow)
|
||||
})
|
||||
}
|
||||
super::super::ModalKind::Txt2MtxtSettings => {
|
||||
let settings = self.txt2mtxt_dialog.as_ref()?;
|
||||
sized_flow(ex, 600, 470, |flow| {
|
||||
crate::ui::window::pdf_dialogs::view_txt2mtxt_settings(settings, flow)
|
||||
})
|
||||
}
|
||||
super::super::ModalKind::PdfImportFile => {
|
||||
let state = self.pdf_import_file.as_ref()?;
|
||||
sized_flow(ex, 960, 560, |flow| {
|
||||
|
||||
Reference in new issue
Block a user