forked from 1b-agent/opencadstudio-upstream
FIELD: Field dialog, FIELD command and the ATTDEF field buttons
A Field dialog with the fields the application evaluates and the reference format choices, FIELD placing a field as multi-line text, the attribute definition dialogs' field buttons, field attachment on commit, system variable / file size / regional date providers for field evaluation, and the codec patch with the field authoring API. Co-Authored-By: Claude Opus 5.5 <[email protected]>
This commit is contained in:
1 parent
53f2983e9c
commit
87211f4654
40 files changed
+1859
-14
No files matched your search
Generated
+1
-1
@@ -4206,7 +4206,7 @@ dependencies = [
|
||||
[[package]]
|
||||
name = "opencadcodec"
|
||||
version = "0.5.5"
|
||||
source = "git+https://github.com/HakanSeven12/opencadcodec.git?rev=d28fd71#d28fd710268e7301798703d3ca9c5698a1af91ed"
|
||||
source = "git+https://github.com/ramox81/cadcodec.git?rev=22a5c3e#22a5c3e07bf9eda6b7937e951906f8aa53fdeaf3"
|
||||
dependencies = [
|
||||
"ahash 0.8.12",
|
||||
"anyhow",
|
||||
|
||||
+5
-1
@@ -74,7 +74,7 @@ naga = { version = "27", features = ["wgsl-in"] }
|
||||
|
||||
[target.'cfg(target_os = "windows")'.dependencies]
|
||||
libloading = "0.8"
|
||||
windows-sys = { version = "0.61", features = ["Win32_UI_Shell", "Win32_UI_WindowsAndMessaging", "Win32_UI_Input", "Win32_System_LibraryLoader", "Win32_System_Threading", "Win32_Security", "Win32_System_Com", "Win32_System_Registry", "Win32_Storage_FileSystem", "Win32_Foundation", "Win32_Graphics_Printing", "Win32_Graphics_Gdi", "Win32_Storage_Xps"] }
|
||||
windows-sys = { version = "0.61", features = ["Win32_UI_Shell", "Win32_UI_WindowsAndMessaging", "Win32_UI_Input", "Win32_System_LibraryLoader", "Win32_System_Threading", "Win32_Security", "Win32_System_Com", "Win32_System_Registry", "Win32_Storage_FileSystem", "Win32_Foundation", "Win32_Graphics_Printing", "Win32_Graphics_Gdi", "Win32_Storage_Xps", "Win32_Globalization"] }
|
||||
|
||||
[target.'cfg(target_os = "linux")'.dependencies]
|
||||
ashpd = { version = "0.13.13", default-features = false, features = ["async-io", "wayland"] }
|
||||
@@ -90,6 +90,10 @@ objc2-app-kit = { version = "0.2", features = ["NSApplication", "NSResponder", "
|
||||
block2 = "0.5"
|
||||
objc2-foundation = { version = "0.2", features = ["NSArray", "NSURL", "NSString", "NSObject", "NSNotification", "NSThread", "NSEnumerator"] }
|
||||
|
||||
# Field authoring API and field format codes (opencadcodec PR pending).
|
||||
[patch."https://github.com/HakanSeven12/opencadcodec.git"]
|
||||
opencadcodec = { git = "https://github.com/ramox81/cadcodec.git", rev = "22a5c3e" }
|
||||
|
||||
[patch.crates-io]
|
||||
iced_core = { git = "https://github.com/iced-rs/iced.git", rev = "23604ff22ab0aad9e00b9327cb7b8546ed84db39" }
|
||||
iced_widget = { git = "https://github.com/iced-rs/iced.git", rev = "23604ff22ab0aad9e00b9327cb7b8546ed84db39" }
|
||||
|
||||
@@ -4053,6 +4053,36 @@ insert =
|
||||
.attdef-tag-spaces = The tag may not contain spaces.
|
||||
.attdef-positive = Value must be positive and nonzero.
|
||||
.attdef-angle = Requires valid numeric angle or second point.
|
||||
.field-title = Field
|
||||
.field-category = Category
|
||||
.field-date-format = Date format
|
||||
.field-examples = Examples
|
||||
.field-file-extension = Display file extension
|
||||
.field-filename-only = Filename only
|
||||
.field-path-only = Path only
|
||||
.field-path-filename = Path and filename
|
||||
.field-bytes = Bytes
|
||||
.field-kilobytes = Kilobytes
|
||||
.field-megabytes = Megabytes
|
||||
.field-system-variable = System variable
|
||||
.field-diesel = Diesel expression
|
||||
.field-named-type = Named object type
|
||||
.field-object-type = Object type
|
||||
.field-select-object = Select object
|
||||
.field-expression = Field expression
|
||||
.field-help = Inserts a field: text that updates from the drawing, the date or another source.
|
||||
.field-cat-date = Date & Time
|
||||
.field-cat-document = Document
|
||||
.field-cat-other = Other
|
||||
.field-case-upper = Uppercase
|
||||
.field-case-lower = Lowercase
|
||||
.field-case-first = First capital
|
||||
.field-case-title = Title case
|
||||
.field-named-dimstyle = Dimstyle
|
||||
.field-named-tablestyle = Tablestyle
|
||||
.field-named-textstyle = Textstyle
|
||||
.field-prop-endpoint = EndPoint
|
||||
.field-prop-startpoint = StartPoint
|
||||
model =
|
||||
.create = إنشاء
|
||||
.boolean = عمليات منطقية
|
||||
|
||||
@@ -4040,6 +4040,36 @@ insert =
|
||||
.attdef-tag-spaces = The tag may not contain spaces.
|
||||
.attdef-positive = Value must be positive and nonzero.
|
||||
.attdef-angle = Requires valid numeric angle or second point.
|
||||
.field-title = Field
|
||||
.field-category = Category
|
||||
.field-date-format = Date format
|
||||
.field-examples = Examples
|
||||
.field-file-extension = Display file extension
|
||||
.field-filename-only = Filename only
|
||||
.field-path-only = Path only
|
||||
.field-path-filename = Path and filename
|
||||
.field-bytes = Bytes
|
||||
.field-kilobytes = Kilobytes
|
||||
.field-megabytes = Megabytes
|
||||
.field-system-variable = System variable
|
||||
.field-diesel = Diesel expression
|
||||
.field-named-type = Named object type
|
||||
.field-object-type = Object type
|
||||
.field-select-object = Select object
|
||||
.field-expression = Field expression
|
||||
.field-help = Inserts a field: text that updates from the drawing, the date or another source.
|
||||
.field-cat-date = Date & Time
|
||||
.field-cat-document = Document
|
||||
.field-cat-other = Other
|
||||
.field-case-upper = Uppercase
|
||||
.field-case-lower = Lowercase
|
||||
.field-case-first = First capital
|
||||
.field-case-title = Title case
|
||||
.field-named-dimstyle = Dimstyle
|
||||
.field-named-tablestyle = Tablestyle
|
||||
.field-named-textstyle = Textstyle
|
||||
.field-prop-endpoint = EndPoint
|
||||
.field-prop-startpoint = StartPoint
|
||||
model =
|
||||
.create = Създаване
|
||||
.boolean = Булеви операции
|
||||
|
||||
@@ -4038,6 +4038,36 @@ insert =
|
||||
.attdef-tag-spaces = The tag may not contain spaces.
|
||||
.attdef-positive = Value must be positive and nonzero.
|
||||
.attdef-angle = Requires valid numeric angle or second point.
|
||||
.field-title = Field
|
||||
.field-category = Category
|
||||
.field-date-format = Date format
|
||||
.field-examples = Examples
|
||||
.field-file-extension = Display file extension
|
||||
.field-filename-only = Filename only
|
||||
.field-path-only = Path only
|
||||
.field-path-filename = Path and filename
|
||||
.field-bytes = Bytes
|
||||
.field-kilobytes = Kilobytes
|
||||
.field-megabytes = Megabytes
|
||||
.field-system-variable = System variable
|
||||
.field-diesel = Diesel expression
|
||||
.field-named-type = Named object type
|
||||
.field-object-type = Object type
|
||||
.field-select-object = Select object
|
||||
.field-expression = Field expression
|
||||
.field-help = Inserts a field: text that updates from the drawing, the date or another source.
|
||||
.field-cat-date = Date & Time
|
||||
.field-cat-document = Document
|
||||
.field-cat-other = Other
|
||||
.field-case-upper = Uppercase
|
||||
.field-case-lower = Lowercase
|
||||
.field-case-first = First capital
|
||||
.field-case-title = Title case
|
||||
.field-named-dimstyle = Dimstyle
|
||||
.field-named-tablestyle = Tablestyle
|
||||
.field-named-textstyle = Textstyle
|
||||
.field-prop-endpoint = EndPoint
|
||||
.field-prop-startpoint = StartPoint
|
||||
model =
|
||||
.create = Vytvořit
|
||||
.boolean = Booleovské operace
|
||||
|
||||
@@ -4039,6 +4039,36 @@ insert =
|
||||
.attdef-tag-spaces = The tag may not contain spaces.
|
||||
.attdef-positive = Value must be positive and nonzero.
|
||||
.attdef-angle = Requires valid numeric angle or second point.
|
||||
.field-title = Field
|
||||
.field-category = Category
|
||||
.field-date-format = Date format
|
||||
.field-examples = Examples
|
||||
.field-file-extension = Display file extension
|
||||
.field-filename-only = Filename only
|
||||
.field-path-only = Path only
|
||||
.field-path-filename = Path and filename
|
||||
.field-bytes = Bytes
|
||||
.field-kilobytes = Kilobytes
|
||||
.field-megabytes = Megabytes
|
||||
.field-system-variable = System variable
|
||||
.field-diesel = Diesel expression
|
||||
.field-named-type = Named object type
|
||||
.field-object-type = Object type
|
||||
.field-select-object = Select object
|
||||
.field-expression = Field expression
|
||||
.field-help = Inserts a field: text that updates from the drawing, the date or another source.
|
||||
.field-cat-date = Date & Time
|
||||
.field-cat-document = Document
|
||||
.field-cat-other = Other
|
||||
.field-case-upper = Uppercase
|
||||
.field-case-lower = Lowercase
|
||||
.field-case-first = First capital
|
||||
.field-case-title = Title case
|
||||
.field-named-dimstyle = Dimstyle
|
||||
.field-named-tablestyle = Tablestyle
|
||||
.field-named-textstyle = Textstyle
|
||||
.field-prop-endpoint = EndPoint
|
||||
.field-prop-startpoint = StartPoint
|
||||
model =
|
||||
.create = Erstellen
|
||||
.boolean = Boolesche Operationen
|
||||
|
||||
@@ -4041,6 +4041,36 @@ insert =
|
||||
.attdef-tag-spaces = The tag may not contain spaces.
|
||||
.attdef-positive = Value must be positive and nonzero.
|
||||
.attdef-angle = Requires valid numeric angle or second point.
|
||||
.field-title = Field
|
||||
.field-category = Category
|
||||
.field-date-format = Date format
|
||||
.field-examples = Examples
|
||||
.field-file-extension = Display file extension
|
||||
.field-filename-only = Filename only
|
||||
.field-path-only = Path only
|
||||
.field-path-filename = Path and filename
|
||||
.field-bytes = Bytes
|
||||
.field-kilobytes = Kilobytes
|
||||
.field-megabytes = Megabytes
|
||||
.field-system-variable = System variable
|
||||
.field-diesel = Diesel expression
|
||||
.field-named-type = Named object type
|
||||
.field-object-type = Object type
|
||||
.field-select-object = Select object
|
||||
.field-expression = Field expression
|
||||
.field-help = Inserts a field: text that updates from the drawing, the date or another source.
|
||||
.field-cat-date = Date & Time
|
||||
.field-cat-document = Document
|
||||
.field-cat-other = Other
|
||||
.field-case-upper = Uppercase
|
||||
.field-case-lower = Lowercase
|
||||
.field-case-first = First capital
|
||||
.field-case-title = Title case
|
||||
.field-named-dimstyle = Dimstyle
|
||||
.field-named-tablestyle = Tablestyle
|
||||
.field-named-textstyle = Textstyle
|
||||
.field-prop-endpoint = EndPoint
|
||||
.field-prop-startpoint = StartPoint
|
||||
model =
|
||||
.create = Δημιουργία
|
||||
.boolean = Δυαδική πράξη
|
||||
|
||||
@@ -4042,6 +4042,36 @@ insert =
|
||||
.attdef-tag-spaces = The tag may not contain spaces.
|
||||
.attdef-positive = Value must be positive and nonzero.
|
||||
.attdef-angle = Requires valid numeric angle or second point.
|
||||
.field-title = Field
|
||||
.field-category = Category
|
||||
.field-date-format = Date format
|
||||
.field-examples = Examples
|
||||
.field-file-extension = Display file extension
|
||||
.field-filename-only = Filename only
|
||||
.field-path-only = Path only
|
||||
.field-path-filename = Path and filename
|
||||
.field-bytes = Bytes
|
||||
.field-kilobytes = Kilobytes
|
||||
.field-megabytes = Megabytes
|
||||
.field-system-variable = System variable
|
||||
.field-diesel = Diesel expression
|
||||
.field-named-type = Named object type
|
||||
.field-object-type = Object type
|
||||
.field-select-object = Select object
|
||||
.field-expression = Field expression
|
||||
.field-help = Inserts a field: text that updates from the drawing, the date or another source.
|
||||
.field-cat-date = Date & Time
|
||||
.field-cat-document = Document
|
||||
.field-cat-other = Other
|
||||
.field-case-upper = Uppercase
|
||||
.field-case-lower = Lowercase
|
||||
.field-case-first = First capital
|
||||
.field-case-title = Title case
|
||||
.field-named-dimstyle = Dimstyle
|
||||
.field-named-tablestyle = Tablestyle
|
||||
.field-named-textstyle = Textstyle
|
||||
.field-prop-endpoint = EndPoint
|
||||
.field-prop-startpoint = StartPoint
|
||||
model =
|
||||
.create = Create
|
||||
.boolean = Boolean
|
||||
|
||||
@@ -4041,6 +4041,36 @@ insert =
|
||||
.attdef-tag-spaces = The tag may not contain spaces.
|
||||
.attdef-positive = Value must be positive and nonzero.
|
||||
.attdef-angle = Requires valid numeric angle or second point.
|
||||
.field-title = Field
|
||||
.field-category = Category
|
||||
.field-date-format = Date format
|
||||
.field-examples = Examples
|
||||
.field-file-extension = Display file extension
|
||||
.field-filename-only = Filename only
|
||||
.field-path-only = Path only
|
||||
.field-path-filename = Path and filename
|
||||
.field-bytes = Bytes
|
||||
.field-kilobytes = Kilobytes
|
||||
.field-megabytes = Megabytes
|
||||
.field-system-variable = System variable
|
||||
.field-diesel = Diesel expression
|
||||
.field-named-type = Named object type
|
||||
.field-object-type = Object type
|
||||
.field-select-object = Select object
|
||||
.field-expression = Field expression
|
||||
.field-help = Inserts a field: text that updates from the drawing, the date or another source.
|
||||
.field-cat-date = Date & Time
|
||||
.field-cat-document = Document
|
||||
.field-cat-other = Other
|
||||
.field-case-upper = Uppercase
|
||||
.field-case-lower = Lowercase
|
||||
.field-case-first = First capital
|
||||
.field-case-title = Title case
|
||||
.field-named-dimstyle = Dimstyle
|
||||
.field-named-tablestyle = Tablestyle
|
||||
.field-named-textstyle = Textstyle
|
||||
.field-prop-endpoint = EndPoint
|
||||
.field-prop-startpoint = StartPoint
|
||||
model =
|
||||
.create = Crear
|
||||
.boolean = Booleanas
|
||||
|
||||
@@ -4040,6 +4040,36 @@ insert =
|
||||
.attdef-tag-spaces = The tag may not contain spaces.
|
||||
.attdef-positive = Value must be positive and nonzero.
|
||||
.attdef-angle = Requires valid numeric angle or second point.
|
||||
.field-title = Field
|
||||
.field-category = Category
|
||||
.field-date-format = Date format
|
||||
.field-examples = Examples
|
||||
.field-file-extension = Display file extension
|
||||
.field-filename-only = Filename only
|
||||
.field-path-only = Path only
|
||||
.field-path-filename = Path and filename
|
||||
.field-bytes = Bytes
|
||||
.field-kilobytes = Kilobytes
|
||||
.field-megabytes = Megabytes
|
||||
.field-system-variable = System variable
|
||||
.field-diesel = Diesel expression
|
||||
.field-named-type = Named object type
|
||||
.field-object-type = Object type
|
||||
.field-select-object = Select object
|
||||
.field-expression = Field expression
|
||||
.field-help = Inserts a field: text that updates from the drawing, the date or another source.
|
||||
.field-cat-date = Date & Time
|
||||
.field-cat-document = Document
|
||||
.field-cat-other = Other
|
||||
.field-case-upper = Uppercase
|
||||
.field-case-lower = Lowercase
|
||||
.field-case-first = First capital
|
||||
.field-case-title = Title case
|
||||
.field-named-dimstyle = Dimstyle
|
||||
.field-named-tablestyle = Tablestyle
|
||||
.field-named-textstyle = Textstyle
|
||||
.field-prop-endpoint = EndPoint
|
||||
.field-prop-startpoint = StartPoint
|
||||
model =
|
||||
.create = Luo
|
||||
.boolean = Boolen operaatiot
|
||||
|
||||
@@ -4041,6 +4041,36 @@ insert =
|
||||
.attdef-tag-spaces = The tag may not contain spaces.
|
||||
.attdef-positive = Value must be positive and nonzero.
|
||||
.attdef-angle = Requires valid numeric angle or second point.
|
||||
.field-title = Field
|
||||
.field-category = Category
|
||||
.field-date-format = Date format
|
||||
.field-examples = Examples
|
||||
.field-file-extension = Display file extension
|
||||
.field-filename-only = Filename only
|
||||
.field-path-only = Path only
|
||||
.field-path-filename = Path and filename
|
||||
.field-bytes = Bytes
|
||||
.field-kilobytes = Kilobytes
|
||||
.field-megabytes = Megabytes
|
||||
.field-system-variable = System variable
|
||||
.field-diesel = Diesel expression
|
||||
.field-named-type = Named object type
|
||||
.field-object-type = Object type
|
||||
.field-select-object = Select object
|
||||
.field-expression = Field expression
|
||||
.field-help = Inserts a field: text that updates from the drawing, the date or another source.
|
||||
.field-cat-date = Date & Time
|
||||
.field-cat-document = Document
|
||||
.field-cat-other = Other
|
||||
.field-case-upper = Uppercase
|
||||
.field-case-lower = Lowercase
|
||||
.field-case-first = First capital
|
||||
.field-case-title = Title case
|
||||
.field-named-dimstyle = Dimstyle
|
||||
.field-named-tablestyle = Tablestyle
|
||||
.field-named-textstyle = Textstyle
|
||||
.field-prop-endpoint = EndPoint
|
||||
.field-prop-startpoint = StartPoint
|
||||
model =
|
||||
.create = Créer
|
||||
.boolean = Opérations booléennes
|
||||
|
||||
@@ -4029,6 +4029,36 @@ insert =
|
||||
.attdef-tag-spaces = The tag may not contain spaces.
|
||||
.attdef-positive = Value must be positive and nonzero.
|
||||
.attdef-angle = Requires valid numeric angle or second point.
|
||||
.field-title = Field
|
||||
.field-category = Category
|
||||
.field-date-format = Date format
|
||||
.field-examples = Examples
|
||||
.field-file-extension = Display file extension
|
||||
.field-filename-only = Filename only
|
||||
.field-path-only = Path only
|
||||
.field-path-filename = Path and filename
|
||||
.field-bytes = Bytes
|
||||
.field-kilobytes = Kilobytes
|
||||
.field-megabytes = Megabytes
|
||||
.field-system-variable = System variable
|
||||
.field-diesel = Diesel expression
|
||||
.field-named-type = Named object type
|
||||
.field-object-type = Object type
|
||||
.field-select-object = Select object
|
||||
.field-expression = Field expression
|
||||
.field-help = Inserts a field: text that updates from the drawing, the date or another source.
|
||||
.field-cat-date = Date & Time
|
||||
.field-cat-document = Document
|
||||
.field-cat-other = Other
|
||||
.field-case-upper = Uppercase
|
||||
.field-case-lower = Lowercase
|
||||
.field-case-first = First capital
|
||||
.field-case-title = Title case
|
||||
.field-named-dimstyle = Dimstyle
|
||||
.field-named-tablestyle = Tablestyle
|
||||
.field-named-textstyle = Textstyle
|
||||
.field-prop-endpoint = EndPoint
|
||||
.field-prop-startpoint = StartPoint
|
||||
model =
|
||||
.create = बनाएँ
|
||||
.boolean = बूलियन संक्रियाएँ
|
||||
|
||||
@@ -4040,6 +4040,36 @@ insert =
|
||||
.attdef-tag-spaces = The tag may not contain spaces.
|
||||
.attdef-positive = Value must be positive and nonzero.
|
||||
.attdef-angle = Requires valid numeric angle or second point.
|
||||
.field-title = Field
|
||||
.field-category = Category
|
||||
.field-date-format = Date format
|
||||
.field-examples = Examples
|
||||
.field-file-extension = Display file extension
|
||||
.field-filename-only = Filename only
|
||||
.field-path-only = Path only
|
||||
.field-path-filename = Path and filename
|
||||
.field-bytes = Bytes
|
||||
.field-kilobytes = Kilobytes
|
||||
.field-megabytes = Megabytes
|
||||
.field-system-variable = System variable
|
||||
.field-diesel = Diesel expression
|
||||
.field-named-type = Named object type
|
||||
.field-object-type = Object type
|
||||
.field-select-object = Select object
|
||||
.field-expression = Field expression
|
||||
.field-help = Inserts a field: text that updates from the drawing, the date or another source.
|
||||
.field-cat-date = Date & Time
|
||||
.field-cat-document = Document
|
||||
.field-cat-other = Other
|
||||
.field-case-upper = Uppercase
|
||||
.field-case-lower = Lowercase
|
||||
.field-case-first = First capital
|
||||
.field-case-title = Title case
|
||||
.field-named-dimstyle = Dimstyle
|
||||
.field-named-tablestyle = Tablestyle
|
||||
.field-named-textstyle = Textstyle
|
||||
.field-prop-endpoint = EndPoint
|
||||
.field-prop-startpoint = StartPoint
|
||||
model =
|
||||
.create = Létrehozás
|
||||
.boolean = Logikai műveletek
|
||||
|
||||
@@ -4040,6 +4040,36 @@ insert =
|
||||
.attdef-tag-spaces = The tag may not contain spaces.
|
||||
.attdef-positive = Value must be positive and nonzero.
|
||||
.attdef-angle = Requires valid numeric angle or second point.
|
||||
.field-title = Field
|
||||
.field-category = Category
|
||||
.field-date-format = Date format
|
||||
.field-examples = Examples
|
||||
.field-file-extension = Display file extension
|
||||
.field-filename-only = Filename only
|
||||
.field-path-only = Path only
|
||||
.field-path-filename = Path and filename
|
||||
.field-bytes = Bytes
|
||||
.field-kilobytes = Kilobytes
|
||||
.field-megabytes = Megabytes
|
||||
.field-system-variable = System variable
|
||||
.field-diesel = Diesel expression
|
||||
.field-named-type = Named object type
|
||||
.field-object-type = Object type
|
||||
.field-select-object = Select object
|
||||
.field-expression = Field expression
|
||||
.field-help = Inserts a field: text that updates from the drawing, the date or another source.
|
||||
.field-cat-date = Date & Time
|
||||
.field-cat-document = Document
|
||||
.field-cat-other = Other
|
||||
.field-case-upper = Uppercase
|
||||
.field-case-lower = Lowercase
|
||||
.field-case-first = First capital
|
||||
.field-case-title = Title case
|
||||
.field-named-dimstyle = Dimstyle
|
||||
.field-named-tablestyle = Tablestyle
|
||||
.field-named-textstyle = Textstyle
|
||||
.field-prop-endpoint = EndPoint
|
||||
.field-prop-startpoint = StartPoint
|
||||
model =
|
||||
.create = Crea
|
||||
.boolean = Operazioni booleane
|
||||
|
||||
@@ -4029,6 +4029,36 @@ insert =
|
||||
.attdef-tag-spaces = The tag may not contain spaces.
|
||||
.attdef-positive = Value must be positive and nonzero.
|
||||
.attdef-angle = Requires valid numeric angle or second point.
|
||||
.field-title = Field
|
||||
.field-category = Category
|
||||
.field-date-format = Date format
|
||||
.field-examples = Examples
|
||||
.field-file-extension = Display file extension
|
||||
.field-filename-only = Filename only
|
||||
.field-path-only = Path only
|
||||
.field-path-filename = Path and filename
|
||||
.field-bytes = Bytes
|
||||
.field-kilobytes = Kilobytes
|
||||
.field-megabytes = Megabytes
|
||||
.field-system-variable = System variable
|
||||
.field-diesel = Diesel expression
|
||||
.field-named-type = Named object type
|
||||
.field-object-type = Object type
|
||||
.field-select-object = Select object
|
||||
.field-expression = Field expression
|
||||
.field-help = Inserts a field: text that updates from the drawing, the date or another source.
|
||||
.field-cat-date = Date & Time
|
||||
.field-cat-document = Document
|
||||
.field-cat-other = Other
|
||||
.field-case-upper = Uppercase
|
||||
.field-case-lower = Lowercase
|
||||
.field-case-first = First capital
|
||||
.field-case-title = Title case
|
||||
.field-named-dimstyle = Dimstyle
|
||||
.field-named-tablestyle = Tablestyle
|
||||
.field-named-textstyle = Textstyle
|
||||
.field-prop-endpoint = EndPoint
|
||||
.field-prop-startpoint = StartPoint
|
||||
model =
|
||||
.create = 作成
|
||||
.boolean = ブール演算
|
||||
|
||||
@@ -4040,6 +4040,36 @@ insert =
|
||||
.attdef-tag-spaces = The tag may not contain spaces.
|
||||
.attdef-positive = Value must be positive and nonzero.
|
||||
.attdef-angle = Requires valid numeric angle or second point.
|
||||
.field-title = Field
|
||||
.field-category = Category
|
||||
.field-date-format = Date format
|
||||
.field-examples = Examples
|
||||
.field-file-extension = Display file extension
|
||||
.field-filename-only = Filename only
|
||||
.field-path-only = Path only
|
||||
.field-path-filename = Path and filename
|
||||
.field-bytes = Bytes
|
||||
.field-kilobytes = Kilobytes
|
||||
.field-megabytes = Megabytes
|
||||
.field-system-variable = System variable
|
||||
.field-diesel = Diesel expression
|
||||
.field-named-type = Named object type
|
||||
.field-object-type = Object type
|
||||
.field-select-object = Select object
|
||||
.field-expression = Field expression
|
||||
.field-help = Inserts a field: text that updates from the drawing, the date or another source.
|
||||
.field-cat-date = Date & Time
|
||||
.field-cat-document = Document
|
||||
.field-cat-other = Other
|
||||
.field-case-upper = Uppercase
|
||||
.field-case-lower = Lowercase
|
||||
.field-case-first = First capital
|
||||
.field-case-title = Title case
|
||||
.field-named-dimstyle = Dimstyle
|
||||
.field-named-tablestyle = Tablestyle
|
||||
.field-named-textstyle = Textstyle
|
||||
.field-prop-endpoint = EndPoint
|
||||
.field-prop-startpoint = StartPoint
|
||||
model =
|
||||
.create = 생성
|
||||
.boolean = 불리언 연산
|
||||
|
||||
@@ -4039,6 +4039,36 @@ insert =
|
||||
.attdef-tag-spaces = The tag may not contain spaces.
|
||||
.attdef-positive = Value must be positive and nonzero.
|
||||
.attdef-angle = Requires valid numeric angle or second point.
|
||||
.field-title = Field
|
||||
.field-category = Category
|
||||
.field-date-format = Date format
|
||||
.field-examples = Examples
|
||||
.field-file-extension = Display file extension
|
||||
.field-filename-only = Filename only
|
||||
.field-path-only = Path only
|
||||
.field-path-filename = Path and filename
|
||||
.field-bytes = Bytes
|
||||
.field-kilobytes = Kilobytes
|
||||
.field-megabytes = Megabytes
|
||||
.field-system-variable = System variable
|
||||
.field-diesel = Diesel expression
|
||||
.field-named-type = Named object type
|
||||
.field-object-type = Object type
|
||||
.field-select-object = Select object
|
||||
.field-expression = Field expression
|
||||
.field-help = Inserts a field: text that updates from the drawing, the date or another source.
|
||||
.field-cat-date = Date & Time
|
||||
.field-cat-document = Document
|
||||
.field-cat-other = Other
|
||||
.field-case-upper = Uppercase
|
||||
.field-case-lower = Lowercase
|
||||
.field-case-first = First capital
|
||||
.field-case-title = Title case
|
||||
.field-named-dimstyle = Dimstyle
|
||||
.field-named-tablestyle = Tablestyle
|
||||
.field-named-textstyle = Textstyle
|
||||
.field-prop-endpoint = EndPoint
|
||||
.field-prop-startpoint = StartPoint
|
||||
model =
|
||||
.create = Maken
|
||||
.boolean = Booleaanse bewerkingen
|
||||
|
||||
@@ -4040,6 +4040,36 @@ insert =
|
||||
.attdef-tag-spaces = The tag may not contain spaces.
|
||||
.attdef-positive = Value must be positive and nonzero.
|
||||
.attdef-angle = Requires valid numeric angle or second point.
|
||||
.field-title = Field
|
||||
.field-category = Category
|
||||
.field-date-format = Date format
|
||||
.field-examples = Examples
|
||||
.field-file-extension = Display file extension
|
||||
.field-filename-only = Filename only
|
||||
.field-path-only = Path only
|
||||
.field-path-filename = Path and filename
|
||||
.field-bytes = Bytes
|
||||
.field-kilobytes = Kilobytes
|
||||
.field-megabytes = Megabytes
|
||||
.field-system-variable = System variable
|
||||
.field-diesel = Diesel expression
|
||||
.field-named-type = Named object type
|
||||
.field-object-type = Object type
|
||||
.field-select-object = Select object
|
||||
.field-expression = Field expression
|
||||
.field-help = Inserts a field: text that updates from the drawing, the date or another source.
|
||||
.field-cat-date = Date & Time
|
||||
.field-cat-document = Document
|
||||
.field-cat-other = Other
|
||||
.field-case-upper = Uppercase
|
||||
.field-case-lower = Lowercase
|
||||
.field-case-first = First capital
|
||||
.field-case-title = Title case
|
||||
.field-named-dimstyle = Dimstyle
|
||||
.field-named-tablestyle = Tablestyle
|
||||
.field-named-textstyle = Textstyle
|
||||
.field-prop-endpoint = EndPoint
|
||||
.field-prop-startpoint = StartPoint
|
||||
model =
|
||||
.create = Utwórz
|
||||
.boolean = Operacje boolowskie
|
||||
|
||||
@@ -4041,6 +4041,36 @@ insert =
|
||||
.attdef-tag-spaces = The tag may not contain spaces.
|
||||
.attdef-positive = Value must be positive and nonzero.
|
||||
.attdef-angle = Requires valid numeric angle or second point.
|
||||
.field-title = Field
|
||||
.field-category = Category
|
||||
.field-date-format = Date format
|
||||
.field-examples = Examples
|
||||
.field-file-extension = Display file extension
|
||||
.field-filename-only = Filename only
|
||||
.field-path-only = Path only
|
||||
.field-path-filename = Path and filename
|
||||
.field-bytes = Bytes
|
||||
.field-kilobytes = Kilobytes
|
||||
.field-megabytes = Megabytes
|
||||
.field-system-variable = System variable
|
||||
.field-diesel = Diesel expression
|
||||
.field-named-type = Named object type
|
||||
.field-object-type = Object type
|
||||
.field-select-object = Select object
|
||||
.field-expression = Field expression
|
||||
.field-help = Inserts a field: text that updates from the drawing, the date or another source.
|
||||
.field-cat-date = Date & Time
|
||||
.field-cat-document = Document
|
||||
.field-cat-other = Other
|
||||
.field-case-upper = Uppercase
|
||||
.field-case-lower = Lowercase
|
||||
.field-case-first = First capital
|
||||
.field-case-title = Title case
|
||||
.field-named-dimstyle = Dimstyle
|
||||
.field-named-tablestyle = Tablestyle
|
||||
.field-named-textstyle = Textstyle
|
||||
.field-prop-endpoint = EndPoint
|
||||
.field-prop-startpoint = StartPoint
|
||||
model =
|
||||
.create = Criar
|
||||
.boolean = Operações booleanas
|
||||
|
||||
@@ -4042,6 +4042,36 @@ insert =
|
||||
.attdef-tag-spaces = The tag may not contain spaces.
|
||||
.attdef-positive = Value must be positive and nonzero.
|
||||
.attdef-angle = Requires valid numeric angle or second point.
|
||||
.field-title = Field
|
||||
.field-category = Category
|
||||
.field-date-format = Date format
|
||||
.field-examples = Examples
|
||||
.field-file-extension = Display file extension
|
||||
.field-filename-only = Filename only
|
||||
.field-path-only = Path only
|
||||
.field-path-filename = Path and filename
|
||||
.field-bytes = Bytes
|
||||
.field-kilobytes = Kilobytes
|
||||
.field-megabytes = Megabytes
|
||||
.field-system-variable = System variable
|
||||
.field-diesel = Diesel expression
|
||||
.field-named-type = Named object type
|
||||
.field-object-type = Object type
|
||||
.field-select-object = Select object
|
||||
.field-expression = Field expression
|
||||
.field-help = Inserts a field: text that updates from the drawing, the date or another source.
|
||||
.field-cat-date = Date & Time
|
||||
.field-cat-document = Document
|
||||
.field-cat-other = Other
|
||||
.field-case-upper = Uppercase
|
||||
.field-case-lower = Lowercase
|
||||
.field-case-first = First capital
|
||||
.field-case-title = Title case
|
||||
.field-named-dimstyle = Dimstyle
|
||||
.field-named-tablestyle = Tablestyle
|
||||
.field-named-textstyle = Textstyle
|
||||
.field-prop-endpoint = EndPoint
|
||||
.field-prop-startpoint = StartPoint
|
||||
model =
|
||||
.create = Создание
|
||||
.boolean = Булевы операции
|
||||
|
||||
@@ -4001,6 +4001,36 @@ insert =
|
||||
.attdef-tag-spaces = Etiket boşluk içeremez.
|
||||
.attdef-positive = Değer pozitif ve sıfırdan farklı olmalı.
|
||||
.attdef-angle = Geçerli bir sayısal açı veya ikinci nokta gerekir.
|
||||
.field-title = Alan
|
||||
.field-category = Kategori
|
||||
.field-date-format = Tarih biçimi
|
||||
.field-examples = Örnekler
|
||||
.field-file-extension = Dosya uzantısını göster
|
||||
.field-filename-only = Yalnız dosya adı
|
||||
.field-path-only = Yalnız yol
|
||||
.field-path-filename = Yol ve dosya adı
|
||||
.field-bytes = Bayt
|
||||
.field-kilobytes = Kilobayt
|
||||
.field-megabytes = Megabayt
|
||||
.field-system-variable = Sistem değişkeni
|
||||
.field-diesel = Diesel ifadesi
|
||||
.field-named-type = Adlandırılmış nesne türü
|
||||
.field-object-type = Nesne türü
|
||||
.field-select-object = Nesne seç
|
||||
.field-expression = Alan ifadesi
|
||||
.field-help = Bir alan ekler: çizimden, tarihten veya başka bir kaynaktan güncellenen metin.
|
||||
.field-cat-date = Tarih ve Saat
|
||||
.field-cat-document = Belge
|
||||
.field-cat-other = Diğer
|
||||
.field-case-upper = Büyük harf
|
||||
.field-case-lower = Küçük harf
|
||||
.field-case-first = İlk harf büyük
|
||||
.field-case-title = Başlık
|
||||
.field-named-dimstyle = Ölçü stili
|
||||
.field-named-tablestyle = Tablo stili
|
||||
.field-named-textstyle = Metin stili
|
||||
.field-prop-endpoint = Bitiş noktası
|
||||
.field-prop-startpoint = Başlangıç noktası
|
||||
model =
|
||||
.create = Oluştur
|
||||
.boolean = Boole işlemleri
|
||||
|
||||
@@ -4024,6 +4024,36 @@ insert =
|
||||
.attdef-tag-spaces = The tag may not contain spaces.
|
||||
.attdef-positive = Value must be positive and nonzero.
|
||||
.attdef-angle = Requires valid numeric angle or second point.
|
||||
.field-title = Field
|
||||
.field-category = Category
|
||||
.field-date-format = Date format
|
||||
.field-examples = Examples
|
||||
.field-file-extension = Display file extension
|
||||
.field-filename-only = Filename only
|
||||
.field-path-only = Path only
|
||||
.field-path-filename = Path and filename
|
||||
.field-bytes = Bytes
|
||||
.field-kilobytes = Kilobytes
|
||||
.field-megabytes = Megabytes
|
||||
.field-system-variable = System variable
|
||||
.field-diesel = Diesel expression
|
||||
.field-named-type = Named object type
|
||||
.field-object-type = Object type
|
||||
.field-select-object = Select object
|
||||
.field-expression = Field expression
|
||||
.field-help = Inserts a field: text that updates from the drawing, the date or another source.
|
||||
.field-cat-date = Date & Time
|
||||
.field-cat-document = Document
|
||||
.field-cat-other = Other
|
||||
.field-case-upper = Uppercase
|
||||
.field-case-lower = Lowercase
|
||||
.field-case-first = First capital
|
||||
.field-case-title = Title case
|
||||
.field-named-dimstyle = Dimstyle
|
||||
.field-named-tablestyle = Tablestyle
|
||||
.field-named-textstyle = Textstyle
|
||||
.field-prop-endpoint = EndPoint
|
||||
.field-prop-startpoint = StartPoint
|
||||
model =
|
||||
.create = 创建
|
||||
.boolean = 布尔运算
|
||||
|
||||
@@ -4036,6 +4036,36 @@ insert =
|
||||
.attdef-tag-spaces = The tag may not contain spaces.
|
||||
.attdef-positive = Value must be positive and nonzero.
|
||||
.attdef-angle = Requires valid numeric angle or second point.
|
||||
.field-title = Field
|
||||
.field-category = Category
|
||||
.field-date-format = Date format
|
||||
.field-examples = Examples
|
||||
.field-file-extension = Display file extension
|
||||
.field-filename-only = Filename only
|
||||
.field-path-only = Path only
|
||||
.field-path-filename = Path and filename
|
||||
.field-bytes = Bytes
|
||||
.field-kilobytes = Kilobytes
|
||||
.field-megabytes = Megabytes
|
||||
.field-system-variable = System variable
|
||||
.field-diesel = Diesel expression
|
||||
.field-named-type = Named object type
|
||||
.field-object-type = Object type
|
||||
.field-select-object = Select object
|
||||
.field-expression = Field expression
|
||||
.field-help = Inserts a field: text that updates from the drawing, the date or another source.
|
||||
.field-cat-date = Date & Time
|
||||
.field-cat-document = Document
|
||||
.field-cat-other = Other
|
||||
.field-case-upper = Uppercase
|
||||
.field-case-lower = Lowercase
|
||||
.field-case-first = First capital
|
||||
.field-case-title = Title case
|
||||
.field-named-dimstyle = Dimstyle
|
||||
.field-named-tablestyle = Tablestyle
|
||||
.field-named-textstyle = Textstyle
|
||||
.field-prop-endpoint = EndPoint
|
||||
.field-prop-startpoint = StartPoint
|
||||
model =
|
||||
.create = 建立
|
||||
.boolean = 布林運算
|
||||
|
||||
@@ -226,8 +226,17 @@ impl OpenCADStudio {
|
||||
self.tabs[i].scene.document.header.text_height = attribute.height;
|
||||
}
|
||||
}
|
||||
// A field the committed text shows (FIELD, an ATTDEF Default from the
|
||||
// Field dialog) is attached once the entity has its handle; the field
|
||||
// objects are not part of the add delta, so the step is a snapshot.
|
||||
let text_field = self.tabs[i].active_cmd.as_ref().and_then(|cmd| cmd.text_field());
|
||||
let label = self.history_label_from_active_cmd(i, "ENTITY");
|
||||
let delta_safe = self.delta_add_safe(i, &entity);
|
||||
let delta_safe = text_field.is_none() && self.delta_add_safe(i, &entity);
|
||||
let field_value = match &entity {
|
||||
codec::EntityType::MText(m) => m.value.clone(),
|
||||
codec::EntityType::AttributeDefinition(a) => a.default_value.clone(),
|
||||
_ => String::new(),
|
||||
};
|
||||
let pending = self.begin_undo(i, label, 1, delta_safe);
|
||||
let is_associative_dimension = matches!(
|
||||
entity,
|
||||
@@ -247,6 +256,14 @@ impl OpenCADStudio {
|
||||
.attach_dimension_association(handle, sources);
|
||||
}
|
||||
}
|
||||
if let (Some(handle), Some(field)) = (committed, text_field) {
|
||||
crate::entities::field::set_text_field(
|
||||
&mut self.tabs[i].scene.document,
|
||||
handle,
|
||||
Some(field),
|
||||
&field_value,
|
||||
);
|
||||
}
|
||||
if let Some(handle) = committed {
|
||||
self.apply_continuous_constraints(i, &[handle]);
|
||||
}
|
||||
|
||||
@@ -54,6 +54,7 @@ impl OpenCADStudio {
|
||||
if let Some(state) = self.attdef_dialog.as_mut() {
|
||||
if let Some(value) = value {
|
||||
state.default = value;
|
||||
state.field = None;
|
||||
}
|
||||
self.active_modal = Some(crate::app::ModalKind::AttDef);
|
||||
}
|
||||
@@ -134,6 +135,7 @@ impl OpenCADStudio {
|
||||
align_below: false,
|
||||
can_align_below: session.last.is_some(),
|
||||
error: None,
|
||||
field: None,
|
||||
});
|
||||
drop(session);
|
||||
self.tabs[i].active_cmd = None;
|
||||
@@ -154,6 +156,8 @@ impl OpenCADStudio {
|
||||
default: a.default_value.clone(),
|
||||
constant: a.flags.constant,
|
||||
error: None,
|
||||
field: None,
|
||||
field_changed: false,
|
||||
});
|
||||
self.active_modal = Some(crate::app::ModalKind::AttDefEdit);
|
||||
}
|
||||
@@ -164,7 +168,15 @@ impl OpenCADStudio {
|
||||
match m {
|
||||
AttdefDialogMsg::EditTag(v) => edit.tag = v,
|
||||
AttdefDialogMsg::EditPrompt(v) => edit.prompt = v,
|
||||
AttdefDialogMsg::EditDefault(v) => edit.default = v,
|
||||
AttdefDialogMsg::EditDefault(v) => {
|
||||
edit.default = v;
|
||||
edit.field = None;
|
||||
edit.field_changed = true;
|
||||
}
|
||||
AttdefDialogMsg::EditInsertField => {
|
||||
self.active_modal = None;
|
||||
self.open_field_dialog(crate::ui::window::field_dialog::FieldTarget::AttdefEdit);
|
||||
}
|
||||
AttdefDialogMsg::Help => self.command_line.push_info(
|
||||
crate::t!("Changes the tag, prompt and default value of an attribute definition.")
|
||||
.as_ref(),
|
||||
@@ -189,7 +201,15 @@ impl OpenCADStudio {
|
||||
AttdefDialogMsg::Annotative(v) => state.annotative = v,
|
||||
AttdefDialogMsg::Tag(v) => state.tag = v,
|
||||
AttdefDialogMsg::Prompt(v) => state.prompt = v,
|
||||
AttdefDialogMsg::Default(v) => state.default = v,
|
||||
AttdefDialogMsg::Default(v) => {
|
||||
state.default = v;
|
||||
state.field = None;
|
||||
}
|
||||
AttdefDialogMsg::InsertField => {
|
||||
self.active_modal = None;
|
||||
self.open_field_dialog(crate::ui::window::field_dialog::FieldTarget::AttdefDefault);
|
||||
return Task::none();
|
||||
}
|
||||
AttdefDialogMsg::Justify(c) => state.justify = c.0,
|
||||
AttdefDialogMsg::Style(v) => state.style = v,
|
||||
AttdefDialogMsg::Height(v) => state.height = v,
|
||||
@@ -279,6 +299,7 @@ impl OpenCADStudio {
|
||||
oblique_angle: defaults.oblique_angle,
|
||||
boundary_width: width,
|
||||
line_spacing: 1.0,
|
||||
field: state.field.clone(),
|
||||
};
|
||||
self.attdef_dialog = None;
|
||||
self.close_active_modal();
|
||||
@@ -336,7 +357,7 @@ impl OpenCADStudio {
|
||||
self.tabs[i].scene.document.get_entity(state.handle),
|
||||
Some(codec::EntityType::AttributeDefinition(a))
|
||||
if a.tag != attdef::normalize_tag(&state.tag) || a.prompt != state.prompt || a.default_value != state.default
|
||||
);
|
||||
) || state.field_changed;
|
||||
if changed && !self.reject_locked_edit(i, state.handle) {
|
||||
self.push_undo_snapshot(i, "DDEDIT");
|
||||
if let Some(codec::EntityType::AttributeDefinition(a)) =
|
||||
@@ -348,6 +369,14 @@ impl OpenCADStudio {
|
||||
}
|
||||
a.default_value = state.default.clone();
|
||||
}
|
||||
if state.field_changed {
|
||||
crate::entities::field::set_text_field(
|
||||
&mut self.tabs[i].scene.document,
|
||||
state.handle,
|
||||
state.field.clone(),
|
||||
&state.default,
|
||||
);
|
||||
}
|
||||
self.tabs[i]
|
||||
.scene
|
||||
.bump_entities(&[(state.handle, crate::scene::ChangeKind::Modified)]);
|
||||
|
||||
@@ -0,0 +1,206 @@
|
||||
//! FIELD and the Field dialog behind the ƒ buttons of the attribute
|
||||
//! definition dialogs.
|
||||
|
||||
use crate::app::{Message, OpenCADStudio};
|
||||
use crate::command::CadCommand;
|
||||
use crate::modules::annotate::field_cmd::{FieldObjectPickCommand, FieldPlaceCommand};
|
||||
use crate::ui::window::field_dialog::{
|
||||
fields_of, object_properties, FieldDialogMsg, FieldDialogState, FieldTarget, DATE_FORMATS,
|
||||
NAMED_TYPES,
|
||||
};
|
||||
use iced::Task;
|
||||
|
||||
impl OpenCADStudio {
|
||||
pub(super) fn dispatch_field(&mut self, cmd: &str, i: usize) -> Option<Task<Message>> {
|
||||
match cmd {
|
||||
"FIELD" => {
|
||||
self.open_field_dialog(FieldTarget::NewText);
|
||||
Some(Task::none())
|
||||
}
|
||||
cmd if cmd == "_FIELD_OBJECT" || cmd.starts_with("_FIELD_OBJECT ") => {
|
||||
let handle = cmd
|
||||
.split_whitespace()
|
||||
.nth(1)
|
||||
.and_then(|h| u64::from_str_radix(h, 16).ok())
|
||||
.map(codec::Handle::new);
|
||||
if let Some(state) = self.field_dialog.as_mut() {
|
||||
if let Some(entity) = handle.and_then(|h| self.tabs[i].scene.document.get_entity(h)) {
|
||||
let props = object_properties(entity);
|
||||
state.object = Some((entity.common().handle, crate::t!(crate::entities::names::ui_name(entity)).into_owned()));
|
||||
state.object_prop = props.first().copied();
|
||||
state.object_props = props;
|
||||
}
|
||||
self.active_modal = Some(crate::app::ModalKind::Field);
|
||||
self.refresh_field_preview();
|
||||
}
|
||||
Some(Task::none())
|
||||
}
|
||||
_ => None,
|
||||
}
|
||||
}
|
||||
|
||||
/// Open the Field dialog for `target`.
|
||||
pub(in crate::app) fn open_field_dialog(&mut self, target: FieldTarget) {
|
||||
let mut state = FieldDialogState::new(target);
|
||||
state.examples = DATE_FORMATS
|
||||
.iter()
|
||||
.map(|f| self.field_value(&format!("\\AcVar Date \\f \"{f}\""), &[]))
|
||||
.collect();
|
||||
self.field_dialog = Some(state);
|
||||
self.fill_named_objects();
|
||||
self.refresh_field_preview();
|
||||
self.active_modal = Some(crate::app::ModalKind::Field);
|
||||
}
|
||||
|
||||
fn field_value(&self, code: &str, objects: &[codec::Handle]) -> String {
|
||||
crate::entities::field::evaluate(&self.tabs[self.active_tab].scene.document, code, objects, None)
|
||||
.unwrap_or_else(|| "----".to_string())
|
||||
}
|
||||
|
||||
fn refresh_field_preview(&mut self) {
|
||||
let Some(state) = self.field_dialog.as_ref() else {
|
||||
return;
|
||||
};
|
||||
let (code, objects) = state.code();
|
||||
let preview = if state.complete() { self.field_value(&code, &objects) } else { String::new() };
|
||||
if let Some(state) = self.field_dialog.as_mut() {
|
||||
state.preview = preview;
|
||||
}
|
||||
}
|
||||
|
||||
/// The names of the chosen named-object type, sorted.
|
||||
fn fill_named_objects(&mut self) {
|
||||
let Some(state) = self.field_dialog.as_mut() else {
|
||||
return;
|
||||
};
|
||||
let document = &self.tabs[self.active_tab].scene.document;
|
||||
let mut names: Vec<(String, codec::Handle)> = match NAMED_TYPES[state.named_type] {
|
||||
"Block" => document
|
||||
.block_records
|
||||
.iter()
|
||||
.filter(|b| !b.name.starts_with('*'))
|
||||
.map(|b| (b.name.clone(), b.handle))
|
||||
.collect(),
|
||||
"Dimstyle" => document.dim_styles.iter().map(|s| (s.name.clone(), s.handle)).collect(),
|
||||
"Layer" => document.layers.iter().map(|l| (l.name.clone(), l.handle)).collect(),
|
||||
"Linetype" => document.line_types.iter().map(|l| (l.name.clone(), l.handle)).collect(),
|
||||
"Textstyle" => document
|
||||
.text_styles
|
||||
.iter()
|
||||
.filter(|s| !s.name.is_empty())
|
||||
.map(|s| (s.name.clone(), s.handle))
|
||||
.collect(),
|
||||
"View" => document.views.iter().map(|v| (v.name.clone(), v.handle)).collect(),
|
||||
_ => Vec::new(),
|
||||
};
|
||||
names.sort_by_key(|(n, _)| n.to_lowercase());
|
||||
state.named_names = names;
|
||||
state.named = None;
|
||||
}
|
||||
|
||||
pub(in crate::app) fn on_field_dialog(&mut self, m: FieldDialogMsg) -> Task<Message> {
|
||||
let i = self.active_tab;
|
||||
let Some(state) = self.field_dialog.as_mut() else {
|
||||
return Task::none();
|
||||
};
|
||||
match m {
|
||||
FieldDialogMsg::Category(c) => {
|
||||
state.category = c;
|
||||
let names = fields_of(c);
|
||||
if !names.contains(&state.name) {
|
||||
state.name = names.first().copied().unwrap_or("Date");
|
||||
}
|
||||
}
|
||||
FieldDialogMsg::Name(n) => {
|
||||
state.name = n;
|
||||
state.text_case = 0;
|
||||
}
|
||||
FieldDialogMsg::DateFormat(f) => state.date_format = f,
|
||||
FieldDialogMsg::DateExample(k) => {
|
||||
if let Some(f) = DATE_FORMATS.get(k) {
|
||||
state.date_format = f.to_string();
|
||||
}
|
||||
}
|
||||
FieldDialogMsg::TextCase(k) => state.text_case = k,
|
||||
FieldDialogMsg::FileParts(b) => state.file_parts = b,
|
||||
FieldDialogMsg::FileExtension(v) => state.file_extension = v,
|
||||
FieldDialogMsg::SizeUnit(k) => state.size_unit = k,
|
||||
FieldDialogMsg::SysVar(v) => state.sysvar = v,
|
||||
FieldDialogMsg::Diesel(v) => state.diesel = v,
|
||||
FieldDialogMsg::NamedType(k) => {
|
||||
state.named_type = k;
|
||||
self.fill_named_objects();
|
||||
}
|
||||
FieldDialogMsg::Named(k) => state.named = Some(k),
|
||||
FieldDialogMsg::ObjectProp(p) => state.object_prop = Some(p),
|
||||
FieldDialogMsg::SelectObject => {
|
||||
// The dialog waits while one object is picked.
|
||||
self.active_modal = None;
|
||||
let command = FieldObjectPickCommand;
|
||||
self.command_line.push_info(&command.prompt());
|
||||
self.tabs[i].active_cmd = Some(Box::new(command));
|
||||
return Task::none();
|
||||
}
|
||||
FieldDialogMsg::Help => self.command_line.push_info(
|
||||
crate::t!("Inserts a field: text that updates from the drawing, the date or another source.").as_ref(),
|
||||
),
|
||||
FieldDialogMsg::Ok => return self.field_dialog_ok(i),
|
||||
}
|
||||
self.refresh_field_preview();
|
||||
Task::none()
|
||||
}
|
||||
|
||||
fn field_dialog_ok(&mut self, i: usize) -> Task<Message> {
|
||||
let Some(state) = self.field_dialog.take() else {
|
||||
return Task::none();
|
||||
};
|
||||
let (code, objects) = state.code();
|
||||
let value = state.preview.clone();
|
||||
let field = Some((code, objects));
|
||||
match state.target {
|
||||
FieldTarget::NewText => {
|
||||
self.close_active_modal();
|
||||
let defaults =
|
||||
crate::scene::creation_style::current_text_defaults(&self.tabs[i].scene.document);
|
||||
let command = FieldPlaceCommand::new(
|
||||
value,
|
||||
field.expect("set above"),
|
||||
defaults.style_name,
|
||||
defaults.height,
|
||||
);
|
||||
self.reset_command_start_state(i);
|
||||
self.command_line.push_info(&command.prompt());
|
||||
self.tabs[i].active_cmd = Some(Box::new(command));
|
||||
self.push_ucs_to_cmd(i);
|
||||
return self.focus_cmd_input();
|
||||
}
|
||||
FieldTarget::AttdefDefault => {
|
||||
if let Some(dialog) = self.attdef_dialog.as_mut() {
|
||||
dialog.default = value;
|
||||
dialog.field = field;
|
||||
}
|
||||
self.active_modal = Some(crate::app::ModalKind::AttDef);
|
||||
}
|
||||
FieldTarget::AttdefEdit => {
|
||||
if let Some(edit) = self.attdef_edit.as_mut() {
|
||||
edit.default = value;
|
||||
edit.field = field;
|
||||
edit.field_changed = true;
|
||||
}
|
||||
self.active_modal = Some(crate::app::ModalKind::AttDefEdit);
|
||||
}
|
||||
}
|
||||
Task::none()
|
||||
}
|
||||
|
||||
/// The dialogs a Field dialog was opened from come back when it closes.
|
||||
pub(in crate::app) fn close_field_dialog(&mut self) {
|
||||
let target = self.field_dialog.take().map(|s| s.target);
|
||||
self.close_active_modal();
|
||||
self.active_modal = match target {
|
||||
Some(FieldTarget::AttdefDefault) if self.attdef_dialog.is_some() => Some(crate::app::ModalKind::AttDef),
|
||||
Some(FieldTarget::AttdefEdit) if self.attdef_edit.is_some() => Some(crate::app::ModalKind::AttDefEdit),
|
||||
_ => None,
|
||||
};
|
||||
}
|
||||
}
|
||||
@@ -5,6 +5,7 @@ use iced::Task;
|
||||
use std::path::PathBuf;
|
||||
|
||||
mod attdef;
|
||||
mod field;
|
||||
mod blocks;
|
||||
mod xref_attach;
|
||||
mod dim;
|
||||
@@ -282,6 +283,9 @@ impl OpenCADStudio {
|
||||
if let Some(t) = self.dispatch_attdef(cmd, i) {
|
||||
return Some(t);
|
||||
}
|
||||
if let Some(t) = self.dispatch_field(cmd, i) {
|
||||
return Some(t);
|
||||
}
|
||||
if let Some(t) = self.dispatch_draw(cmd, i) {
|
||||
return Some(t);
|
||||
}
|
||||
|
||||
@@ -80,6 +80,7 @@ pub(super) const NAMES: &[&str] = &[
|
||||
"ribbon_dropdown",
|
||||
"dialog_ok",
|
||||
"attdef_dialog",
|
||||
"field_dialog",
|
||||
"close_document",
|
||||
"toggle_properties",
|
||||
"toggle_layers",
|
||||
@@ -870,6 +871,8 @@ impl OpenCADStudio {
|
||||
"align_below" => M::AlignBelow(on),
|
||||
"edit_value" => M::EditValue,
|
||||
"dismiss" => M::DismissError,
|
||||
"insert_field" if editing => M::EditInsertField,
|
||||
"insert_field" => M::InsertField,
|
||||
"x" => M::Coord(0, v.into()),
|
||||
"y" => M::Coord(1, v.into()),
|
||||
"z" => M::Coord(2, v.into()),
|
||||
@@ -887,6 +890,35 @@ impl OpenCADStudio {
|
||||
_ => return Err(failure("invalid_value", "Unknown attdef dialog field")),
|
||||
})
|
||||
}
|
||||
// One field of the open Field dialog: `category=<index>`, `name=Date`,
|
||||
// `date=yyyy-MM-dd`, `case=0..4`, `file=1|2|3`, `ext=0|1`, `size=0..2`,
|
||||
// `sysvar=dimscale`, `diesel=…`, `named_type=<index>`, `named=<index>`,
|
||||
// `prop=Area`, `select_object`.
|
||||
"field_dialog" => {
|
||||
use crate::ui::window::field_dialog::{self as fd, FieldDialogMsg as F};
|
||||
let value = string(req, "value")?;
|
||||
let (key, v) = value.split_once('=').unwrap_or((value, ""));
|
||||
let index = || v.parse::<usize>().map_err(|_| failure("invalid_value", "index expected"));
|
||||
let known = |list: &[&'static str]| {
|
||||
list.iter().copied().find(|n| n.eq_ignore_ascii_case(v)).ok_or_else(|| failure("invalid_value", "unknown name"))
|
||||
};
|
||||
Message::FieldDialog(match key {
|
||||
"category" => F::Category(index()?),
|
||||
"name" => F::Name(known(&fd::fields_of(0))?),
|
||||
"date" => F::DateFormat(v.into()),
|
||||
"case" => F::TextCase(index()?),
|
||||
"file" => F::FileParts(v.parse::<u8>().map_err(|_| failure("invalid_value", "1|2|3"))?),
|
||||
"ext" => F::FileExtension(v == "1"),
|
||||
"size" => F::SizeUnit(index()?),
|
||||
"sysvar" => F::SysVar(v.into()),
|
||||
"diesel" => F::Diesel(v.into()),
|
||||
"named_type" => F::NamedType(index()?),
|
||||
"named" => F::Named(index()?),
|
||||
"prop" => F::ObjectProp(known(&["Area", "Center", "Circumference", "Diameter", "EndPoint", "Length", "Radius", "StartPoint"])?),
|
||||
"select_object" => F::SelectObject,
|
||||
_ => return Err(failure("invalid_value", "Unknown field dialog key")),
|
||||
})
|
||||
}
|
||||
"ribbon_dropdown" => Message::ToggleRibbonDropdown(string(req, "value")?.into()),
|
||||
"pdf_dialog_ok" => {
|
||||
use crate::ui::window::pdf_dialogs::PdfDialogMsg;
|
||||
@@ -913,6 +945,9 @@ impl OpenCADStudio {
|
||||
Some(crate::app::ModalKind::AttDefEdit) => Message::AttdefDialog(
|
||||
crate::ui::window::attdef_dialog::AttdefDialogMsg::EditOk,
|
||||
),
|
||||
Some(crate::app::ModalKind::Field) => Message::FieldDialog(
|
||||
crate::ui::window::field_dialog::FieldDialogMsg::Ok,
|
||||
),
|
||||
_ => return Err(failure("no_dialog", "No dialog with an OK button is open")),
|
||||
},
|
||||
"close_document" => Message::TabClose(self.tabs[self.active_tab].id),
|
||||
|
||||
@@ -501,6 +501,8 @@ pub(super) struct OpenCADStudio {
|
||||
/// ATTDEF dialog and Edit Attribute Definition dialog; `None` while closed.
|
||||
attdef_dialog: Option<crate::ui::window::attdef_dialog::AttdefDialogState>,
|
||||
attdef_edit: Option<crate::ui::window::attdef_dialog::AttdefEditState>,
|
||||
/// Field dialog working copy; `None` while closed.
|
||||
field_dialog: Option<crate::ui::window::field_dialog::FieldDialogState>,
|
||||
point_cloud_color_map: Option<crate::ui::window::pdf_dialogs::PointCloudColorMapState>,
|
||||
pc_section: Option<crate::ui::window::pdf_dialogs::PcSectionState>,
|
||||
underlay_layers: Option<crate::ui::window::pdf_dialogs::UnderlayLayersState>,
|
||||
@@ -1922,6 +1924,7 @@ pub enum ModalKind {
|
||||
GeometricTolerance,
|
||||
AttDef,
|
||||
AttDefEdit,
|
||||
Field,
|
||||
DraftingSettings,
|
||||
AutoConstrainSettings,
|
||||
LayerStateEditor,
|
||||
@@ -3870,6 +3873,7 @@ pub enum Message {
|
||||
/// An edit in one of the PDF dialogs.
|
||||
PdfDialog(crate::ui::window::pdf_dialogs::PdfDialogMsg),
|
||||
AttdefDialog(crate::ui::window::attdef_dialog::AttdefDialogMsg),
|
||||
FieldDialog(crate::ui::window::field_dialog::FieldDialogMsg),
|
||||
/// A click or search in the Point Cloud Manager.
|
||||
PcManager(crate::ui::window::pc_manager::PcManagerMsg),
|
||||
PdfImportPickResult(Result<(std::path::PathBuf, std::sync::Arc<Vec<u8>>), String>),
|
||||
@@ -4065,6 +4069,7 @@ impl OpenCADStudio {
|
||||
point_cloud_attach: None,
|
||||
attdef_dialog: None,
|
||||
attdef_edit: None,
|
||||
field_dialog: None,
|
||||
point_cloud_color_map: None,
|
||||
pc_section: None,
|
||||
underlay_layers: None,
|
||||
|
||||
@@ -1813,6 +1813,7 @@ impl OpenCADStudio {
|
||||
|
||||
Message::PdfDialog(message) => self.update_pdf_dialog(message),
|
||||
Message::AttdefDialog(message) => self.on_attdef_dialog(message),
|
||||
Message::FieldDialog(message) => self.on_field_dialog(message),
|
||||
Message::PcManager(message) => self.update_pc_manager(message),
|
||||
Message::RibbonSelectTab(idx) => {
|
||||
self.ribbon.select(idx);
|
||||
@@ -8503,6 +8504,10 @@ impl OpenCADStudio {
|
||||
}
|
||||
|
||||
Message::CloseModal => {
|
||||
if self.active_modal == Some(super::ModalKind::Field) {
|
||||
self.close_field_dialog();
|
||||
return Task::none();
|
||||
}
|
||||
if matches!(
|
||||
self.active_modal,
|
||||
Some(super::ModalKind::AttDef | super::ModalKind::AttDefEdit)
|
||||
|
||||
@@ -40,6 +40,7 @@ impl OpenCADStudio {
|
||||
Some(K::GeometricTolerance) => crate::t!("Geometric Tolerance").into_owned(),
|
||||
Some(K::AttDef) => crate::t!("Attribute Definition").into_owned(),
|
||||
Some(K::AttDefEdit) => crate::t!("Edit Attribute Definition").into_owned(),
|
||||
Some(K::Field) => crate::t!("Field").into_owned(),
|
||||
Some(K::DraftingSettings) => crate::t!("Drafting Settings").into_owned(),
|
||||
Some(K::AutoConstrainSettings) => crate::t!("Constraint Settings").into_owned(),
|
||||
Some(K::LayerStateEditor) => crate::tr!("modal", "edit-layer-state"),
|
||||
@@ -578,6 +579,10 @@ impl OpenCADStudio {
|
||||
let state = self.attdef_dialog.as_ref()?;
|
||||
sized_flow(ex, 720, 520, |flow| crate::ui::window::attdef_dialog::view(state, flow))
|
||||
}
|
||||
super::super::ModalKind::Field => {
|
||||
let state = self.field_dialog.as_ref()?;
|
||||
sized_flow(ex, 760, 660, |flow| crate::ui::window::field_dialog::view(state, flow))
|
||||
}
|
||||
super::super::ModalKind::AttDefEdit => {
|
||||
let state = self.attdef_edit.as_ref()?;
|
||||
sized_flow(ex, 460, 220, |flow| crate::ui::window::attdef_dialog::view_edit(state, flow))
|
||||
|
||||
@@ -2628,6 +2628,12 @@ pub trait CadCommand: Send {
|
||||
/// Resume the command with collected rich text.
|
||||
fn on_editor_text(&mut self, _value: String) {}
|
||||
|
||||
/// A field (code and referenced objects) to attach to the entity this
|
||||
/// command commits.
|
||||
fn text_field(&self) -> Option<(String, Vec<Handle>)> {
|
||||
None
|
||||
}
|
||||
|
||||
fn on_editor_display_height(&mut self, _height: f64) {}
|
||||
|
||||
/// Called when the user clicks and `needs_entity_pick()` is true.
|
||||
|
||||
+176
-5
@@ -11,10 +11,11 @@ use codec::fields::FieldContext;
|
||||
use codec::types::Handle;
|
||||
use codec::CadDocument;
|
||||
|
||||
/// OCS's environment provider for field evaluation.
|
||||
struct OcsFieldContext;
|
||||
/// OCS's environment provider for field evaluation. With the document at
|
||||
/// hand it also answers the system variables the engine asks the host for.
|
||||
struct OcsFieldContext<'a>(Option<&'a CadDocument>);
|
||||
|
||||
impl FieldContext for OcsFieldContext {
|
||||
impl FieldContext for OcsFieldContext<'_> {
|
||||
fn now_julian(&self) -> f64 {
|
||||
// Unix epoch is Julian Day 2440587.5.
|
||||
epoch_secs() as f64 / 86_400.0 + 2_440_587.5
|
||||
@@ -34,6 +35,24 @@ impl FieldContext for OcsFieldContext {
|
||||
None
|
||||
}
|
||||
|
||||
fn getvar(&self, name: &str) -> Option<String> {
|
||||
self.0.and_then(|document| sysvar(document, name))
|
||||
}
|
||||
|
||||
fn file_size(&self) -> Option<u64> {
|
||||
#[cfg(not(target_arch = "wasm32"))]
|
||||
{
|
||||
let path = self.0?.source_path.as_deref()?;
|
||||
return std::fs::metadata(path).ok().map(|m| m.len());
|
||||
}
|
||||
#[cfg(target_arch = "wasm32")]
|
||||
None
|
||||
}
|
||||
|
||||
fn date_locale(&self) -> codec::fields::DateLocale {
|
||||
os_date_locale()
|
||||
}
|
||||
|
||||
fn getenv(&self, name: &str) -> Option<String> {
|
||||
#[cfg(not(target_arch = "wasm32"))]
|
||||
{
|
||||
@@ -50,7 +69,7 @@ impl FieldContext for OcsFieldContext {
|
||||
/// Re-evaluate the field hosted by entity `host` (usually an MTEXT), or `None`
|
||||
/// to keep the cached text. Thin wrapper over the library engine.
|
||||
pub fn resolve(document: &CadDocument, host: Handle) -> Option<String> {
|
||||
codec::fields::resolve(document, host, &OcsFieldContext)
|
||||
codec::fields::resolve(document, host, &OcsFieldContext(Some(document)))
|
||||
}
|
||||
|
||||
pub fn resolve_handle(
|
||||
@@ -58,7 +77,87 @@ pub fn resolve_handle(
|
||||
field: Handle,
|
||||
host: Handle,
|
||||
) -> Option<String> {
|
||||
codec::fields::resolve_handle(document, field, host, &OcsFieldContext)
|
||||
codec::fields::resolve_handle(document, field, host, &OcsFieldContext(Some(document)))
|
||||
}
|
||||
|
||||
/// Evaluate one field code (`\AcVar Date \f "yyyy-MM-dd"`, or the `%<…>%`
|
||||
/// form) for a preview; `None` when it cannot be evaluated.
|
||||
pub fn evaluate(
|
||||
document: &CadDocument,
|
||||
code: &str,
|
||||
objects: &[Handle],
|
||||
host: Option<Handle>,
|
||||
) -> Option<String> {
|
||||
codec::fields::evaluate_code(document, code, objects, host, &OcsFieldContext(Some(document)))
|
||||
}
|
||||
|
||||
/// The system variables the Field dialog lists, in its order (lower case).
|
||||
pub const SYSVARS: &[&str] = &[
|
||||
"aunits", "auprec", "celtscale", "chamfera", "chamferb", "chamferc", "chamferd",
|
||||
"clayer", "dimscale", "dimtxt", "dwgname", "dwgprefix", "elevation", "filletrad",
|
||||
"insbase", "insunits", "limmax", "limmin", "ltscale", "lunits", "luprec",
|
||||
"measurement", "mirrtext", "pdmode", "pdsize", "textsize", "textstyle", "thickness",
|
||||
"useri1", "useri2", "useri3", "useri4", "useri5", "userr1", "userr2", "userr3",
|
||||
"userr4", "userr5",
|
||||
];
|
||||
|
||||
fn num(v: f64) -> String {
|
||||
let t = format!("{v:.4}");
|
||||
t.trim_end_matches('0').trim_end_matches('.').to_string()
|
||||
}
|
||||
|
||||
/// A system variable's value as a field shows it.
|
||||
pub fn sysvar(document: &CadDocument, name: &str) -> Option<String> {
|
||||
let h = &document.header;
|
||||
let path = document.source_path.as_deref().unwrap_or("");
|
||||
let (folder, file) = match path.rfind(['/', '\\']) {
|
||||
Some(i) => (&path[..=i], &path[i + 1..]),
|
||||
None => ("", path),
|
||||
};
|
||||
Some(match name.trim().to_ascii_lowercase().as_str() {
|
||||
"aunits" => h.angular_unit_format.to_string(),
|
||||
"auprec" => h.angular_unit_precision.to_string(),
|
||||
"celtscale" => num(h.current_entity_linetype_scale),
|
||||
"chamfera" => num(h.chamfer_distance_a),
|
||||
"chamferb" => num(h.chamfer_distance_b),
|
||||
"chamferc" => num(h.chamfer_length),
|
||||
"chamferd" => num(h.chamfer_angle),
|
||||
"clayer" => h.current_layer_name.clone(),
|
||||
"dimscale" => num(h.dim_scale),
|
||||
"dimtxt" => num(h.dim_text_height),
|
||||
"dwgname" => if file.is_empty() { "Drawing1.dwg".to_string() } else { file.to_string() },
|
||||
"dwgprefix" => folder.to_string(),
|
||||
"elevation" => num(h.elevation),
|
||||
"filletrad" => num(h.fillet_radius),
|
||||
"insbase" => {
|
||||
let p = h.model_space_insertion_base;
|
||||
format!("{},{},{}", num(p.x), num(p.y), num(p.z))
|
||||
}
|
||||
"insunits" => h.insertion_units.to_string(),
|
||||
"limmax" => format!("{},{}", num(h.model_space_limits_max.x), num(h.model_space_limits_max.y)),
|
||||
"limmin" => format!("{},{}", num(h.model_space_limits_min.x), num(h.model_space_limits_min.y)),
|
||||
"ltscale" => num(h.linetype_scale),
|
||||
"lunits" => h.linear_unit_format.to_string(),
|
||||
"luprec" => h.linear_unit_precision.to_string(),
|
||||
"measurement" => h.measurement.to_string(),
|
||||
"mirrtext" => i16::from(h.mirror_text).to_string(),
|
||||
"pdmode" => h.point_display_mode.to_string(),
|
||||
"pdsize" => num(h.point_display_size),
|
||||
"textsize" => num(h.text_height),
|
||||
"textstyle" => h.current_text_style_name.clone(),
|
||||
"thickness" => num(h.thickness),
|
||||
"useri1" => h.user_int1.to_string(),
|
||||
"useri2" => h.user_int2.to_string(),
|
||||
"useri3" => h.user_int3.to_string(),
|
||||
"useri4" => h.user_int4.to_string(),
|
||||
"useri5" => h.user_int5.to_string(),
|
||||
"userr1" => num(h.user_real1),
|
||||
"userr2" => num(h.user_real2),
|
||||
"userr3" => num(h.user_real3),
|
||||
"userr4" => num(h.user_real4),
|
||||
"userr5" => num(h.user_real5),
|
||||
_ => return None,
|
||||
})
|
||||
}
|
||||
|
||||
/// Seconds since the Unix epoch. Native uses the system clock; wasm uses the JS
|
||||
@@ -77,3 +176,75 @@ fn epoch_secs() -> i64 {
|
||||
(js_sys::Date::now() / 1000.0) as i64
|
||||
}
|
||||
}
|
||||
|
||||
/// Attach `field` (its code and referenced objects) to `host`, showing
|
||||
/// `value`; `None` removes the host's field.
|
||||
pub fn set_text_field(
|
||||
document: &mut CadDocument,
|
||||
host: Handle,
|
||||
field: Option<(String, Vec<Handle>)>,
|
||||
value: &str,
|
||||
) {
|
||||
match field {
|
||||
Some((code, objects)) => {
|
||||
let mut child = codec::fields::NewField::new(code, value);
|
||||
child.objects = objects;
|
||||
document.set_text_field(host, "%<\\_FldIdx 0>%", vec![child]);
|
||||
}
|
||||
None => {
|
||||
document.remove_text_field(host);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// Month and day names and the regional date / time pictures, as date
|
||||
/// fields show them: the operating system's regional settings.
|
||||
fn os_date_locale() -> codec::fields::DateLocale {
|
||||
#[cfg(target_os = "windows")]
|
||||
{
|
||||
use windows_sys::Win32::Globalization::GetLocaleInfoEx;
|
||||
let info = |kind: u32| -> Option<String> {
|
||||
let mut buffer = [0u16; 128];
|
||||
// A null locale name is the user's default locale.
|
||||
let len = unsafe { GetLocaleInfoEx(std::ptr::null(), kind, buffer.as_mut_ptr(), buffer.len() as i32) };
|
||||
(len > 1).then(|| String::from_utf16_lossy(&buffer[..len as usize - 1]))
|
||||
};
|
||||
let mut locale = codec::fields::DateLocale::default();
|
||||
for k in 0..12u32 {
|
||||
if let Some(v) = info(0x38 + k) {
|
||||
locale.months[k as usize] = v; // LOCALE_SMONTHNAME1..
|
||||
}
|
||||
if let Some(v) = info(0x44 + k) {
|
||||
locale.months_abbr[k as usize] = v; // LOCALE_SABBREVMONTHNAME1..
|
||||
}
|
||||
}
|
||||
// LOCALE_SDAYNAME1 is Monday; the codec lists Sunday first.
|
||||
for k in 0..7u32 {
|
||||
let slot = ((k + 1) % 7) as usize;
|
||||
if let Some(v) = info(0x2A + k) {
|
||||
locale.days[slot] = v;
|
||||
}
|
||||
if let Some(v) = info(0x31 + k) {
|
||||
locale.days_abbr[slot] = v;
|
||||
}
|
||||
}
|
||||
if let Some(v) = info(0x28) {
|
||||
locale.am = v; // LOCALE_S1159
|
||||
}
|
||||
if let Some(v) = info(0x29) {
|
||||
locale.pm = v; // LOCALE_S2359
|
||||
}
|
||||
if let Some(v) = info(0x1F) {
|
||||
locale.short_date = v; // LOCALE_SSHORTDATE
|
||||
}
|
||||
if let Some(v) = info(0x20) {
|
||||
locale.long_date = v; // LOCALE_SLONGDATE
|
||||
}
|
||||
if let Some(v) = info(0x1003) {
|
||||
locale.long_time = v; // LOCALE_STIMEFORMAT
|
||||
}
|
||||
locale
|
||||
}
|
||||
#[cfg(not(target_os = "windows"))]
|
||||
codec::fields::DateLocale::default()
|
||||
}
|
||||
@@ -184,6 +184,36 @@ pub(super) fn message_attribute(source: &str) -> Option<(&'static str, &'static
|
||||
"The tag may not contain spaces." => Some(("insert", "attdef-tag-spaces")),
|
||||
"Value must be positive and nonzero." => Some(("insert", "attdef-positive")),
|
||||
"Requires valid numeric angle or second point." => Some(("insert", "attdef-angle")),
|
||||
"Field" => Some(("insert", "field-title")),
|
||||
"Category" => Some(("insert", "field-category")),
|
||||
"Date format" => Some(("insert", "field-date-format")),
|
||||
"Examples" => Some(("insert", "field-examples")),
|
||||
"Display file extension" => Some(("insert", "field-file-extension")),
|
||||
"Filename only" => Some(("insert", "field-filename-only")),
|
||||
"Path only" => Some(("insert", "field-path-only")),
|
||||
"Path and filename" => Some(("insert", "field-path-filename")),
|
||||
"Bytes" => Some(("insert", "field-bytes")),
|
||||
"Kilobytes" => Some(("insert", "field-kilobytes")),
|
||||
"Megabytes" => Some(("insert", "field-megabytes")),
|
||||
"System variable" => Some(("insert", "field-system-variable")),
|
||||
"Diesel expression" => Some(("insert", "field-diesel")),
|
||||
"Named object type" => Some(("insert", "field-named-type")),
|
||||
"Object type" => Some(("insert", "field-object-type")),
|
||||
"Select object" => Some(("insert", "field-select-object")),
|
||||
"Field expression" => Some(("insert", "field-expression")),
|
||||
"Inserts a field: text that updates from the drawing, the date or another source." => Some(("insert", "field-help")),
|
||||
"Date & Time" => Some(("insert", "field-cat-date")),
|
||||
"Document" => Some(("insert", "field-cat-document")),
|
||||
"Other" => Some(("insert", "field-cat-other")),
|
||||
"Uppercase" => Some(("insert", "field-case-upper")),
|
||||
"Lowercase" => Some(("insert", "field-case-lower")),
|
||||
"First capital" => Some(("insert", "field-case-first")),
|
||||
"Title case" => Some(("insert", "field-case-title")),
|
||||
"Dimstyle" => Some(("insert", "field-named-dimstyle")),
|
||||
"Tablestyle" => Some(("insert", "field-named-tablestyle")),
|
||||
"Textstyle" => Some(("insert", "field-named-textstyle")),
|
||||
"EndPoint" => Some(("insert", "field-prop-endpoint")),
|
||||
"StartPoint" => Some(("insert", "field-prop-startpoint")),
|
||||
"Stylization" => Some(("properties", "stylization")),
|
||||
"Color scheme" => Some(("properties", "color-scheme")),
|
||||
"Scan Colors" => Some(("properties", "scan-colors")),
|
||||
|
||||
@@ -0,0 +1,190 @@
|
||||
// FIELD — places the field chosen in the Field dialog as a multi-line text,
|
||||
// and the object pick behind the dialog's "Select object" button.
|
||||
|
||||
use codec::entities::mtext::AttachmentPoint;
|
||||
use codec::entities::MText;
|
||||
use codec::types::{Handle, Vector3};
|
||||
use codec::EntityType;
|
||||
use glam::DVec3;
|
||||
|
||||
use crate::command::{CadCommand, CmdResult, InputKind, WorkingPlane};
|
||||
|
||||
/// A field to attach to the entity a command commits: the field code and the
|
||||
/// objects it refers to.
|
||||
pub type TextField = (String, Vec<Handle>);
|
||||
|
||||
#[derive(Clone, Copy, PartialEq)]
|
||||
enum Step {
|
||||
Point,
|
||||
Height,
|
||||
Justify,
|
||||
}
|
||||
|
||||
/// `Specify start point or [Height/Justify]:` → an MTEXT holding the field.
|
||||
pub struct FieldPlaceCommand {
|
||||
step: Step,
|
||||
value: String,
|
||||
field: TextField,
|
||||
style: String,
|
||||
height: f64,
|
||||
attachment: AttachmentPoint,
|
||||
plane: WorkingPlane,
|
||||
}
|
||||
|
||||
impl FieldPlaceCommand {
|
||||
pub fn new(value: String, field: TextField, style: String, height: f64) -> Self {
|
||||
Self {
|
||||
step: Step::Point,
|
||||
value,
|
||||
field,
|
||||
style,
|
||||
height,
|
||||
attachment: AttachmentPoint::TopLeft,
|
||||
plane: WorkingPlane::default(),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
fn short(v: f64) -> String {
|
||||
let t = format!("{v:.4}");
|
||||
t.trim_end_matches('0').trim_end_matches('.').to_string()
|
||||
}
|
||||
|
||||
impl CadCommand for FieldPlaceCommand {
|
||||
fn name(&self) -> &'static str {
|
||||
"FIELD"
|
||||
}
|
||||
|
||||
fn set_working_plane(&mut self, plane: WorkingPlane) {
|
||||
self.plane = plane;
|
||||
}
|
||||
|
||||
fn prompt(&self) -> String {
|
||||
match self.step {
|
||||
Step::Point => "Specify start point or [Height/Justify]:".into(),
|
||||
Step::Height => format!("Specify height <{}>:", short(self.height)),
|
||||
Step::Justify => "Enter justification [TL/TC/TR/ML/MC/MR/BL/BC/BR] <TL>:".into(),
|
||||
}
|
||||
}
|
||||
|
||||
fn input_kind(&self) -> InputKind {
|
||||
match self.step {
|
||||
Step::Justify => InputKind::SingleToken,
|
||||
_ => InputKind::Point,
|
||||
}
|
||||
}
|
||||
|
||||
fn point_step_accepts_keywords(&self) -> bool {
|
||||
self.step == Step::Point
|
||||
}
|
||||
|
||||
fn on_text_input(&mut self, text: &str) -> Option<CmdResult> {
|
||||
let word = text.trim().trim_start_matches('_').to_ascii_uppercase();
|
||||
match self.step {
|
||||
Step::Point => match word.as_str() {
|
||||
"H" | "HEIGHT" => self.step = Step::Height,
|
||||
"J" | "JUSTIFY" => self.step = Step::Justify,
|
||||
"" => return Some(CmdResult::ReportError("Point or option keyword required.".into())),
|
||||
_ => return Some(CmdResult::ReportError("Point or option keyword required.".into())),
|
||||
},
|
||||
Step::Height => {
|
||||
if word.is_empty() {
|
||||
self.step = Step::Point;
|
||||
return Some(CmdResult::NeedPoint);
|
||||
}
|
||||
match word.parse::<f64>() {
|
||||
Ok(v) if v > 0.0 => {
|
||||
self.height = v;
|
||||
self.step = Step::Point;
|
||||
}
|
||||
Ok(_) => return Some(CmdResult::ReportError("Value must be positive and nonzero.".into())),
|
||||
Err(_) => return Some(CmdResult::ReportError("Requires numeric distance or two points.".into())),
|
||||
}
|
||||
}
|
||||
Step::Justify => {
|
||||
self.attachment = match word.as_str() {
|
||||
"" | "TL" => AttachmentPoint::TopLeft,
|
||||
"TC" => AttachmentPoint::TopCenter,
|
||||
"TR" => AttachmentPoint::TopRight,
|
||||
"ML" => AttachmentPoint::MiddleLeft,
|
||||
"MC" => AttachmentPoint::MiddleCenter,
|
||||
"MR" => AttachmentPoint::MiddleRight,
|
||||
"BL" => AttachmentPoint::BottomLeft,
|
||||
"BC" => AttachmentPoint::BottomCenter,
|
||||
"BR" => AttachmentPoint::BottomRight,
|
||||
_ => return Some(CmdResult::ReportError("Invalid option keyword.".into())),
|
||||
};
|
||||
self.step = Step::Point;
|
||||
}
|
||||
}
|
||||
Some(CmdResult::NeedPoint)
|
||||
}
|
||||
|
||||
fn on_point(&mut self, pt: DVec3) -> CmdResult {
|
||||
if self.step == Step::Height {
|
||||
return CmdResult::NeedPoint;
|
||||
}
|
||||
let p = self.plane.to_local(pt);
|
||||
let mut mtext = MText::new();
|
||||
mtext.value = self.value.clone();
|
||||
mtext.insertion_point = Vector3::new(p.x, p.y, p.z);
|
||||
mtext.height = self.height;
|
||||
mtext.style = self.style.clone();
|
||||
mtext.attachment_point = self.attachment;
|
||||
CmdResult::CommitAndExit(self.plane.place_entity(EntityType::MText(mtext)))
|
||||
}
|
||||
|
||||
fn on_enter(&mut self) -> CmdResult {
|
||||
match self.step {
|
||||
Step::Point => CmdResult::ReportError("Point or option keyword required.".into()),
|
||||
_ => self.on_text_input("").unwrap_or(CmdResult::NeedPoint),
|
||||
}
|
||||
}
|
||||
|
||||
fn text_field(&self) -> Option<TextField> {
|
||||
Some(self.field.clone())
|
||||
}
|
||||
}
|
||||
|
||||
/// The Field dialog's "Select object": one object is picked and handed back
|
||||
/// (`_FIELD_OBJECT <handle>`); Esc returns without one.
|
||||
pub struct FieldObjectPickCommand;
|
||||
|
||||
impl CadCommand for FieldObjectPickCommand {
|
||||
fn name(&self) -> &'static str {
|
||||
"FIELD"
|
||||
}
|
||||
|
||||
fn prompt(&self) -> String {
|
||||
"Select object:".into()
|
||||
}
|
||||
|
||||
fn needs_entity_pick(&self) -> bool {
|
||||
true
|
||||
}
|
||||
|
||||
fn entity_pick_highlights_hover(&self) -> bool {
|
||||
true
|
||||
}
|
||||
|
||||
fn on_entity_pick(&mut self, handle: Handle, _pt: DVec3) -> CmdResult {
|
||||
if handle.is_null() {
|
||||
return CmdResult::NeedPoint;
|
||||
}
|
||||
CmdResult::Dispatch(format!("_FIELD_OBJECT {:X}", handle.value()))
|
||||
}
|
||||
|
||||
fn on_point(&mut self, _pt: DVec3) -> CmdResult {
|
||||
CmdResult::NeedPoint
|
||||
}
|
||||
|
||||
fn on_enter(&mut self) -> CmdResult {
|
||||
CmdResult::Dispatch("_FIELD_OBJECT".into())
|
||||
}
|
||||
|
||||
fn on_escape(&mut self) -> CmdResult {
|
||||
CmdResult::Dispatch("_FIELD_OBJECT".into())
|
||||
}
|
||||
}
|
||||
|
||||
inventory::submit!(crate::command::CommandRegistration { names: &["FIELD"] });
|
||||
@@ -7,6 +7,7 @@ pub mod angular_dim;
|
||||
pub mod data_extract;
|
||||
pub mod data_link;
|
||||
pub mod ddedit;
|
||||
pub mod field_cmd;
|
||||
pub mod diameter_dim;
|
||||
pub mod dim_baseline;
|
||||
pub mod dim_continue;
|
||||
|
||||
@@ -218,6 +218,8 @@ pub struct AttdefSpec {
|
||||
/// Multi-line boundary width (0 = none).
|
||||
pub boundary_width: f64,
|
||||
pub line_spacing: f64,
|
||||
/// A field the default value comes from: its code and referenced objects.
|
||||
pub field: Option<(String, Vec<codec::Handle>)>,
|
||||
}
|
||||
|
||||
impl AttdefSpec {
|
||||
@@ -447,6 +449,7 @@ impl AttdefCommand {
|
||||
oblique_angle,
|
||||
boundary_width: 0.0,
|
||||
line_spacing: 1.0,
|
||||
field: None,
|
||||
},
|
||||
lines: Vec::new(),
|
||||
styles,
|
||||
@@ -952,6 +955,10 @@ impl CadCommand for AttdefPlaceCommand {
|
||||
fn on_mouse_move(&mut self, pt: DVec3) -> Option<WireModel> {
|
||||
Some(cross_preview(pt, self.plane))
|
||||
}
|
||||
|
||||
fn text_field(&self) -> Option<(String, Vec<codec::Handle>)> {
|
||||
self.spec.field.clone()
|
||||
}
|
||||
}
|
||||
|
||||
/// A small cross at the cursor while a placement point is asked.
|
||||
|
||||
@@ -34,6 +34,9 @@ pub enum AttdefDialogMsg {
|
||||
PickHeight,
|
||||
PickRotation,
|
||||
PickWidth,
|
||||
/// The ƒ button: pick a field for the Default value.
|
||||
InsertField,
|
||||
EditInsertField,
|
||||
/// Edit the default value in the multi-line text editor.
|
||||
EditValue,
|
||||
DismissError,
|
||||
@@ -76,6 +79,8 @@ pub struct AttdefDialogState {
|
||||
pub can_align_below: bool,
|
||||
/// Validation message shown above the buttons.
|
||||
pub error: Option<String>,
|
||||
/// The field the Default value shows, when one was inserted.
|
||||
pub field: Option<(String, Vec<codec::Handle>)>,
|
||||
}
|
||||
|
||||
/// The Edit Attribute Definition dialog's fields.
|
||||
@@ -87,6 +92,10 @@ pub struct AttdefEditState {
|
||||
pub default: String,
|
||||
pub constant: bool,
|
||||
pub error: Option<String>,
|
||||
pub field: Option<(String, Vec<codec::Handle>)>,
|
||||
/// The Default was retyped or a field inserted: the stored field is
|
||||
/// replaced on Apply.
|
||||
pub field_changed: bool,
|
||||
}
|
||||
|
||||
fn msg(m: AttdefDialogMsg) -> Message {
|
||||
@@ -239,8 +248,7 @@ pub fn view<'a>(state: &'a AttdefDialogState, sizing: crate::ui::modal::ModalSiz
|
||||
Some(AttdefDialogMsg::EditValue),
|
||||
));
|
||||
}
|
||||
// No field picker in this application yet.
|
||||
value.push(pick("ƒ", t!("Insert field").into_owned(), None))
|
||||
value.push(pick("ƒ", t!("Insert field").into_owned(), Some(AttdefDialogMsg::InsertField)))
|
||||
}
|
||||
.spacing(6)
|
||||
.into(),
|
||||
@@ -372,7 +380,7 @@ pub fn view_edit<'a>(state: &'a AttdefEditState, sizing: crate::ui::modal::Modal
|
||||
true,
|
||||
AttdefDialogMsg::EditDefault
|
||||
),
|
||||
pick("ƒ", t!("Insert field").into_owned(), None),
|
||||
pick("ƒ", t!("Insert field").into_owned(), Some(AttdefDialogMsg::EditInsertField)),
|
||||
]
|
||||
.spacing(6)
|
||||
.align_y(iced::Center),
|
||||
|
||||
@@ -0,0 +1,491 @@
|
||||
//! Field dialog (FIELD and the ƒ buttons of the attribute definition
|
||||
//! dialogs): a category and field list on the left, the chosen field's
|
||||
//! format on the right, a live preview and the field expression below.
|
||||
|
||||
use iced::widget::{button, checkbox, column, container, pick_list, row, scrollable, text, text_input, Space};
|
||||
use iced::{Element, Fill, Length};
|
||||
|
||||
use crate::app::Message;
|
||||
use crate::t;
|
||||
use crate::ui::style::common::muted_style;
|
||||
use crate::ui::style::form::{button_style, dialog_button, field_style};
|
||||
use crate::ui::window::pdf_dialogs::{accent_text, card_style};
|
||||
|
||||
/// Where the chosen field goes.
|
||||
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
|
||||
pub enum FieldTarget {
|
||||
/// FIELD: a new multi-line text placed on screen.
|
||||
NewText,
|
||||
/// The Default of the Attribute Definition dialog.
|
||||
AttdefDefault,
|
||||
/// The Default of the Edit Attribute Definition dialog.
|
||||
AttdefEdit,
|
||||
}
|
||||
|
||||
/// The categories the dialog offers, with their fields (only the ones the
|
||||
/// application evaluates).
|
||||
pub const CATEGORIES: &[(&str, &[&str])] = &[
|
||||
("All", &[]),
|
||||
("Date & Time", &["CreateDate", "Date", "PlotDate", "SaveDate"]),
|
||||
(
|
||||
"Document",
|
||||
&["Author", "Comments", "Filename", "Filesize", "HyperlinkBase", "Keywords", "LastSavedBy", "Subject", "Title"],
|
||||
),
|
||||
("Objects", &["NamedObject", "Object"]),
|
||||
("Other", &["DieselExpression", "SystemVariable"]),
|
||||
("Plot", &["Login", "PlotDate"]),
|
||||
];
|
||||
|
||||
/// The fields of a category; "All" lists every field once, sorted.
|
||||
pub fn fields_of(category: usize) -> Vec<&'static str> {
|
||||
if category == 0 {
|
||||
let mut all: Vec<&'static str> = CATEGORIES.iter().flat_map(|(_, f)| f.iter().copied()).collect();
|
||||
all.sort_unstable_by_key(|s| s.to_ascii_lowercase());
|
||||
all.dedup();
|
||||
return all;
|
||||
}
|
||||
CATEGORIES.get(category).map(|(_, f)| f.to_vec()).unwrap_or_default()
|
||||
}
|
||||
|
||||
/// The date formats the dialog lists, in the reference order.
|
||||
pub const DATE_FORMATS: &[&str] = &[
|
||||
"M/d/yyyy", "dddd, MMMM dd, yyyy", "MMMM d, yyyy", "M/d/yy", "yyyy-MM-dd", "d-MMM-yy",
|
||||
"M.d.yyyy", "MMM. d, yy", "d MMMM yyyy", "dd.MM.yyyy", "dd/MM/yyyy", "yyyy/MM/dd",
|
||||
"yyyy-M-d", "MMMM yy", "MMM-yy", "M/d/yyyy h:mm tt", "M/d/yyyy h:mm:ss tt", "h:mm tt",
|
||||
"h:mm:ss tt", "HH:mm", "HH:mm:ss", "%#x", "%#c", "%x", "%c", "%X",
|
||||
];
|
||||
|
||||
pub const TEXT_CASES: &[&str] = &["(none)", "Uppercase", "Lowercase", "First capital", "Title case"];
|
||||
pub const SIZE_UNITS: &[&str] = &["Bytes", "Kilobytes", "Megabytes"];
|
||||
pub const NAMED_TYPES: &[&str] = &["Block", "Dimstyle", "Layer", "Linetype", "Tablestyle", "Textstyle", "View"];
|
||||
|
||||
/// How a field is formatted, by its name.
|
||||
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
|
||||
pub enum FieldKind {
|
||||
Date,
|
||||
Text,
|
||||
Filename,
|
||||
Filesize,
|
||||
SystemVariable,
|
||||
Diesel,
|
||||
NamedObject,
|
||||
Object,
|
||||
}
|
||||
|
||||
pub fn kind_of(name: &str) -> FieldKind {
|
||||
match name {
|
||||
"CreateDate" | "Date" | "PlotDate" | "SaveDate" => FieldKind::Date,
|
||||
"Filename" => FieldKind::Filename,
|
||||
"Filesize" => FieldKind::Filesize,
|
||||
"SystemVariable" => FieldKind::SystemVariable,
|
||||
"DieselExpression" => FieldKind::Diesel,
|
||||
"NamedObject" => FieldKind::NamedObject,
|
||||
"Object" => FieldKind::Object,
|
||||
_ => FieldKind::Text,
|
||||
}
|
||||
}
|
||||
|
||||
/// The Field dialog's working copy.
|
||||
#[derive(Debug, Clone)]
|
||||
pub struct FieldDialogState {
|
||||
pub target: FieldTarget,
|
||||
pub category: usize,
|
||||
pub name: &'static str,
|
||||
pub date_format: String,
|
||||
pub text_case: usize,
|
||||
/// 1 = path only, 2 = file name only, 3 = path and file name.
|
||||
pub file_parts: u8,
|
||||
pub file_extension: bool,
|
||||
pub size_unit: usize,
|
||||
pub sysvar: String,
|
||||
pub diesel: String,
|
||||
pub named_type: usize,
|
||||
pub named_names: Vec<(String, codec::Handle)>,
|
||||
pub named: Option<usize>,
|
||||
/// The object picked for an Object field, its type and properties.
|
||||
pub object: Option<(codec::Handle, String)>,
|
||||
pub object_props: Vec<&'static str>,
|
||||
pub object_prop: Option<&'static str>,
|
||||
pub preview: String,
|
||||
/// Today in each of DATE_FORMATS, for the Examples list.
|
||||
pub examples: Vec<String>,
|
||||
}
|
||||
|
||||
impl FieldDialogState {
|
||||
pub fn new(target: FieldTarget) -> Self {
|
||||
Self {
|
||||
target,
|
||||
category: 0,
|
||||
name: "Date",
|
||||
date_format: "%x".into(),
|
||||
text_case: 0,
|
||||
file_parts: 3,
|
||||
file_extension: true,
|
||||
size_unit: 0,
|
||||
sysvar: crate::entities::field::SYSVARS[0].into(),
|
||||
diesel: String::new(),
|
||||
named_type: 2,
|
||||
named_names: Vec::new(),
|
||||
named: None,
|
||||
object: None,
|
||||
object_props: Vec::new(),
|
||||
object_prop: None,
|
||||
preview: String::new(),
|
||||
examples: Vec::new(),
|
||||
}
|
||||
}
|
||||
|
||||
/// The field code (without the `%<…>%` wrapper) and the objects it refers to.
|
||||
pub fn code(&self) -> (String, Vec<codec::Handle>) {
|
||||
let case = |base: String| match self.text_case {
|
||||
0 => base,
|
||||
n => format!("{base} \\f \"%tc{n}\""),
|
||||
};
|
||||
match kind_of(self.name) {
|
||||
FieldKind::Date => (format!("\\AcVar {} \\f \"{}\"", self.name, self.date_format), vec![]),
|
||||
FieldKind::Text => (case(format!("\\AcVar {}", self.name)), vec![]),
|
||||
FieldKind::Filename => {
|
||||
let bits = self.file_parts + if self.file_extension && self.file_parts & 2 != 0 { 4 } else { 0 };
|
||||
(case(format!("\\AcVar Filename \\f \"%fn{bits}\"")), vec![])
|
||||
}
|
||||
FieldKind::Filesize => (format!("\\AcVar Filesize \\f \"%ld%by{}\"", self.size_unit + 1), vec![]),
|
||||
FieldKind::SystemVariable => (format!("\\AcVar {}", self.sysvar), vec![]),
|
||||
FieldKind::Diesel => (format!("\\AcDiesel {}", self.diesel), vec![]),
|
||||
FieldKind::NamedObject => match self.named.and_then(|i| self.named_names.get(i)) {
|
||||
Some((_, handle)) => (case("\\AcObjProp Object(%<\\_ObjIdx 0>%).Name".into()), vec![*handle]),
|
||||
None => ("\\AcObjProp".into(), vec![]),
|
||||
},
|
||||
FieldKind::Object => match (&self.object, self.object_prop) {
|
||||
(Some((handle, _)), Some(prop)) => {
|
||||
(format!("\\AcObjProp Object(%<\\_ObjIdx 0>%).{prop}"), vec![*handle])
|
||||
}
|
||||
_ => ("\\AcObjProp".into(), vec![]),
|
||||
},
|
||||
}
|
||||
}
|
||||
|
||||
/// The field can be inserted (an object field needs its object and
|
||||
/// property, a named object its name, a DIESEL field an expression).
|
||||
pub fn complete(&self) -> bool {
|
||||
match kind_of(self.name) {
|
||||
FieldKind::NamedObject => self.named.is_some(),
|
||||
FieldKind::Object => self.object.is_some() && self.object_prop.is_some(),
|
||||
FieldKind::Diesel => !self.diesel.trim().is_empty(),
|
||||
_ => true,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// The geometric properties a field can show for an object of this type.
|
||||
pub fn object_properties(entity: &codec::EntityType) -> Vec<&'static str> {
|
||||
use codec::EntityType as E;
|
||||
match entity {
|
||||
E::Line(_) => vec!["Center", "EndPoint", "Length", "StartPoint"],
|
||||
E::Circle(_) => vec!["Area", "Center", "Circumference", "Diameter", "Radius"],
|
||||
E::Arc(_) => vec!["Center", "Length", "Radius"],
|
||||
E::Ellipse(_) => vec!["Area", "Center"],
|
||||
E::LwPolyline(_) | E::Polyline2D(_) | E::Spline(_) => vec!["Area", "Length"],
|
||||
E::Hatch(_) => vec!["Area"],
|
||||
_ => vec![],
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Debug, Clone)]
|
||||
pub enum FieldDialogMsg {
|
||||
Category(usize),
|
||||
Name(&'static str),
|
||||
DateFormat(String),
|
||||
DateExample(usize),
|
||||
TextCase(usize),
|
||||
FileParts(u8),
|
||||
FileExtension(bool),
|
||||
SizeUnit(usize),
|
||||
SysVar(String),
|
||||
Diesel(String),
|
||||
NamedType(usize),
|
||||
Named(usize),
|
||||
SelectObject,
|
||||
ObjectProp(&'static str),
|
||||
Help,
|
||||
Ok,
|
||||
}
|
||||
|
||||
#[derive(Debug, Clone, PartialEq, Eq)]
|
||||
struct Pick(usize, String);
|
||||
|
||||
impl std::fmt::Display for Pick {
|
||||
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
|
||||
f.write_str(&self.1)
|
||||
}
|
||||
}
|
||||
|
||||
fn msg(m: FieldDialogMsg) -> Message {
|
||||
Message::FieldDialog(m)
|
||||
}
|
||||
|
||||
fn card<'a>(title: String, content: impl Into<Element<'a, Message>>) -> Element<'a, Message> {
|
||||
container(column![text(title.to_uppercase()).size(10).style(accent_text), content.into()].spacing(8))
|
||||
.padding([10, 12])
|
||||
.width(Fill)
|
||||
.style(card_style)
|
||||
.into()
|
||||
}
|
||||
|
||||
/// A selectable list: one row per item, the chosen one highlighted.
|
||||
fn list<'a>(rows: Vec<(String, bool, FieldDialogMsg)>, height: f32) -> Element<'a, Message> {
|
||||
let rows = rows.into_iter().map(|(label, on, m)| {
|
||||
button(text(label).size(12))
|
||||
.on_press(msg(m))
|
||||
.style(button_style(on))
|
||||
.padding([3, 8])
|
||||
.width(Fill)
|
||||
.into()
|
||||
});
|
||||
container(scrollable(iced::widget::Column::with_children(rows.collect::<Vec<Element<'a, Message>>>()).spacing(1)))
|
||||
.height(Length::Fixed(height))
|
||||
.padding(2)
|
||||
.style(|theme: &iced::Theme| field_style(theme, text_input::Status::Active).into_container())
|
||||
.into()
|
||||
}
|
||||
|
||||
trait IntoContainer {
|
||||
fn into_container(self) -> container::Style;
|
||||
}
|
||||
|
||||
impl IntoContainer for text_input::Style {
|
||||
fn into_container(self) -> container::Style {
|
||||
container::Style {
|
||||
background: Some(self.background),
|
||||
border: self.border,
|
||||
..Default::default()
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
fn case_list<'a>(state: &FieldDialogState) -> Element<'a, Message> {
|
||||
list(
|
||||
TEXT_CASES
|
||||
.iter()
|
||||
.enumerate()
|
||||
.map(|(i, c)| (t!(*c).into_owned(), state.text_case == i, FieldDialogMsg::TextCase(i)))
|
||||
.collect(),
|
||||
130.0,
|
||||
)
|
||||
}
|
||||
|
||||
fn format_panel<'a>(state: &'a FieldDialogState) -> Element<'a, Message> {
|
||||
let examples = &state.examples;
|
||||
match kind_of(state.name) {
|
||||
FieldKind::Date => column![
|
||||
row![
|
||||
text(t!("Date format")).size(12).width(Length::Fixed(96.0)),
|
||||
text_input("", &state.date_format)
|
||||
.size(12)
|
||||
.padding([5, 8])
|
||||
.style(field_style)
|
||||
.on_input(|v| msg(FieldDialogMsg::DateFormat(v))),
|
||||
]
|
||||
.spacing(8)
|
||||
.align_y(iced::Center),
|
||||
text(t!("Examples")).size(12),
|
||||
list(
|
||||
examples
|
||||
.iter()
|
||||
.enumerate()
|
||||
.map(|(i, e)| (e.clone(), DATE_FORMATS.get(i) == Some(&state.date_format.as_str()), FieldDialogMsg::DateExample(i)))
|
||||
.collect(),
|
||||
220.0,
|
||||
),
|
||||
]
|
||||
.spacing(8)
|
||||
.into(),
|
||||
FieldKind::Text => column![text(t!("Format")).size(12), case_list(state)].spacing(8).into(),
|
||||
FieldKind::Filename => {
|
||||
let part = |label: &str, bits: u8| {
|
||||
checkbox(state.file_parts == bits)
|
||||
.label(t!(label).into_owned())
|
||||
.text_size(12)
|
||||
.size(14)
|
||||
.on_toggle(move |_| msg(FieldDialogMsg::FileParts(bits)))
|
||||
};
|
||||
let mut ext = checkbox(state.file_extension)
|
||||
.label(t!("Display file extension").into_owned())
|
||||
.text_size(12)
|
||||
.size(14);
|
||||
if state.file_parts & 2 != 0 {
|
||||
ext = ext.on_toggle(|v| msg(FieldDialogMsg::FileExtension(v)));
|
||||
}
|
||||
column![
|
||||
part("Filename only", 2),
|
||||
part("Path only", 1),
|
||||
part("Path and filename", 3),
|
||||
ext,
|
||||
text(t!("Format")).size(12),
|
||||
case_list(state),
|
||||
]
|
||||
.spacing(8)
|
||||
.into()
|
||||
}
|
||||
FieldKind::Filesize => column![
|
||||
text(t!("Format")).size(12),
|
||||
list(
|
||||
SIZE_UNITS
|
||||
.iter()
|
||||
.enumerate()
|
||||
.map(|(i, u)| (t!(*u).into_owned(), state.size_unit == i, FieldDialogMsg::SizeUnit(i)))
|
||||
.collect(),
|
||||
90.0,
|
||||
),
|
||||
]
|
||||
.spacing(8)
|
||||
.into(),
|
||||
FieldKind::SystemVariable => column![
|
||||
text(t!("System variable")).size(12),
|
||||
list(
|
||||
crate::entities::field::SYSVARS
|
||||
.iter()
|
||||
.map(|v| (v.to_string(), state.sysvar == *v, FieldDialogMsg::SysVar(v.to_string())))
|
||||
.collect(),
|
||||
250.0,
|
||||
),
|
||||
]
|
||||
.spacing(8)
|
||||
.into(),
|
||||
FieldKind::Diesel => column![
|
||||
text(t!("Diesel expression")).size(12),
|
||||
text_input("$(getvar,dwgname)", &state.diesel)
|
||||
.size(12)
|
||||
.padding([5, 8])
|
||||
.style(field_style)
|
||||
.on_input(|v| msg(FieldDialogMsg::Diesel(v))),
|
||||
]
|
||||
.spacing(8)
|
||||
.into(),
|
||||
FieldKind::NamedObject => column![
|
||||
row![
|
||||
text(t!("Named object type")).size(12).width(Length::Fixed(120.0)),
|
||||
pick_list(
|
||||
Some(Pick(state.named_type, t!(NAMED_TYPES[state.named_type]).into_owned())),
|
||||
NAMED_TYPES.iter().enumerate().map(|(i, n)| Pick(i, t!(*n).into_owned())).collect::<Vec<_>>(),
|
||||
|p: &Pick| p.1.clone(),
|
||||
)
|
||||
.on_select(|p| msg(FieldDialogMsg::NamedType(p.0)))
|
||||
.text_size(12)
|
||||
.padding([5, 8])
|
||||
.width(Fill),
|
||||
]
|
||||
.spacing(8)
|
||||
.align_y(iced::Center),
|
||||
text(t!("Name")).size(12),
|
||||
list(
|
||||
state
|
||||
.named_names
|
||||
.iter()
|
||||
.enumerate()
|
||||
.map(|(i, (n, _))| (n.clone(), state.named == Some(i), FieldDialogMsg::Named(i)))
|
||||
.collect(),
|
||||
150.0,
|
||||
),
|
||||
text(t!("Format")).size(12),
|
||||
case_list(state),
|
||||
]
|
||||
.spacing(8)
|
||||
.into(),
|
||||
FieldKind::Object => column![
|
||||
row![
|
||||
text(t!("Object type")).size(12).width(Length::Fixed(96.0)),
|
||||
container(
|
||||
text(state.object.as_ref().map(|(_, t)| t.clone()).unwrap_or_default()).size(12).style(muted_style)
|
||||
)
|
||||
.padding([5, 8])
|
||||
.width(Fill),
|
||||
button(text(t!("Select object")).size(12))
|
||||
.on_press(msg(FieldDialogMsg::SelectObject))
|
||||
.style(button_style(false))
|
||||
.padding([5, 10]),
|
||||
]
|
||||
.spacing(8)
|
||||
.align_y(iced::Center),
|
||||
text(t!("Property")).size(12),
|
||||
list(
|
||||
state
|
||||
.object_props
|
||||
.iter()
|
||||
.map(|p| (t!(*p).into_owned(), state.object_prop == Some(*p), FieldDialogMsg::ObjectProp(p)))
|
||||
.collect(),
|
||||
180.0,
|
||||
),
|
||||
]
|
||||
.spacing(8)
|
||||
.into(),
|
||||
}
|
||||
}
|
||||
|
||||
pub fn view<'a>(
|
||||
state: &'a FieldDialogState,
|
||||
sizing: crate::ui::modal::ModalSizing,
|
||||
) -> Element<'a, Message> {
|
||||
let categories: Vec<Pick> = CATEGORIES.iter().enumerate().map(|(i, (c, _))| Pick(i, t!(*c).into_owned())).collect();
|
||||
let left = card(
|
||||
t!("Field").into_owned(),
|
||||
column![
|
||||
row![
|
||||
text(t!("Category")).size(12).width(Length::Fixed(70.0)),
|
||||
pick_list(Some(categories[state.category].clone()), categories.clone(), |p: &Pick| p.1.clone())
|
||||
.on_select(|p| msg(FieldDialogMsg::Category(p.0)))
|
||||
.text_size(12)
|
||||
.padding([5, 8])
|
||||
.width(Fill),
|
||||
]
|
||||
.spacing(8)
|
||||
.align_y(iced::Center),
|
||||
list(
|
||||
fields_of(state.category)
|
||||
.into_iter()
|
||||
.map(|n| (n.to_string(), state.name == n, FieldDialogMsg::Name(n)))
|
||||
.collect(),
|
||||
330.0,
|
||||
),
|
||||
]
|
||||
.spacing(8),
|
||||
);
|
||||
let preview = card(
|
||||
t!("Preview").into_owned(),
|
||||
container(text(if state.preview.is_empty() { "----".to_string() } else { state.preview.clone() }).size(15))
|
||||
.padding([6, 8])
|
||||
.width(Fill),
|
||||
);
|
||||
let right = column![card(t!("Format").into_owned(), format_panel(state)), preview].spacing(10);
|
||||
let (code, _) = state.code();
|
||||
let expression = column![
|
||||
text(t!("Field expression")).size(12),
|
||||
container(text(format!("%<{code}>%")).size(12).font(iced::Font::MONOSPACE).style(muted_style))
|
||||
.padding([5, 8])
|
||||
.width(Fill)
|
||||
.style(|theme: &iced::Theme| field_style(theme, text_input::Status::Disabled).into_container()),
|
||||
]
|
||||
.spacing(4);
|
||||
let mut insert = button(text(t!("Insert")).size(12))
|
||||
.style(button_style(true))
|
||||
.padding([6, 16]);
|
||||
if state.complete() {
|
||||
insert = insert.on_press(msg(FieldDialogMsg::Ok));
|
||||
}
|
||||
let footer = row![
|
||||
button(text("?").size(12)).on_press(msg(FieldDialogMsg::Help)).style(button_style(false)).padding([5, 11]),
|
||||
Space::new().width(Fill),
|
||||
dialog_button(t!("Cancel"), Message::CloseModal, false),
|
||||
insert,
|
||||
]
|
||||
.spacing(6)
|
||||
.align_y(iced::Center);
|
||||
column![
|
||||
row![container(left).width(Length::Fixed(240.0)), right].spacing(10),
|
||||
expression,
|
||||
footer,
|
||||
]
|
||||
.spacing(10)
|
||||
.padding([10, 12])
|
||||
.width(sizing.width)
|
||||
.into()
|
||||
}
|
||||
@@ -1,5 +1,6 @@
|
||||
pub mod about;
|
||||
pub mod attdef_dialog;
|
||||
pub mod field_dialog;
|
||||
pub mod block_definition;
|
||||
pub mod block_palette;
|
||||
pub mod browser;
|
||||
|
||||
Reference in new issue
Block a user