Blocks palette: tabs, options, drag insertion and -INSERT

Rebuild the Blocks palette with Current Drawing / Recent / Favorites /
Libraries tabs, wildcard filter, six views, insertion options, click and
drop insertion and a block context menu. INSERT opens the palette, the
new -INSERT runs on the command line, the ribbon Insert button becomes a
block gallery, and BLOCKMRULIST, BLOCKREDEFINEMODE, BLOCKNAVIGATE,
BLOCKSTATE, EXPLMODE and INSNAME are added.

Co-Authored-By: Claude Opus 5.5 <[email protected]>
This commit is contained in:
ramox81andClaude Opus 5.5 committed 2026-10-01 11:17:38 +03:00
1 parent 3dcebf0dc6
commit 0826101c5e
49 files changed
+3401 -573

No files matched your search

+34
View File
@@ -4032,6 +4032,40 @@ insert =
.pcs-positive = Requires a positive number.
.pcs-positive-int = Requires a positive integer.
.pcs-help = Traces the points on the kept side of a live section as lines.
.bp-add-favorite = Add to Favorites
.bp-annotative-dynamic = Annotative / Dynamic
.bp-auto-placement = Auto-Placement
.bp-gallery-libraries = Blocks from Libraries...
.bp-choose-library = Choose a library folder with …
.bp-tab-current = Current Drawing
.bp-heading-current = Current Drawing Blocks
.bp-view-xl = Extra Large Icons
.bp-heading-favorites = Favorite Blocks
.bp-gallery-favorites = Favorite Blocks...
.bp-tab-favorites = Favorites
.bp-filter = Filter... (* ? wildcards)
.bp-icon = Icon
.bp-insertion-point = Insertion Point
.bp-view-large = Large Icons
.bp-tab-libraries = Libraries
.bp-heading-libraries = Library Blocks
.bp-view-medium = Medium Icons
.bp-tab-recent = Recent
.bp-heading-recent = Recent Blocks
.bp-gallery-recent = Recent Blocks...
.bp-redefine = Redefine
.bp-remove-block = Remove Block
.bp-remove-favorite = Remove from Favorites
.bp-remove-list = Remove from List
.bp-repeat-placement = Repeat Placement
.bp-select-library = Select a Block Library Folder
.bp-view-small = Small Icons
.bp-on-screen = specified on screen
.bp-block-not-found = Block "{}" not found.
.bp-block-redefined = Block "{name}" redefined.
.bp-block-no-reference = Block "{name}" has no reference in the drawing to edit.
.bp-block-referenced = Block "{name}" is referenced in the drawing and cannot be removed.
.bp-block-removed = Block "{name}" removed.
model =
.create = إنشاء
.boolean = عمليات منطقية
+34
View File
@@ -4019,6 +4019,40 @@ insert =
.pcs-positive = Requires a positive number.
.pcs-positive-int = Requires a positive integer.
.pcs-help = Traces the points on the kept side of a live section as lines.
.bp-add-favorite = Add to Favorites
.bp-annotative-dynamic = Annotative / Dynamic
.bp-auto-placement = Auto-Placement
.bp-gallery-libraries = Blocks from Libraries...
.bp-choose-library = Choose a library folder with …
.bp-tab-current = Current Drawing
.bp-heading-current = Current Drawing Blocks
.bp-view-xl = Extra Large Icons
.bp-heading-favorites = Favorite Blocks
.bp-gallery-favorites = Favorite Blocks...
.bp-tab-favorites = Favorites
.bp-filter = Filter... (* ? wildcards)
.bp-icon = Icon
.bp-insertion-point = Insertion Point
.bp-view-large = Large Icons
.bp-tab-libraries = Libraries
.bp-heading-libraries = Library Blocks
.bp-view-medium = Medium Icons
.bp-tab-recent = Recent
.bp-heading-recent = Recent Blocks
.bp-gallery-recent = Recent Blocks...
.bp-redefine = Redefine
.bp-remove-block = Remove Block
.bp-remove-favorite = Remove from Favorites
.bp-remove-list = Remove from List
.bp-repeat-placement = Repeat Placement
.bp-select-library = Select a Block Library Folder
.bp-view-small = Small Icons
.bp-on-screen = specified on screen
.bp-block-not-found = Block "{}" not found.
.bp-block-redefined = Block "{name}" redefined.
.bp-block-no-reference = Block "{name}" has no reference in the drawing to edit.
.bp-block-referenced = Block "{name}" is referenced in the drawing and cannot be removed.
.bp-block-removed = Block "{name}" removed.
model =
.create = Създаване
.boolean = Булеви операции
+34
View File
@@ -4017,6 +4017,40 @@ insert =
.pcs-positive = Requires a positive number.
.pcs-positive-int = Requires a positive integer.
.pcs-help = Traces the points on the kept side of a live section as lines.
.bp-add-favorite = Add to Favorites
.bp-annotative-dynamic = Annotative / Dynamic
.bp-auto-placement = Auto-Placement
.bp-gallery-libraries = Blocks from Libraries...
.bp-choose-library = Choose a library folder with …
.bp-tab-current = Current Drawing
.bp-heading-current = Current Drawing Blocks
.bp-view-xl = Extra Large Icons
.bp-heading-favorites = Favorite Blocks
.bp-gallery-favorites = Favorite Blocks...
.bp-tab-favorites = Favorites
.bp-filter = Filter... (* ? wildcards)
.bp-icon = Icon
.bp-insertion-point = Insertion Point
.bp-view-large = Large Icons
.bp-tab-libraries = Libraries
.bp-heading-libraries = Library Blocks
.bp-view-medium = Medium Icons
.bp-tab-recent = Recent
.bp-heading-recent = Recent Blocks
.bp-gallery-recent = Recent Blocks...
.bp-redefine = Redefine
.bp-remove-block = Remove Block
.bp-remove-favorite = Remove from Favorites
.bp-remove-list = Remove from List
.bp-repeat-placement = Repeat Placement
.bp-select-library = Select a Block Library Folder
.bp-view-small = Small Icons
.bp-on-screen = specified on screen
.bp-block-not-found = Block "{}" not found.
.bp-block-redefined = Block "{name}" redefined.
.bp-block-no-reference = Block "{name}" has no reference in the drawing to edit.
.bp-block-referenced = Block "{name}" is referenced in the drawing and cannot be removed.
.bp-block-removed = Block "{name}" removed.
model =
.create = Vytvořit
.boolean = Booleovské operace
+34
View File
@@ -4018,6 +4018,40 @@ insert =
.pcs-positive = Requires a positive number.
.pcs-positive-int = Requires a positive integer.
.pcs-help = Traces the points on the kept side of a live section as lines.
.bp-add-favorite = Add to Favorites
.bp-annotative-dynamic = Annotative / Dynamic
.bp-auto-placement = Auto-Placement
.bp-gallery-libraries = Blocks from Libraries...
.bp-choose-library = Choose a library folder with …
.bp-tab-current = Current Drawing
.bp-heading-current = Current Drawing Blocks
.bp-view-xl = Extra Large Icons
.bp-heading-favorites = Favorite Blocks
.bp-gallery-favorites = Favorite Blocks...
.bp-tab-favorites = Favorites
.bp-filter = Filter... (* ? wildcards)
.bp-icon = Icon
.bp-insertion-point = Insertion Point
.bp-view-large = Large Icons
.bp-tab-libraries = Libraries
.bp-heading-libraries = Library Blocks
.bp-view-medium = Medium Icons
.bp-tab-recent = Recent
.bp-heading-recent = Recent Blocks
.bp-gallery-recent = Recent Blocks...
.bp-redefine = Redefine
.bp-remove-block = Remove Block
.bp-remove-favorite = Remove from Favorites
.bp-remove-list = Remove from List
.bp-repeat-placement = Repeat Placement
.bp-select-library = Select a Block Library Folder
.bp-view-small = Small Icons
.bp-on-screen = specified on screen
.bp-block-not-found = Block "{}" not found.
.bp-block-redefined = Block "{name}" redefined.
.bp-block-no-reference = Block "{name}" has no reference in the drawing to edit.
.bp-block-referenced = Block "{name}" is referenced in the drawing and cannot be removed.
.bp-block-removed = Block "{name}" removed.
model =
.create = Erstellen
.boolean = Boolesche Operationen
+34
View File
@@ -4020,6 +4020,40 @@ insert =
.pcs-positive = Requires a positive number.
.pcs-positive-int = Requires a positive integer.
.pcs-help = Traces the points on the kept side of a live section as lines.
.bp-add-favorite = Add to Favorites
.bp-annotative-dynamic = Annotative / Dynamic
.bp-auto-placement = Auto-Placement
.bp-gallery-libraries = Blocks from Libraries...
.bp-choose-library = Choose a library folder with …
.bp-tab-current = Current Drawing
.bp-heading-current = Current Drawing Blocks
.bp-view-xl = Extra Large Icons
.bp-heading-favorites = Favorite Blocks
.bp-gallery-favorites = Favorite Blocks...
.bp-tab-favorites = Favorites
.bp-filter = Filter... (* ? wildcards)
.bp-icon = Icon
.bp-insertion-point = Insertion Point
.bp-view-large = Large Icons
.bp-tab-libraries = Libraries
.bp-heading-libraries = Library Blocks
.bp-view-medium = Medium Icons
.bp-tab-recent = Recent
.bp-heading-recent = Recent Blocks
.bp-gallery-recent = Recent Blocks...
.bp-redefine = Redefine
.bp-remove-block = Remove Block
.bp-remove-favorite = Remove from Favorites
.bp-remove-list = Remove from List
.bp-repeat-placement = Repeat Placement
.bp-select-library = Select a Block Library Folder
.bp-view-small = Small Icons
.bp-on-screen = specified on screen
.bp-block-not-found = Block "{}" not found.
.bp-block-redefined = Block "{name}" redefined.
.bp-block-no-reference = Block "{name}" has no reference in the drawing to edit.
.bp-block-referenced = Block "{name}" is referenced in the drawing and cannot be removed.
.bp-block-removed = Block "{name}" removed.
model =
.create = Δημιουργία
.boolean = Δυαδική πράξη
+34
View File
@@ -4021,6 +4021,40 @@ insert =
.pcs-positive = Requires a positive number.
.pcs-positive-int = Requires a positive integer.
.pcs-help = Traces the points on the kept side of a live section as lines.
.bp-add-favorite = Add to Favorites
.bp-annotative-dynamic = Annotative / Dynamic
.bp-auto-placement = Auto-Placement
.bp-gallery-libraries = Blocks from Libraries...
.bp-choose-library = Choose a library folder with …
.bp-tab-current = Current Drawing
.bp-heading-current = Current Drawing Blocks
.bp-view-xl = Extra Large Icons
.bp-heading-favorites = Favorite Blocks
.bp-gallery-favorites = Favorite Blocks...
.bp-tab-favorites = Favorites
.bp-filter = Filter... (* ? wildcards)
.bp-icon = Icon
.bp-insertion-point = Insertion Point
.bp-view-large = Large Icons
.bp-tab-libraries = Libraries
.bp-heading-libraries = Library Blocks
.bp-view-medium = Medium Icons
.bp-tab-recent = Recent
.bp-heading-recent = Recent Blocks
.bp-gallery-recent = Recent Blocks...
.bp-redefine = Redefine
.bp-remove-block = Remove Block
.bp-remove-favorite = Remove from Favorites
.bp-remove-list = Remove from List
.bp-repeat-placement = Repeat Placement
.bp-select-library = Select a Block Library Folder
.bp-view-small = Small Icons
.bp-on-screen = specified on screen
.bp-block-not-found = Block "{}" not found.
.bp-block-redefined = Block "{name}" redefined.
.bp-block-no-reference = Block "{name}" has no reference in the drawing to edit.
.bp-block-referenced = Block "{name}" is referenced in the drawing and cannot be removed.
.bp-block-removed = Block "{name}" removed.
model =
.create = Create
.boolean = Boolean
+34
View File
@@ -4020,6 +4020,40 @@ insert =
.pcs-positive = Requires a positive number.
.pcs-positive-int = Requires a positive integer.
.pcs-help = Traces the points on the kept side of a live section as lines.
.bp-add-favorite = Add to Favorites
.bp-annotative-dynamic = Annotative / Dynamic
.bp-auto-placement = Auto-Placement
.bp-gallery-libraries = Blocks from Libraries...
.bp-choose-library = Choose a library folder with …
.bp-tab-current = Current Drawing
.bp-heading-current = Current Drawing Blocks
.bp-view-xl = Extra Large Icons
.bp-heading-favorites = Favorite Blocks
.bp-gallery-favorites = Favorite Blocks...
.bp-tab-favorites = Favorites
.bp-filter = Filter... (* ? wildcards)
.bp-icon = Icon
.bp-insertion-point = Insertion Point
.bp-view-large = Large Icons
.bp-tab-libraries = Libraries
.bp-heading-libraries = Library Blocks
.bp-view-medium = Medium Icons
.bp-tab-recent = Recent
.bp-heading-recent = Recent Blocks
.bp-gallery-recent = Recent Blocks...
.bp-redefine = Redefine
.bp-remove-block = Remove Block
.bp-remove-favorite = Remove from Favorites
.bp-remove-list = Remove from List
.bp-repeat-placement = Repeat Placement
.bp-select-library = Select a Block Library Folder
.bp-view-small = Small Icons
.bp-on-screen = specified on screen
.bp-block-not-found = Block "{}" not found.
.bp-block-redefined = Block "{name}" redefined.
.bp-block-no-reference = Block "{name}" has no reference in the drawing to edit.
.bp-block-referenced = Block "{name}" is referenced in the drawing and cannot be removed.
.bp-block-removed = Block "{name}" removed.
model =
.create = Crear
.boolean = Booleanas
+34
View File
@@ -4019,6 +4019,40 @@ insert =
.pcs-positive = Requires a positive number.
.pcs-positive-int = Requires a positive integer.
.pcs-help = Traces the points on the kept side of a live section as lines.
.bp-add-favorite = Add to Favorites
.bp-annotative-dynamic = Annotative / Dynamic
.bp-auto-placement = Auto-Placement
.bp-gallery-libraries = Blocks from Libraries...
.bp-choose-library = Choose a library folder with …
.bp-tab-current = Current Drawing
.bp-heading-current = Current Drawing Blocks
.bp-view-xl = Extra Large Icons
.bp-heading-favorites = Favorite Blocks
.bp-gallery-favorites = Favorite Blocks...
.bp-tab-favorites = Favorites
.bp-filter = Filter... (* ? wildcards)
.bp-icon = Icon
.bp-insertion-point = Insertion Point
.bp-view-large = Large Icons
.bp-tab-libraries = Libraries
.bp-heading-libraries = Library Blocks
.bp-view-medium = Medium Icons
.bp-tab-recent = Recent
.bp-heading-recent = Recent Blocks
.bp-gallery-recent = Recent Blocks...
.bp-redefine = Redefine
.bp-remove-block = Remove Block
.bp-remove-favorite = Remove from Favorites
.bp-remove-list = Remove from List
.bp-repeat-placement = Repeat Placement
.bp-select-library = Select a Block Library Folder
.bp-view-small = Small Icons
.bp-on-screen = specified on screen
.bp-block-not-found = Block "{}" not found.
.bp-block-redefined = Block "{name}" redefined.
.bp-block-no-reference = Block "{name}" has no reference in the drawing to edit.
.bp-block-referenced = Block "{name}" is referenced in the drawing and cannot be removed.
.bp-block-removed = Block "{name}" removed.
model =
.create = Luo
.boolean = Boolen operaatiot
+34
View File
@@ -4020,6 +4020,40 @@ insert =
.pcs-positive = Requires a positive number.
.pcs-positive-int = Requires a positive integer.
.pcs-help = Traces the points on the kept side of a live section as lines.
.bp-add-favorite = Add to Favorites
.bp-annotative-dynamic = Annotative / Dynamic
.bp-auto-placement = Auto-Placement
.bp-gallery-libraries = Blocks from Libraries...
.bp-choose-library = Choose a library folder with …
.bp-tab-current = Current Drawing
.bp-heading-current = Current Drawing Blocks
.bp-view-xl = Extra Large Icons
.bp-heading-favorites = Favorite Blocks
.bp-gallery-favorites = Favorite Blocks...
.bp-tab-favorites = Favorites
.bp-filter = Filter... (* ? wildcards)
.bp-icon = Icon
.bp-insertion-point = Insertion Point
.bp-view-large = Large Icons
.bp-tab-libraries = Libraries
.bp-heading-libraries = Library Blocks
.bp-view-medium = Medium Icons
.bp-tab-recent = Recent
.bp-heading-recent = Recent Blocks
.bp-gallery-recent = Recent Blocks...
.bp-redefine = Redefine
.bp-remove-block = Remove Block
.bp-remove-favorite = Remove from Favorites
.bp-remove-list = Remove from List
.bp-repeat-placement = Repeat Placement
.bp-select-library = Select a Block Library Folder
.bp-view-small = Small Icons
.bp-on-screen = specified on screen
.bp-block-not-found = Block "{}" not found.
.bp-block-redefined = Block "{name}" redefined.
.bp-block-no-reference = Block "{name}" has no reference in the drawing to edit.
.bp-block-referenced = Block "{name}" is referenced in the drawing and cannot be removed.
.bp-block-removed = Block "{name}" removed.
model =
.create = Créer
.boolean = Opérations booléennes
+34
View File
@@ -4008,6 +4008,40 @@ insert =
.pcs-positive = Requires a positive number.
.pcs-positive-int = Requires a positive integer.
.pcs-help = Traces the points on the kept side of a live section as lines.
.bp-add-favorite = Add to Favorites
.bp-annotative-dynamic = Annotative / Dynamic
.bp-auto-placement = Auto-Placement
.bp-gallery-libraries = Blocks from Libraries...
.bp-choose-library = Choose a library folder with …
.bp-tab-current = Current Drawing
.bp-heading-current = Current Drawing Blocks
.bp-view-xl = Extra Large Icons
.bp-heading-favorites = Favorite Blocks
.bp-gallery-favorites = Favorite Blocks...
.bp-tab-favorites = Favorites
.bp-filter = Filter... (* ? wildcards)
.bp-icon = Icon
.bp-insertion-point = Insertion Point
.bp-view-large = Large Icons
.bp-tab-libraries = Libraries
.bp-heading-libraries = Library Blocks
.bp-view-medium = Medium Icons
.bp-tab-recent = Recent
.bp-heading-recent = Recent Blocks
.bp-gallery-recent = Recent Blocks...
.bp-redefine = Redefine
.bp-remove-block = Remove Block
.bp-remove-favorite = Remove from Favorites
.bp-remove-list = Remove from List
.bp-repeat-placement = Repeat Placement
.bp-select-library = Select a Block Library Folder
.bp-view-small = Small Icons
.bp-on-screen = specified on screen
.bp-block-not-found = Block "{}" not found.
.bp-block-redefined = Block "{name}" redefined.
.bp-block-no-reference = Block "{name}" has no reference in the drawing to edit.
.bp-block-referenced = Block "{name}" is referenced in the drawing and cannot be removed.
.bp-block-removed = Block "{name}" removed.
model =
.create = बनाएँ
.boolean = बूलियन संक्रियाएँ
+34
View File
@@ -4019,6 +4019,40 @@ insert =
.pcs-positive = Requires a positive number.
.pcs-positive-int = Requires a positive integer.
.pcs-help = Traces the points on the kept side of a live section as lines.
.bp-add-favorite = Add to Favorites
.bp-annotative-dynamic = Annotative / Dynamic
.bp-auto-placement = Auto-Placement
.bp-gallery-libraries = Blocks from Libraries...
.bp-choose-library = Choose a library folder with …
.bp-tab-current = Current Drawing
.bp-heading-current = Current Drawing Blocks
.bp-view-xl = Extra Large Icons
.bp-heading-favorites = Favorite Blocks
.bp-gallery-favorites = Favorite Blocks...
.bp-tab-favorites = Favorites
.bp-filter = Filter... (* ? wildcards)
.bp-icon = Icon
.bp-insertion-point = Insertion Point
.bp-view-large = Large Icons
.bp-tab-libraries = Libraries
.bp-heading-libraries = Library Blocks
.bp-view-medium = Medium Icons
.bp-tab-recent = Recent
.bp-heading-recent = Recent Blocks
.bp-gallery-recent = Recent Blocks...
.bp-redefine = Redefine
.bp-remove-block = Remove Block
.bp-remove-favorite = Remove from Favorites
.bp-remove-list = Remove from List
.bp-repeat-placement = Repeat Placement
.bp-select-library = Select a Block Library Folder
.bp-view-small = Small Icons
.bp-on-screen = specified on screen
.bp-block-not-found = Block "{}" not found.
.bp-block-redefined = Block "{name}" redefined.
.bp-block-no-reference = Block "{name}" has no reference in the drawing to edit.
.bp-block-referenced = Block "{name}" is referenced in the drawing and cannot be removed.
.bp-block-removed = Block "{name}" removed.
model =
.create = Létrehozás
.boolean = Logikai műveletek
+34
View File
@@ -4019,6 +4019,40 @@ insert =
.pcs-positive = Requires a positive number.
.pcs-positive-int = Requires a positive integer.
.pcs-help = Traces the points on the kept side of a live section as lines.
.bp-add-favorite = Add to Favorites
.bp-annotative-dynamic = Annotative / Dynamic
.bp-auto-placement = Auto-Placement
.bp-gallery-libraries = Blocks from Libraries...
.bp-choose-library = Choose a library folder with …
.bp-tab-current = Current Drawing
.bp-heading-current = Current Drawing Blocks
.bp-view-xl = Extra Large Icons
.bp-heading-favorites = Favorite Blocks
.bp-gallery-favorites = Favorite Blocks...
.bp-tab-favorites = Favorites
.bp-filter = Filter... (* ? wildcards)
.bp-icon = Icon
.bp-insertion-point = Insertion Point
.bp-view-large = Large Icons
.bp-tab-libraries = Libraries
.bp-heading-libraries = Library Blocks
.bp-view-medium = Medium Icons
.bp-tab-recent = Recent
.bp-heading-recent = Recent Blocks
.bp-gallery-recent = Recent Blocks...
.bp-redefine = Redefine
.bp-remove-block = Remove Block
.bp-remove-favorite = Remove from Favorites
.bp-remove-list = Remove from List
.bp-repeat-placement = Repeat Placement
.bp-select-library = Select a Block Library Folder
.bp-view-small = Small Icons
.bp-on-screen = specified on screen
.bp-block-not-found = Block "{}" not found.
.bp-block-redefined = Block "{name}" redefined.
.bp-block-no-reference = Block "{name}" has no reference in the drawing to edit.
.bp-block-referenced = Block "{name}" is referenced in the drawing and cannot be removed.
.bp-block-removed = Block "{name}" removed.
model =
.create = Crea
.boolean = Operazioni booleane
+34
View File
@@ -4008,6 +4008,40 @@ insert =
.pcs-positive = Requires a positive number.
.pcs-positive-int = Requires a positive integer.
.pcs-help = Traces the points on the kept side of a live section as lines.
.bp-add-favorite = Add to Favorites
.bp-annotative-dynamic = Annotative / Dynamic
.bp-auto-placement = Auto-Placement
.bp-gallery-libraries = Blocks from Libraries...
.bp-choose-library = Choose a library folder with …
.bp-tab-current = Current Drawing
.bp-heading-current = Current Drawing Blocks
.bp-view-xl = Extra Large Icons
.bp-heading-favorites = Favorite Blocks
.bp-gallery-favorites = Favorite Blocks...
.bp-tab-favorites = Favorites
.bp-filter = Filter... (* ? wildcards)
.bp-icon = Icon
.bp-insertion-point = Insertion Point
.bp-view-large = Large Icons
.bp-tab-libraries = Libraries
.bp-heading-libraries = Library Blocks
.bp-view-medium = Medium Icons
.bp-tab-recent = Recent
.bp-heading-recent = Recent Blocks
.bp-gallery-recent = Recent Blocks...
.bp-redefine = Redefine
.bp-remove-block = Remove Block
.bp-remove-favorite = Remove from Favorites
.bp-remove-list = Remove from List
.bp-repeat-placement = Repeat Placement
.bp-select-library = Select a Block Library Folder
.bp-view-small = Small Icons
.bp-on-screen = specified on screen
.bp-block-not-found = Block "{}" not found.
.bp-block-redefined = Block "{name}" redefined.
.bp-block-no-reference = Block "{name}" has no reference in the drawing to edit.
.bp-block-referenced = Block "{name}" is referenced in the drawing and cannot be removed.
.bp-block-removed = Block "{name}" removed.
model =
.create = 作成
.boolean = ブール演算
+34
View File
@@ -4019,6 +4019,40 @@ insert =
.pcs-positive = Requires a positive number.
.pcs-positive-int = Requires a positive integer.
.pcs-help = Traces the points on the kept side of a live section as lines.
.bp-add-favorite = Add to Favorites
.bp-annotative-dynamic = Annotative / Dynamic
.bp-auto-placement = Auto-Placement
.bp-gallery-libraries = Blocks from Libraries...
.bp-choose-library = Choose a library folder with …
.bp-tab-current = Current Drawing
.bp-heading-current = Current Drawing Blocks
.bp-view-xl = Extra Large Icons
.bp-heading-favorites = Favorite Blocks
.bp-gallery-favorites = Favorite Blocks...
.bp-tab-favorites = Favorites
.bp-filter = Filter... (* ? wildcards)
.bp-icon = Icon
.bp-insertion-point = Insertion Point
.bp-view-large = Large Icons
.bp-tab-libraries = Libraries
.bp-heading-libraries = Library Blocks
.bp-view-medium = Medium Icons
.bp-tab-recent = Recent
.bp-heading-recent = Recent Blocks
.bp-gallery-recent = Recent Blocks...
.bp-redefine = Redefine
.bp-remove-block = Remove Block
.bp-remove-favorite = Remove from Favorites
.bp-remove-list = Remove from List
.bp-repeat-placement = Repeat Placement
.bp-select-library = Select a Block Library Folder
.bp-view-small = Small Icons
.bp-on-screen = specified on screen
.bp-block-not-found = Block "{}" not found.
.bp-block-redefined = Block "{name}" redefined.
.bp-block-no-reference = Block "{name}" has no reference in the drawing to edit.
.bp-block-referenced = Block "{name}" is referenced in the drawing and cannot be removed.
.bp-block-removed = Block "{name}" removed.
model =
.create = 생성
.boolean = 불리언 연산
+34
View File
@@ -4018,6 +4018,40 @@ insert =
.pcs-positive = Requires a positive number.
.pcs-positive-int = Requires a positive integer.
.pcs-help = Traces the points on the kept side of a live section as lines.
.bp-add-favorite = Add to Favorites
.bp-annotative-dynamic = Annotative / Dynamic
.bp-auto-placement = Auto-Placement
.bp-gallery-libraries = Blocks from Libraries...
.bp-choose-library = Choose a library folder with …
.bp-tab-current = Current Drawing
.bp-heading-current = Current Drawing Blocks
.bp-view-xl = Extra Large Icons
.bp-heading-favorites = Favorite Blocks
.bp-gallery-favorites = Favorite Blocks...
.bp-tab-favorites = Favorites
.bp-filter = Filter... (* ? wildcards)
.bp-icon = Icon
.bp-insertion-point = Insertion Point
.bp-view-large = Large Icons
.bp-tab-libraries = Libraries
.bp-heading-libraries = Library Blocks
.bp-view-medium = Medium Icons
.bp-tab-recent = Recent
.bp-heading-recent = Recent Blocks
.bp-gallery-recent = Recent Blocks...
.bp-redefine = Redefine
.bp-remove-block = Remove Block
.bp-remove-favorite = Remove from Favorites
.bp-remove-list = Remove from List
.bp-repeat-placement = Repeat Placement
.bp-select-library = Select a Block Library Folder
.bp-view-small = Small Icons
.bp-on-screen = specified on screen
.bp-block-not-found = Block "{}" not found.
.bp-block-redefined = Block "{name}" redefined.
.bp-block-no-reference = Block "{name}" has no reference in the drawing to edit.
.bp-block-referenced = Block "{name}" is referenced in the drawing and cannot be removed.
.bp-block-removed = Block "{name}" removed.
model =
.create = Maken
.boolean = Booleaanse bewerkingen
+34
View File
@@ -4019,6 +4019,40 @@ insert =
.pcs-positive = Requires a positive number.
.pcs-positive-int = Requires a positive integer.
.pcs-help = Traces the points on the kept side of a live section as lines.
.bp-add-favorite = Add to Favorites
.bp-annotative-dynamic = Annotative / Dynamic
.bp-auto-placement = Auto-Placement
.bp-gallery-libraries = Blocks from Libraries...
.bp-choose-library = Choose a library folder with …
.bp-tab-current = Current Drawing
.bp-heading-current = Current Drawing Blocks
.bp-view-xl = Extra Large Icons
.bp-heading-favorites = Favorite Blocks
.bp-gallery-favorites = Favorite Blocks...
.bp-tab-favorites = Favorites
.bp-filter = Filter... (* ? wildcards)
.bp-icon = Icon
.bp-insertion-point = Insertion Point
.bp-view-large = Large Icons
.bp-tab-libraries = Libraries
.bp-heading-libraries = Library Blocks
.bp-view-medium = Medium Icons
.bp-tab-recent = Recent
.bp-heading-recent = Recent Blocks
.bp-gallery-recent = Recent Blocks...
.bp-redefine = Redefine
.bp-remove-block = Remove Block
.bp-remove-favorite = Remove from Favorites
.bp-remove-list = Remove from List
.bp-repeat-placement = Repeat Placement
.bp-select-library = Select a Block Library Folder
.bp-view-small = Small Icons
.bp-on-screen = specified on screen
.bp-block-not-found = Block "{}" not found.
.bp-block-redefined = Block "{name}" redefined.
.bp-block-no-reference = Block "{name}" has no reference in the drawing to edit.
.bp-block-referenced = Block "{name}" is referenced in the drawing and cannot be removed.
.bp-block-removed = Block "{name}" removed.
model =
.create = Utwórz
.boolean = Operacje boolowskie
+34
View File
@@ -4020,6 +4020,40 @@ insert =
.pcs-positive = Requires a positive number.
.pcs-positive-int = Requires a positive integer.
.pcs-help = Traces the points on the kept side of a live section as lines.
.bp-add-favorite = Add to Favorites
.bp-annotative-dynamic = Annotative / Dynamic
.bp-auto-placement = Auto-Placement
.bp-gallery-libraries = Blocks from Libraries...
.bp-choose-library = Choose a library folder with …
.bp-tab-current = Current Drawing
.bp-heading-current = Current Drawing Blocks
.bp-view-xl = Extra Large Icons
.bp-heading-favorites = Favorite Blocks
.bp-gallery-favorites = Favorite Blocks...
.bp-tab-favorites = Favorites
.bp-filter = Filter... (* ? wildcards)
.bp-icon = Icon
.bp-insertion-point = Insertion Point
.bp-view-large = Large Icons
.bp-tab-libraries = Libraries
.bp-heading-libraries = Library Blocks
.bp-view-medium = Medium Icons
.bp-tab-recent = Recent
.bp-heading-recent = Recent Blocks
.bp-gallery-recent = Recent Blocks...
.bp-redefine = Redefine
.bp-remove-block = Remove Block
.bp-remove-favorite = Remove from Favorites
.bp-remove-list = Remove from List
.bp-repeat-placement = Repeat Placement
.bp-select-library = Select a Block Library Folder
.bp-view-small = Small Icons
.bp-on-screen = specified on screen
.bp-block-not-found = Block "{}" not found.
.bp-block-redefined = Block "{name}" redefined.
.bp-block-no-reference = Block "{name}" has no reference in the drawing to edit.
.bp-block-referenced = Block "{name}" is referenced in the drawing and cannot be removed.
.bp-block-removed = Block "{name}" removed.
model =
.create = Criar
.boolean = Operações booleanas
+34
View File
@@ -4021,6 +4021,40 @@ insert =
.pcs-positive = Requires a positive number.
.pcs-positive-int = Requires a positive integer.
.pcs-help = Traces the points on the kept side of a live section as lines.
.bp-add-favorite = Add to Favorites
.bp-annotative-dynamic = Annotative / Dynamic
.bp-auto-placement = Auto-Placement
.bp-gallery-libraries = Blocks from Libraries...
.bp-choose-library = Choose a library folder with …
.bp-tab-current = Current Drawing
.bp-heading-current = Current Drawing Blocks
.bp-view-xl = Extra Large Icons
.bp-heading-favorites = Favorite Blocks
.bp-gallery-favorites = Favorite Blocks...
.bp-tab-favorites = Favorites
.bp-filter = Filter... (* ? wildcards)
.bp-icon = Icon
.bp-insertion-point = Insertion Point
.bp-view-large = Large Icons
.bp-tab-libraries = Libraries
.bp-heading-libraries = Library Blocks
.bp-view-medium = Medium Icons
.bp-tab-recent = Recent
.bp-heading-recent = Recent Blocks
.bp-gallery-recent = Recent Blocks...
.bp-redefine = Redefine
.bp-remove-block = Remove Block
.bp-remove-favorite = Remove from Favorites
.bp-remove-list = Remove from List
.bp-repeat-placement = Repeat Placement
.bp-select-library = Select a Block Library Folder
.bp-view-small = Small Icons
.bp-on-screen = specified on screen
.bp-block-not-found = Block "{}" not found.
.bp-block-redefined = Block "{name}" redefined.
.bp-block-no-reference = Block "{name}" has no reference in the drawing to edit.
.bp-block-referenced = Block "{name}" is referenced in the drawing and cannot be removed.
.bp-block-removed = Block "{name}" removed.
model =
.create = Создание
.boolean = Булевы операции
+34
View File
@@ -3980,6 +3980,40 @@ insert =
.pcs-positive = Pozitif bir sayı gerekir.
.pcs-positive-int = Pozitif bir tamsayı gerekir.
.pcs-help = Canlı kesitin korunan tarafındaki noktaları çizgilerle izler.
.bp-add-favorite = Sık kullanılanlara ekle
.bp-annotative-dynamic = Açıklayıcı / Dinamik
.bp-auto-placement = Otomatik yerleştirme
.bp-gallery-libraries = Kütüphanelerden bloklar...
.bp-choose-library = … ile bir kütüphane klasörü seçin
.bp-tab-current = Geçerli Çizim
.bp-heading-current = Geçerli çizimdeki bloklar
.bp-view-xl = Çok büyük simgeler
.bp-heading-favorites = Sık kullanılan bloklar
.bp-gallery-favorites = Sık kullanılan bloklar...
.bp-tab-favorites = Sık Kullanılan
.bp-filter = Filtre... (* ? joker)
.bp-icon = Simge
.bp-insertion-point = Ekleme noktası
.bp-view-large = Büyük simgeler
.bp-tab-libraries = Kütüphaneler
.bp-heading-libraries = Kütüphane blokları
.bp-view-medium = Orta simgeler
.bp-tab-recent = Son Kullanılan
.bp-heading-recent = Son kullanılan bloklar
.bp-gallery-recent = Son kullanılan bloklar...
.bp-redefine = Yeniden tanımla
.bp-remove-block = Bloğu kaldır
.bp-remove-favorite = Sık kullanılanlardan çıkar
.bp-remove-list = Listeden kaldır
.bp-repeat-placement = Yerleştirmeyi yinele
.bp-select-library = Blok kütüphanesi klasörü seçin
.bp-view-small = Küçük simgeler
.bp-on-screen = ekranda belirtilir
.bp-block-not-found = "__ocs_fmt_0__" bloğu bulunamadı.
.bp-block-redefined = "__ocs_fmt_0__" bloğu yeniden tanımlandı.
.bp-block-no-reference = "__ocs_fmt_0__" bloğunun çizimde düzenlenecek bir başvurusu yok.
.bp-block-referenced = "__ocs_fmt_0__" bloğu çizimde kullanılıyor, kaldırılamaz.
.bp-block-removed = "__ocs_fmt_0__" bloğu kaldırıldı.
model =
.create = Oluştur
.boolean = Boole işlemleri
+34
View File
@@ -4003,6 +4003,40 @@ insert =
.pcs-positive = Requires a positive number.
.pcs-positive-int = Requires a positive integer.
.pcs-help = Traces the points on the kept side of a live section as lines.
.bp-add-favorite = Add to Favorites
.bp-annotative-dynamic = Annotative / Dynamic
.bp-auto-placement = Auto-Placement
.bp-gallery-libraries = Blocks from Libraries...
.bp-choose-library = Choose a library folder with …
.bp-tab-current = Current Drawing
.bp-heading-current = Current Drawing Blocks
.bp-view-xl = Extra Large Icons
.bp-heading-favorites = Favorite Blocks
.bp-gallery-favorites = Favorite Blocks...
.bp-tab-favorites = Favorites
.bp-filter = Filter... (* ? wildcards)
.bp-icon = Icon
.bp-insertion-point = Insertion Point
.bp-view-large = Large Icons
.bp-tab-libraries = Libraries
.bp-heading-libraries = Library Blocks
.bp-view-medium = Medium Icons
.bp-tab-recent = Recent
.bp-heading-recent = Recent Blocks
.bp-gallery-recent = Recent Blocks...
.bp-redefine = Redefine
.bp-remove-block = Remove Block
.bp-remove-favorite = Remove from Favorites
.bp-remove-list = Remove from List
.bp-repeat-placement = Repeat Placement
.bp-select-library = Select a Block Library Folder
.bp-view-small = Small Icons
.bp-on-screen = specified on screen
.bp-block-not-found = Block "{}" not found.
.bp-block-redefined = Block "{name}" redefined.
.bp-block-no-reference = Block "{name}" has no reference in the drawing to edit.
.bp-block-referenced = Block "{name}" is referenced in the drawing and cannot be removed.
.bp-block-removed = Block "{name}" removed.
model =
.create = 创建
.boolean = 布尔运算
+34
View File
@@ -4015,6 +4015,40 @@ insert =
.pcs-positive = Requires a positive number.
.pcs-positive-int = Requires a positive integer.
.pcs-help = Traces the points on the kept side of a live section as lines.
.bp-add-favorite = Add to Favorites
.bp-annotative-dynamic = Annotative / Dynamic
.bp-auto-placement = Auto-Placement
.bp-gallery-libraries = Blocks from Libraries...
.bp-choose-library = Choose a library folder with …
.bp-tab-current = Current Drawing
.bp-heading-current = Current Drawing Blocks
.bp-view-xl = Extra Large Icons
.bp-heading-favorites = Favorite Blocks
.bp-gallery-favorites = Favorite Blocks...
.bp-tab-favorites = Favorites
.bp-filter = Filter... (* ? wildcards)
.bp-icon = Icon
.bp-insertion-point = Insertion Point
.bp-view-large = Large Icons
.bp-tab-libraries = Libraries
.bp-heading-libraries = Library Blocks
.bp-view-medium = Medium Icons
.bp-tab-recent = Recent
.bp-heading-recent = Recent Blocks
.bp-gallery-recent = Recent Blocks...
.bp-redefine = Redefine
.bp-remove-block = Remove Block
.bp-remove-favorite = Remove from Favorites
.bp-remove-list = Remove from List
.bp-repeat-placement = Repeat Placement
.bp-select-library = Select a Block Library Folder
.bp-view-small = Small Icons
.bp-on-screen = specified on screen
.bp-block-not-found = Block "{}" not found.
.bp-block-redefined = Block "{name}" redefined.
.bp-block-no-reference = Block "{name}" has no reference in the drawing to edit.
.bp-block-referenced = Block "{name}" is referenced in the drawing and cannot be removed.
.bp-block-removed = Block "{name}" removed.
model =
.create = 建立
.boolean = 布林運算
+21 -2
View File
@@ -748,6 +748,7 @@ impl OpenCADStudio {
self.command_line
.push_output(crate::tf!("Block \"{name}\" created.").as_ref());
self.refresh_properties();
self.note_recent_block(&name);
self.refresh_block_palette();
}
Err(err) => {
@@ -985,7 +986,16 @@ impl OpenCADStudio {
self.commit_entity(entity);
self.tabs[i].dirty = true;
self.tabs[i].scene.clear_preview_wire();
self.tabs[i].active_cmd = None;
// A repeating insertion keeps its command for the next block.
let keep = self.tabs[i].active_cmd.as_ref().is_some_and(|c| c.attreq_continue());
if keep {
let prompt = self.tabs[i].active_cmd.as_ref().map(|c| c.prompt());
if let Some(p) = prompt {
self.command_line.push_info(&p);
}
} else {
self.tabs[i].active_cmd = None;
}
self.tabs[i].snap_result = None;
if let Some(n) = insert_name {
self.record_block_insert(&n);
@@ -1001,7 +1011,16 @@ impl OpenCADStudio {
.as_mut()
.and_then(|cmd| cmd.attreq_set_attdefs(attdefs));
if let Some(entity) = completed {
return Some(self.apply_cmd_result(CmdResult::CommitAndExit(entity)));
if let codec::EntityType::Insert(ins) = &entity {
let name = ins.block_name.clone();
self.record_block_insert(&name);
}
let keep = self.tabs[i].active_cmd.as_ref().is_some_and(|c| c.attreq_continue());
return Some(self.apply_cmd_result(if keep {
CmdResult::CommitEntity(entity)
} else {
CmdResult::CommitAndExit(entity)
}));
}
let prompt = self.tabs[i].active_cmd.as_ref().map(|c| c.prompt());
if let Some(p) = prompt {
+26
View File
@@ -767,6 +767,32 @@ impl OpenCADStudio {
return task;
}
}
CmdResult::CommitExplodedInsert { insert, keep_going } => {
let i = self.active_tab;
if let codec::EntityType::Insert(ins) = &insert {
let name = ins.block_name.clone();
self.record_block_insert(&name);
}
let pieces = crate::modules::draw::modify::explode::explode_entity(
&insert,
&self.tabs[i].scene.document,
);
if keep_going {
if let Some(task) = self.handle_commit_entities(
pieces,
preserve_commit_style,
preserve_commit_layer,
) {
return task;
}
} else {
self.handle_commit_entities_and_exit(
pieces,
preserve_commit_style,
preserve_commit_layer,
);
}
}
CmdResult::AttreqNeeded { block_name } => {
if let Some(task) = self.handle_attreq_needed(block_name) {
return task;
+63 -26
View File
@@ -489,25 +489,50 @@ impl OpenCADStudio {
self.active_modal = Some(crate::app::ModalKind::WriteBlock);
}
// INSERT opens the Blocks palette on the drawing's blocks;
// -INSERT runs on the command line.
"INSERT" => {
self.command_line.push_output("*Insert a block from the Blocks palette");
self.open_blocks_palette(Some(crate::ui::window::block_palette::Tab::Current));
}
"-INSERT" => {
use crate::command::CadCommand;
use crate::modules::insert::insert_block::InsertBlockCommand;
let blocks = self.tabs[i].scene.custom_block_names();
if blocks.is_empty() {
self.command_line.push_error(
crate::t!("No user-defined blocks found in this drawing.").as_ref(),
);
} else {
use crate::modules::insert::insert_block::InsertBlockCommand;
let ranked = self.ranked_block_names(&blocks);
let snapshot = self.block_usage_snapshot();
let cmd = InsertBlockCommand::new_with_usage(
ranked,
snapshot,
self.cliprompt_lines.clamp(0, 50) as u8,
);
self.command_line.push_info(&cmd.prompt());
let opts = cmd.options();
self.command_line.set_step_options(opts.clone());
self.tabs[i].active_cmd = Some(Box::new(cmd));
let ranked = self.ranked_block_names(&blocks);
let snapshot = self.block_usage_snapshot();
let cmd = InsertBlockCommand::classic(
ranked,
snapshot,
self.cliprompt_lines.clamp(0, 50) as u8,
self.block_catalog(),
self.insname.clone(),
);
self.command_line.push_info(&cmd.prompt());
self.command_line.set_step_options(cmd.options());
self.tabs[i].active_cmd = Some(Box::new(cmd));
}
// A drawing named at -INSERT's block prompt: brought in as a
// block, then placed.
cmd if cmd.starts_with("_-INSERTFILE ") => {
use crate::command::CadCommand;
use crate::modules::insert::insert_block::InsertBlockCommand;
let path = std::path::PathBuf::from(cmd["_-INSERTFILE ".len()..].trim().trim_matches('"'));
match self.import_file_as_block(path) {
Ok(name) => {
let units = self
.block_catalog()
.units
.remove(&name.to_ascii_uppercase())
.unwrap_or_default();
let preview = self.tabs[i].scene.block_preview_wires(&name);
let cmd = InsertBlockCommand::classic_for_block(name, preview, units);
self.command_line.push_info(&cmd.prompt());
self.tabs[i].active_cmd = Some(Box::new(cmd));
}
Err(e) => self.command_line.push_error(e.as_str()),
}
}
@@ -667,15 +692,27 @@ impl OpenCADStudio {
);
}
// BLOCKPALETTE / BLOCKSPALETTE — toggle the docked Insert Block panel.
"BLOCKPALETTE" | "BLOCKSPALETTE" => {
self.show_block_palette ^= true;
if self.show_block_palette {
// Always open expanded so the panel is immediately usable;
// the user can still collapse it via the pin (Auto) button.
self.dock_expanded = Some(crate::ui::dock::PanelId::BlockPalette);
self.refresh_block_palette();
}
// BLOCKSPALETTE opens the Blocks palette; BLOCKSPALETTECLOSE
// closes it. `_BLOCKSPALETTE <tab>` opens it on a tab.
"BLOCKPALETTE" | "BLOCKSPALETTE" => self.open_blocks_palette(None),
"BLOCKSPALETTECLOSE" => {
self.show_block_palette = false;
self.block_palette.placing = None;
}
// A block picked in the ribbon gallery.
cmd if cmd.starts_with("_BLOCKINSERT ") => {
let name = cmd["_BLOCKINSERT ".len()..].trim().to_string();
return Some(self.palette_insert(crate::ui::window::block_palette::Item::Current(name), None));
}
cmd if cmd.starts_with("_BLOCKSPALETTE ") => {
use crate::ui::window::block_palette::Tab;
let tab = match cmd["_BLOCKSPALETTE ".len()..].trim() {
"RECENT" => Tab::Recent,
"FAVORITES" => Tab::Favorites,
"LIBRARIES" => Tab::Libraries,
_ => Tab::Current,
};
self.open_blocks_palette(Some(tab));
}
// ATTMAN / BATTMAN — the Block Attribute Manager. Rather than a
+125
View File
@@ -0,0 +1,125 @@
//! Block system variables. `BLOCKMRULIST`, `BLOCKREDEFINEMODE` and
//! `BLOCKNAVIGATE` are application preferences kept with the settings;
//! `EXPLMODE` lives in the drawing; `INSNAME` is the session's default block
//! name; `BLOCKSTATE` is read-only. Each answers to its bare name (prompting
//! for the value) and to `SETVAR NAME value`, with the reference's messages.
use crate::app::{Message, OpenCADStudio};
use iced::Task;
/// The variables this family answers for, for the command registry and the
/// `SETVAR ?` listing.
pub(super) const BLOCK_SYSVARS: &[&str] = &[
"BLOCKMRULIST",
"BLOCKREDEFINEMODE",
"BLOCKNAVIGATE",
"BLOCKSTATE",
"EXPLMODE",
"INSNAME",
];
/// The `SETVAR ?` line for this family.
pub(super) fn setvar_listing() -> String {
format!("SETVAR (block): {}", BLOCK_SYSVARS.join(" "))
}
/// EXPLMODE of the drawing (default 1).
pub(crate) fn explmode(document: &codec::CadDocument) -> i16 {
crate::io::drawing_variable(document, "EXPLMODE")
.and_then(|v| v.trim().parse().ok())
.unwrap_or(1)
}
impl OpenCADStudio {
pub(super) fn dispatch_blockvars(&mut self, cmd: &str, i: usize) -> Option<Task<Message>> {
let rest = cmd.strip_prefix("SETVAR ").map(str::trim).unwrap_or(cmd);
let mut parts = rest.splitn(2, char::is_whitespace);
let name = parts.next().unwrap_or("").to_ascii_uppercase();
if !BLOCK_SYSVARS.contains(&name.as_str()) {
return None;
}
let value = parts.next().map(str::trim).filter(|v| !v.is_empty());
match name.as_str() {
"BLOCKSTATE" => {
let state = self.tabs[i].refedit_session.is_some() as u8;
self.command_line.push_output(&format!("BLOCKSTATE = {state} (read only)"));
}
"INSNAME" | "BLOCKNAVIGATE" => {
let current = if name == "INSNAME" {
self.insname.clone()
} else {
self.block_navigate.clone()
};
match value {
Some(v) => {
let v = if v == "." { String::new() } else { v.trim_matches('"').to_string() };
if name == "INSNAME" {
self.insname = v;
} else {
self.block_navigate = if v.is_empty() { ".".to_string() } else { v };
self.persist_settings_if_changed();
}
}
None => {
self.command_line.push_output(&format!(
"Enter new value for {name}, or . for none <\"{current}\">:"
));
self.pending_setvar = Some(name);
}
}
}
_ => {
let (current, max) = match name.as_str() {
"BLOCKMRULIST" => (self.block_mru_list as i32, 100),
"BLOCKREDEFINEMODE" => (self.block_redefine_mode as i32, 2),
_ => (explmode(&self.tabs[i].scene.document) as i32, 1),
};
let ask = |app: &mut Self, name: String| {
app.command_line
.push_output(&format!("Enter new value for {name} <{current}>:"));
app.pending_setvar = Some(name);
};
match value {
None => ask(self, name),
Some(v) => match v.parse::<i32>() {
Err(_) => {
self.command_line.push_error("Requires an integer value.");
ask(self, name);
}
Ok(n) if !(0..=max).contains(&n) => {
self.command_line.push_error(&if max == 1 {
"Requires 0 or 1 only.".to_string()
} else {
format!("Requires an integer between 0 and {max}.")
});
ask(self, name);
}
Ok(n) => match name.as_str() {
"BLOCKMRULIST" => {
self.block_mru_list = n as u8;
self.trim_recent_blocks();
self.persist_settings_if_changed();
}
"BLOCKREDEFINEMODE" => {
self.block_redefine_mode = n as u8;
self.persist_settings_if_changed();
}
_ => {
if n != current {
self.push_undo_snapshot(i, "EXPLMODE");
crate::io::set_drawing_variable(
&mut self.tabs[i].scene.document,
"EXPLMODE",
&n.to_string(),
);
self.tabs[i].dirty = true;
}
}
},
},
}
}
}
Some(Task::none())
}
}
+21
View File
@@ -1217,6 +1217,27 @@ impl OpenCADStudio {
self.command_line.push_info(&cmd.prompt());
self.tabs[i].active_cmd = Some(Box::new(cmd));
} else {
// EXPLMODE 0 keeps references that are not uniformly and
// positively scaled whole.
let selected: Vec<_> = if super::blockvars::explmode(&self.tabs[i].scene.document) == 0 {
selected
.into_iter()
.filter(|(_, entity)| match entity {
codec::EntityType::Insert(ins) => {
let (x, y, z) = (ins.x_scale(), ins.y_scale(), ins.z_scale());
let uniform = x > 0.0 && (x - y).abs() <= 1e-10 && (x - z).abs() <= 1e-10;
if !uniform {
self.command_line
.push_output("The object is not uniformly and positively scaled.");
}
uniform
}
_ => true,
})
.collect()
} else {
selected
};
let replacements = plan_explode(&selected, &self.tabs[i].scene.document);
let exploded = replacements.len();
if exploded > 0 {
+12
View File
@@ -20,6 +20,7 @@ mod pc_extract;
mod pdf_underlay;
mod pdf_dialogs;
mod xclip;
mod blockvars;
mod plotvars;
mod styleprops;
mod view;
@@ -296,6 +297,9 @@ impl OpenCADStudio {
if let Some(t) = self.dispatch_plotvars(cmd, i) {
return Some(t);
}
if let Some(t) = self.dispatch_blockvars(cmd, i) {
return Some(t);
}
if let Some(t) = self.dispatch_styleprops(cmd, i) {
return Some(t);
}
@@ -473,6 +477,14 @@ inventory::submit!(crate::command::CommandRegistration {
// Block list + block-attribute list (command-line forms).
"BLOCKPALETTE",
"BLOCKSPALETTE",
"BLOCKSPALETTECLOSE",
"-INSERT",
"BLOCKMRULIST",
"BLOCKREDEFINEMODE",
"BLOCKNAVIGATE",
"BLOCKSTATE",
"EXPLMODE",
"INSNAME",
"ATTMAN",
"BATTMAN",
// Drawing-content overview.
+1
View File
@@ -1107,6 +1107,7 @@ impl OpenCADStudio {
let value = it.next().map(|s| s.trim().to_string());
if name.is_empty() || name == "?" {
self.command_line.push_info(&super::plotvars::setvar_listing());
self.command_line.push_info(&super::blockvars::setvar_listing());
self.command_line.push_info(
crate::t!("SETVAR: CETRANSPARENCY LTSCALE CELTSCALE PDMODE PDSIZE TEXTSIZE ORTHOMODE FILLMODE MIRRTEXT FRAME IMAGEFRAME PDFFRAME WIPEOUTFRAME XCLIPFRAME POINTCLOUDCLIPFRAME ZOOMWHEEL ZOOMFACTOR SHORTCUTMENU SHORTCUTMENUDURATION CURSORSIZE PICKBOX CURSORTYPE SNAPANG TEXTFILL CLIPROMPTLINES COMMANDLINEFADETIME ATTREQ ATTDIA DIMASSOC DIMCONTINUEMODE CONSTRAINTSOLVEMODE CONSTRAINTINFER CONSTRAINTBARDISPLAY CONSTRAINTBARMODE CONSTRAINTNAMEFORMAT DYNCONSTRAINTDISPLAY ANGBASE ANGDIR SKETCHINC SKPOLY SKTOLERANCE DONUTID DONUTOD CENTEREXE CENTERLAYER CENTERLTYPE CENTERLTSCALE CENTERLTYPEFILE CENTERCROSSSIZE CENTERCROSSGAP CENTERMARKEXE COLORTHEME SELECTIONAREA SELECTIONAREAOPACITY SELECTIONEFFECT SELECTIONEFFECTCOLOR WINDOWSAREACOLOR CROSSINGAREACOLOR SELECTIONPREVIEW GRIPSIZE GRIPCOLOR GRIPHOT GRIPHOVER GRIPOBJLIMIT | CLAYER CELTYPE TEXTSTYLE (read-only)").as_ref(),
);
+88
View File
@@ -73,6 +73,7 @@ pub(super) const NAMES: &[&str] = &[
"pdf_layer_toggle",
"pc_manager_toggle",
"pc_manager",
"blocks_palette",
"pdf_page_select",
"pc_colormap",
"pc_section",
@@ -768,6 +769,93 @@ impl OpenCADStudio {
_ => return Err(failure("invalid_value", "Unknown point cloud manager edit")),
})
}
// Blocks palette: "tab=recent", "search=D*", "view=2",
// "option=x:2", "options", "library=<dir>", "insert=<item>",
// "drop=<item>@x,y", "menu=<action>:<item>". An item is
// "current:NAME", "recent:N", "fav:N", "lib:N" or "file:<path>".
"blocks_palette" => {
use crate::ui::window::block_palette::{BlockPaletteMsg as M, Item, MenuAction, OptionMsg, Tab, ViewMode};
let value = string(req, "value")?;
let (key, arg) = value.split_once('=').unwrap_or((value, ""));
let item = |app: &Self, spec: &str| -> Result<Item, Value> {
let (kind, rest) = spec.split_once(':').unwrap_or((spec, ""));
let index = || rest.parse::<usize>().map_err(|_| failure("invalid_value", "Bad item index"));
let missing = || failure("invalid_value", "No such palette item");
Ok(match kind {
"current" => Item::Current(rest.to_string()),
"recent" => Item::Ref(app.block_palette.recent.get(index()?).cloned().ok_or_else(missing)?),
"fav" => Item::Ref(app.block_palette.favorites.get(index()?).cloned().ok_or_else(missing)?),
"lib" => {
let e = app.block_palette.library_entries.get(index()?).ok_or_else(missing)?;
if e.folder { Item::Folder(e.path.clone()) } else { Item::File(e.path.clone()) }
}
"file" => Item::File(std::path::PathBuf::from(rest)),
_ => return Err(missing()),
})
};
match key {
"tab" => Message::BlockPalette(M::Tab(match arg {
"recent" => Tab::Recent,
"favorites" => Tab::Favorites,
"libraries" => Tab::Libraries,
_ => Tab::Current,
})),
"search" => Message::BlockPalette(M::Search(arg.into())),
"view" => Message::BlockPalette(M::View(ViewMode::from_u8(arg.parse().unwrap_or(1)))),
"options" => Message::BlockPalette(M::ToggleOptions),
"library" => Message::BlockPalette(M::LibraryPicked(Ok(arg.into()))),
"up" => Message::BlockPalette(M::LibraryUp),
"option" => {
let (name, v) = arg.split_once(':').unwrap_or((arg, "1"));
let on = v == "1";
Message::BlockPalette(M::Option(match name {
"insertion_point" => OptionMsg::InsertionPoint(on),
"scale" => OptionMsg::Scale(on),
"uniform" => OptionMsg::Uniform(on),
"rotation" => OptionMsg::Rotation(on),
"auto" => OptionMsg::AutoPlacement(on),
"repeat" => OptionMsg::Repeat(on),
"explode" => OptionMsg::Explode(on),
"x" => OptionMsg::X(v.into()),
"y" => OptionMsg::Y(v.into()),
"z" => OptionMsg::Z(v.into()),
"angle" => OptionMsg::Angle(v.into()),
_ => return Err(failure("invalid_value", "Unknown palette option")),
}))
}
"insert" => {
let it = item(self, arg)?;
self.block_palette.pressed = Some(it.clone());
Message::BlockPalette(M::Release(it))
}
"drop" => {
let (spec, at) = arg.rsplit_once('@').ok_or_else(|| failure("invalid_value", "drop needs @x,y"))?;
let xy: Vec<f64> = at.split(',').filter_map(|v| v.trim().parse().ok()).collect();
if xy.len() < 2 {
return Err(failure("invalid_value", "drop needs @x,y"));
}
let it = item(self, spec)?;
let i = self.active_tab;
self.tabs[i].last_cursor_world = glam::DVec3::new(xy[0], xy[1], xy.get(2).copied().unwrap_or(0.0));
self.block_palette.pressed = Some(it);
Message::ViewportLeftRelease
}
"menu" => {
let (action, spec) = arg.split_once(':').ok_or_else(|| failure("invalid_value", "menu needs action:item"))?;
let action = match action {
"insert" => MenuAction::Insert,
"redefine" => MenuAction::Redefine,
"favorite" => MenuAction::Favorite,
"unfavorite" => MenuAction::Unfavorite,
"edit" => MenuAction::Edit,
"remove" => MenuAction::Remove,
_ => return Err(failure("invalid_value", "Unknown menu action")),
};
Message::BlockPalette(M::Menu(item(self, spec)?, action))
}
_ => return Err(failure("invalid_value", "Unknown blocks palette edit")),
}
}
// Attach dialog: choose pages by index ("0,2").
"pdf_page_select" => {
let pages: Vec<usize> = string(req, "value")?
+1 -1
View File
@@ -676,7 +676,7 @@ impl OpenCADStudio {
/// Silent no-op for an unknown name (callers validate first);
/// `*`-prefixed layout records are rejected by the callers. Returns the
/// number of entities erased.
fn erase_block_definition(scene: &mut crate::scene::Scene, name: &str) -> usize {
pub(crate) fn erase_block_definition(scene: &mut crate::scene::Scene, name: &str) -> usize {
let Some(record) = scene.document.block_records.get(name) else {
return 0;
};
+1
View File
@@ -1340,6 +1340,7 @@ impl OpenCADStudio {
mod actions;
pub(crate) use actions::property_json;
mod entities;
pub(crate) use entities::erase_block_definition;
#[cfg(not(target_arch = "wasm32"))]
pub(crate) mod http_bridge;
mod interactive;
+12
View File
@@ -671,6 +671,14 @@ pub(super) struct OpenCADStudio {
block_mru: Vec<String>,
/// Insertion frequency per block name (uppercase key → count), capped.
block_freq: std::collections::HashMap<String, u32>,
/// BLOCKMRULIST: how many recent blocks the palette keeps (0–100).
pub(crate) block_mru_list: u8,
/// BLOCKREDEFINEMODE (0–2).
pub(crate) block_redefine_mode: u8,
/// BLOCKNAVIGATE: the Libraries tab's start folder ("." for none).
pub(crate) block_navigate: String,
/// INSNAME: the default block name for -INSERT (session only).
pub(crate) insname: String,
/// Last time block-usage was flushed to disk (debounce per 2.4).
#[cfg(not(target_arch = "wasm32"))]
block_usage_last_persist: Option<std::time::Instant>,
@@ -4142,6 +4150,10 @@ impl OpenCADStudio {
commandline_fade_ms: 3000,
block_mru: Vec::new(),
block_freq: std::collections::HashMap::new(),
block_mru_list: 50,
block_redefine_mode: 1,
block_navigate: ".".to_string(),
insname: String::new(),
#[cfg(not(target_arch = "wasm32"))]
block_usage_last_persist: None,
awaiting_vports: false,
+98
View File
@@ -546,6 +546,96 @@ pub struct UserSettings {
/// Insertion frequency per block name (uppercase key → count).
#[serde(default)]
pub block_freq: std::collections::HashMap<String, u32>,
/// Blocks palette: the recent list (newest first, BLOCKMRULIST long).
#[serde(default)]
pub block_recent: Vec<PaletteBlockRef>,
/// Blocks palette: favorites, kept locally.
#[serde(default)]
pub block_favorites: Vec<PaletteBlockRef>,
/// Blocks palette: library folders or drawings, last used first.
#[serde(default)]
pub block_libraries: Vec<String>,
/// Blocks palette view: 0 extra large, 1 large, 2 medium, 3 small icons,
/// 4 details, 5 list.
#[serde(default = "default_block_view")]
pub block_palette_view: u8,
/// Blocks palette insertion options.
#[serde(default)]
pub block_insert: BlockInsertOptions,
/// BLOCKMRULIST (0–100, default 50).
#[serde(default = "default_block_mru_list")]
pub block_mru_list: u8,
/// BLOCKREDEFINEMODE (0–2, default 1).
#[serde(default = "default_one_u8")]
pub block_redefine_mode: u8,
/// BLOCKNAVIGATE (default ".").
#[serde(default = "default_block_navigate")]
pub block_navigate: String,
}
/// A block the Blocks palette lists from outside the current drawing (a
/// recent or favorite entry): its name, the drawing it came from (empty for
/// an unsaved drawing), when it was last used (Unix seconds), and whether it
/// stands for a whole drawing inserted as a block.
#[derive(Clone, Debug, PartialEq, Serialize, Deserialize)]
pub struct PaletteBlockRef {
pub name: String,
#[serde(default)]
pub source: String,
#[serde(default)]
pub time: u64,
#[serde(default)]
pub drawing: bool,
}
/// The Blocks palette's insertion options.
#[derive(Clone, Debug, PartialEq, Serialize, Deserialize)]
pub struct BlockInsertOptions {
pub insertion_point: bool,
pub scale: bool,
pub uniform: bool,
pub x: f64,
pub y: f64,
pub z: f64,
pub rotation: bool,
pub angle: f64,
pub auto_placement: bool,
pub repeat: bool,
pub explode: bool,
}
impl Default for BlockInsertOptions {
fn default() -> Self {
Self {
insertion_point: true,
scale: false,
uniform: false,
x: 1.0,
y: 1.0,
z: 1.0,
rotation: false,
angle: 0.0,
auto_placement: true,
repeat: false,
explode: false,
}
}
}
fn default_block_view() -> u8 {
1
}
fn default_block_mru_list() -> u8 {
50
}
fn default_one_u8() -> u8 {
1
}
fn default_block_navigate() -> String {
".".to_string()
}
fn default_clipromptlines() -> i32 {
@@ -710,6 +800,14 @@ impl Default for UserSettings {
grid_beyond_limits: true,
block_mru: Vec::new(),
block_freq: std::collections::HashMap::new(),
block_recent: Vec::new(),
block_favorites: Vec::new(),
block_libraries: Vec::new(),
block_palette_view: 1,
block_insert: BlockInsertOptions::default(),
block_mru_list: 50,
block_redefine_mode: 1,
block_navigate: ".".to_string(),
}
}
}
+603
View File
@@ -0,0 +1,603 @@
//! The Blocks palette on the app side: tabs, the recent / favorite lists,
//! library folders, the insertion options, click and drag insertion, and the
//! block context menu.
use std::path::{Path, PathBuf};
use crate::app::settings::PaletteBlockRef;
use crate::app::{Message, OpenCADStudio};
use crate::modules::insert::insert_block::{BlockCatalog, InsertBlockCommand};
use crate::ui::window::block_palette::{
BlockEntry, BlockPaletteMsg, Item, LibraryEntry, MenuAction, OptionMsg, Tab,
};
use iced::Task;
/// Library folders remembered in the Libraries list.
const MAX_LIBRARIES: usize = 10;
fn now_secs() -> u64 {
#[cfg(not(target_arch = "wasm32"))]
{
std::time::SystemTime::now()
.duration_since(std::time::UNIX_EPOCH)
.map(|d| d.as_secs())
.unwrap_or(0)
}
#[cfg(target_arch = "wasm32")]
{
0
}
}
fn file_stem(path: &Path) -> String {
path.file_stem()
.map(|s| s.to_string_lossy().into_owned())
.unwrap_or_else(|| "Block".to_string())
}
fn is_drawing(path: &Path) -> bool {
path.extension()
.and_then(|e| e.to_str())
.is_some_and(|e| e.eq_ignore_ascii_case("dwg") || e.eq_ignore_ascii_case("dxf"))
}
impl OpenCADStudio {
pub(super) fn on_block_palette(&mut self, m: BlockPaletteMsg) -> Task<Message> {
match m {
BlockPaletteMsg::Search(s) => self.block_palette.search = s,
BlockPaletteMsg::Tab(tab) => self.show_block_palette_tab(tab),
BlockPaletteMsg::View(v) => {
self.block_palette.view = v;
self.persist_settings_if_changed();
}
BlockPaletteMsg::Refresh => self.refresh_block_palette(),
BlockPaletteMsg::PickFile => {
return Task::perform(
async {
let handle = rfd::AsyncFileDialog::new()
.set_title(crate::t!("Select Drawing to Insert as Block").as_ref())
.add_filter(crate::t!("DWG/DXF Files").as_ref(), &["dwg", "dxf", "DWG", "DXF"])
.pick_file()
.await;
match handle {
Some(h) => Ok(crate::sys::handle_path(&h)),
None => Err("Cancelled".to_string()),
}
},
|r| Message::BlockPalette(BlockPaletteMsg::FilePicked(r)),
);
}
BlockPaletteMsg::FilePicked(Ok(path)) => return self.palette_insert(Item::File(path), None),
BlockPaletteMsg::FilePicked(Err(e)) | BlockPaletteMsg::LibraryPicked(Err(e)) => {
if e != "Cancelled" {
self.command_line.push_error(&e);
}
}
BlockPaletteMsg::Option(o) => self.on_block_option(o),
BlockPaletteMsg::ToggleOptions => {
self.block_palette.options_collapsed ^= true;
}
BlockPaletteMsg::LibraryBrowse => {
return Task::perform(
async {
match rfd::AsyncFileDialog::new()
.set_title(crate::t!("Select a Block Library Folder").as_ref())
.pick_folder()
.await
{
Some(h) => Ok(crate::sys::handle_path(&h)),
None => Err("Cancelled".to_string()),
}
},
|r| Message::BlockPalette(BlockPaletteMsg::LibraryPicked(r)),
);
}
BlockPaletteMsg::LibraryPicked(Ok(path)) => self.open_block_library(path),
BlockPaletteMsg::LibrarySelect(path) => self.open_block_library(PathBuf::from(path)),
BlockPaletteMsg::LibraryUp => {
if let Some(parent) = self.block_palette.library_dir.as_ref().and_then(|d| d.parent()) {
let parent = parent.to_path_buf();
self.list_block_library(parent);
}
}
BlockPaletteMsg::Press(item) => self.block_palette.pressed = Some(item),
BlockPaletteMsg::Release(item) => {
if self.block_palette.pressed.take().as_ref() == Some(&item) {
return match item {
Item::Folder(dir) => {
self.list_block_library(dir);
Task::none()
}
item => self.palette_insert(item, None),
};
}
}
BlockPaletteMsg::Menu(item, action) => return self.on_block_menu(item, action),
}
Task::none()
}
/// BLOCKSPALETTE: open (expanded) on `tab`, or on the tab it was left on.
pub(crate) fn open_blocks_palette(&mut self, tab: Option<Tab>) {
self.show_block_palette = true;
self.dock_expanded = Some(crate::ui::dock::PanelId::BlockPalette);
self.show_block_palette_tab(tab.unwrap_or(self.block_palette.tab));
}
fn show_block_palette_tab(&mut self, tab: Tab) {
self.block_palette.tab = tab;
if tab == Tab::Libraries && self.block_palette.library_dir.is_none() {
let start = Some(self.block_navigate.trim())
.filter(|p| !p.is_empty() && *p != "." && Path::new(p).is_dir())
.map(PathBuf::from)
.or_else(|| self.block_palette.libraries.first().map(PathBuf::from));
if let Some(dir) = start {
self.list_block_library(dir);
}
}
self.refresh_block_palette();
}
fn open_block_library(&mut self, root: PathBuf) {
let key = root.display().to_string();
let libraries = &mut self.block_palette.libraries;
libraries.retain(|l| l != &key);
libraries.insert(0, key);
libraries.truncate(MAX_LIBRARIES);
self.list_block_library(root);
self.persist_settings_if_changed();
}
/// Show `dir`: its sub-folders, then its drawings, each by name.
fn list_block_library(&mut self, dir: PathBuf) {
let mut entries: Vec<LibraryEntry> = std::fs::read_dir(&dir)
.into_iter()
.flatten()
.flatten()
.filter_map(|e| {
let path = e.path();
// Hidden folders (".name") hold tool data, not blocks.
if e.file_name().to_string_lossy().starts_with('.') {
return None;
}
if path.is_dir() {
Some(LibraryEntry { path, folder: true, thumb: None })
} else if is_drawing(&path) {
let thumb = crate::io::thumbnail::read_handle(&path);
Some(LibraryEntry { path, folder: false, thumb })
} else {
None
}
})
.collect();
entries.sort_by_key(|e| {
(!e.folder, e.path.file_name().map(|n| n.to_string_lossy().to_lowercase()))
});
self.block_palette.library_dir = Some(dir);
self.block_palette.library_entries = entries;
}
fn on_block_option(&mut self, o: OptionMsg) {
let p = &mut self.block_palette;
let number = |t: &str| t.trim().parse::<f64>().ok().filter(|v| v.is_finite());
match o {
OptionMsg::InsertionPoint(v) => p.options.insertion_point = v,
OptionMsg::Scale(v) => p.options.scale = v,
OptionMsg::Uniform(v) => p.options.uniform = v,
OptionMsg::Rotation(v) => p.options.rotation = v,
OptionMsg::AutoPlacement(v) => p.options.auto_placement = v,
OptionMsg::Repeat(v) => p.options.repeat = v,
OptionMsg::Explode(v) => p.options.explode = v,
OptionMsg::X(t) => {
if let Some(v) = number(&t).filter(|v| *v != 0.0) {
p.options.x = v;
}
p.option_text[0] = t;
}
OptionMsg::Y(t) => {
if let Some(v) = number(&t).filter(|v| *v != 0.0) {
p.options.y = v;
}
p.option_text[1] = t;
}
OptionMsg::Z(t) => {
if let Some(v) = number(&t).filter(|v| *v != 0.0) {
p.options.z = v;
}
p.option_text[2] = t;
}
OptionMsg::Angle(t) => {
if let Some(v) = number(&t) {
p.options.angle = v;
}
p.option_text[3] = t;
}
}
self.persist_settings_if_changed();
}
/// Start placing `name` as a palette click would.
#[cfg(test)]
pub(super) fn start_block_placement(&mut self, name: &str) {
let _ = self.start_palette_insert(name, None);
}
fn start_palette_insert(&mut self, name: &str, drop_at: Option<glam::DVec3>) -> Task<Message> {
use crate::command::CadCommand;
let i = self.active_tab;
let wires = self
.block_palette
.blocks
.iter()
.find(|b| b.name.eq_ignore_ascii_case(name))
.map(|b| b.wires.clone())
.unwrap_or_else(|| self.tabs[i].scene.block_preview_wires(name));
let mut options = self.block_palette.options.clone();
if drop_at.is_some() {
// A dropped block goes in where it lands with the fixed values.
options.insertion_point = false;
options.scale = false;
options.rotation = false;
options.repeat = false;
}
let mut cmd = InsertBlockCommand::palette(name.to_string(), wires, &options);
if let Some(point) = drop_at {
cmd.set_point(point);
}
self.reset_command_start_state(i);
let plane = if self.tabs[i].editing_model_space() {
self.tabs[i].ucs_xform().working_plane()
} else {
crate::command::WorkingPlane::default()
};
cmd.set_working_plane(plane);
let first = cmd.initial();
let prompt = cmd.prompt();
self.tabs[i].active_cmd = Some(Box::new(cmd));
self.block_palette.placing = Some(name.to_string());
match first {
Some(result) => self.apply_cmd_result(result),
None => {
self.command_line.push_info(&prompt);
Task::none()
}
}
}
/// Insert `item` (a click, or a drop at `drop_at`), bringing its block
/// into the drawing first when it lives elsewhere.
pub(crate) fn palette_insert(&mut self, item: Item, drop_at: Option<glam::DVec3>) -> Task<Message> {
match self.resolve_palette_block(&item) {
Ok(name) => {
self.refresh_block_palette();
self.start_palette_insert(&name, drop_at)
}
Err(e) => {
if !e.is_empty() {
self.command_line.push_error(&e);
}
Task::none()
}
}
}
fn block_exists(&self, name: &str) -> bool {
self.tabs[self.active_tab].scene.document.block_records.get(name).is_some()
}
fn current_drawing_path(&self) -> String {
self.tabs[self.active_tab]
.current_path
.as_ref()
.map(|p| p.display().to_string())
.unwrap_or_default()
}
fn resolve_palette_block(&mut self, item: &Item) -> Result<String, String> {
match item {
Item::Current(name) => Ok(name.clone()),
Item::Ref(r) if r.drawing => self.block_from_drawing(PathBuf::from(&r.source)),
Item::Ref(r) => {
let elsewhere = !r.source.is_empty() && r.source != self.current_drawing_path();
if self.block_exists(&r.name) && !(elsewhere && self.block_redefine_mode == 2) {
return Ok(r.name.clone());
}
if !elsewhere {
return Err(crate::tf!("Block \"{}\" not found.", r.name).into_owned());
}
let doc = crate::io::load_file(Path::new(&r.source)).map_err(|e| e.to_string())?;
let redefine = self.block_exists(&r.name);
self.import_block_definition(&doc, &r.name, redefine)?;
Ok(r.name.clone())
}
Item::File(path) => self.block_from_drawing(path.clone()),
Item::Folder(_) => Err(String::new()),
}
}
/// The block standing for drawing `path`: the drawing's own block when
/// the drawing already has one of that name (redefined from the file
/// under BLOCKREDEFINEMODE 2), else the file inserted as a new block.
fn block_from_drawing(&mut self, path: PathBuf) -> Result<String, String> {
let stem = file_stem(&path);
let name = if self.block_exists(&stem) && self.block_redefine_mode != 2 {
stem
} else {
self.import_drawing_block(path.clone(), true)?
};
self.block_palette
.file_sources
.insert(name.to_ascii_uppercase(), path.display().to_string());
Ok(name)
}
/// Bring block `name` (and what it needs) from `source` into the active
/// drawing; with `redefine` an existing definition takes the source's.
fn import_block_definition(
&mut self,
source: &codec::CadDocument,
name: &str,
redefine: bool,
) -> Result<(), String> {
if source.block_records.get(name).is_none() {
return Err(crate::tf!("Block \"{}\" not found.", name).into_owned());
}
let probe = codec::EntityType::Insert(codec::entities::Insert::new(
name.to_string(),
codec::types::Vector3::ZERO,
));
let deps = crate::app::ClipboardDeps::capture(source, std::slice::from_ref(&probe));
let i = self.active_tab;
self.push_undo_snapshot(i, "INSERT");
self.merge_dependencies(i, &deps);
for def in deps.blocks {
let scene = &mut self.tabs[i].scene;
if redefine && def.name.eq_ignore_ascii_case(name) {
scene.redefine_block_raw(&def.name, def.base_point, def.entities);
} else if scene.document.block_records.get(&def.name).is_none() {
scene.define_block_raw(&def.name, def.base_point, def.entities);
}
}
self.tabs[i].scene.populate_meshes_from_document();
self.tabs[i].dirty = true;
Ok(())
}
fn palette_ref(&self, item: &Item) -> Option<PaletteBlockRef> {
let time = now_secs();
match item {
Item::Current(name) => Some(PaletteBlockRef {
name: name.clone(),
source: self.current_drawing_path(),
time,
drawing: false,
}),
Item::Ref(r) => Some(r.clone()),
Item::File(path) => Some(PaletteBlockRef {
name: file_stem(path),
source: path.display().to_string(),
time,
drawing: true,
}),
Item::Folder(_) => None,
}
}
fn on_block_menu(&mut self, item: Item, action: MenuAction) -> Task<Message> {
let same = |a: &PaletteBlockRef, b: &PaletteBlockRef| {
a.name.eq_ignore_ascii_case(&b.name) && a.source == b.source
};
match action {
MenuAction::Insert => return self.palette_insert(item, None),
MenuAction::Redefine => {
let result = match &item {
Item::Ref(r) if r.drawing => self.import_drawing_block(PathBuf::from(&r.source), true),
Item::File(path) => self.import_drawing_block(path.clone(), true),
Item::Ref(r) if !r.source.is_empty() => crate::io::load_file(Path::new(&r.source))
.map_err(|e| e.to_string())
.and_then(|doc| self.import_block_definition(&doc, &r.name, true))
.map(|_| r.name.clone()),
_ => return Task::none(),
};
match result {
Ok(name) => {
self.command_line
.push_output(&crate::tf!("Block \"{name}\" redefined."));
self.refresh_block_palette();
}
Err(e) => self.command_line.push_error(&e),
}
}
MenuAction::Favorite => {
if let Some(r) = self.palette_ref(&item) {
let favorites = &mut self.block_palette.favorites;
favorites.retain(|f| !same(f, &r));
favorites.insert(0, r);
self.persist_settings_if_changed();
self.refresh_block_palette();
}
}
MenuAction::Unfavorite => {
if let Some(r) = self.palette_ref(&item) {
self.block_palette.favorites.retain(|f| !same(f, &r));
self.persist_settings_if_changed();
}
}
MenuAction::Edit => {
let name = item.name();
let i = self.active_tab;
let reference = self.tabs[i].scene.document.entities().find_map(|e| match e {
codec::EntityType::Insert(ins) if ins.block_name.eq_ignore_ascii_case(&name) => {
Some(ins.common.handle)
}
_ => None,
});
match reference {
Some(handle) => return self.dispatch_command(&format!("BEDIT_BEGIN:{}", handle.value())),
None => self.command_line.push_error(&crate::tf!(
"Block \"{name}\" has no reference in the drawing to edit."
)),
}
}
MenuAction::Remove => match self.block_palette.tab {
Tab::Current => {
let name = item.name();
let i = self.active_tab;
let referenced = self.tabs[i].scene.document.entities().any(|e| {
matches!(e, codec::EntityType::Insert(ins) if ins.block_name.eq_ignore_ascii_case(&name))
});
if referenced {
self.command_line.push_error(&crate::tf!(
"Block \"{name}\" is referenced in the drawing and cannot be removed."
));
} else {
self.push_undo_snapshot(i, "PURGE");
crate::app::control::erase_block_definition(&mut self.tabs[i].scene, &name);
self.tabs[i].dirty = true;
self.command_line.push_output(&crate::tf!("Block \"{name}\" removed."));
self.refresh_block_palette();
}
}
Tab::Recent | Tab::Favorites => {
if let Some(r) = self.palette_ref(&item) {
let list = if self.block_palette.tab == Tab::Recent {
&mut self.block_palette.recent
} else {
&mut self.block_palette.favorites
};
list.retain(|f| !same(f, &r));
self.persist_settings_if_changed();
}
}
Tab::Libraries => {}
},
}
Task::none()
}
/// A press in the drawing ends any palette press; a release there with
/// a block still pressed drops it at the cursor.
pub(super) fn take_block_drop(&mut self) -> Option<Item> {
self.block_palette.pressed.take()
}
pub(super) fn drop_block(&mut self, item: Item) -> Task<Message> {
let at = self.tabs[self.active_tab].last_cursor_world;
self.palette_insert(item, Some(at))
}
/// Put `name` at the top of the Recent list (BLOCKMRULIST long).
pub(crate) fn note_recent_block(&mut self, name: &str) {
let (source, drawing) = match self.block_palette.file_sources.get(&name.to_ascii_uppercase()) {
Some(path) => (path.clone(), true),
None => (self.current_drawing_path(), false),
};
let entry = PaletteBlockRef {
name: if drawing { file_stem(Path::new(&source)) } else { name.to_string() },
source,
time: now_secs(),
drawing,
};
let recent = &mut self.block_palette.recent;
recent.retain(|r| !(r.name.eq_ignore_ascii_case(&entry.name) && r.source == entry.source));
recent.insert(0, entry);
self.trim_recent_blocks();
}
pub(crate) fn trim_recent_blocks(&mut self) {
self.block_palette.recent.truncate(self.block_mru_list as usize);
}
/// Rebuild the Current Drawing list, and the previews kept for blocks
/// of other drawings and for whole drawings.
pub(crate) fn refresh_block_palette(&mut self) {
let i = self.active_tab;
let names = self.tabs[i].scene.custom_block_names();
self.block_palette.cached_names = names.clone();
self.block_palette.source_tab_id = Some(self.tabs[i].id);
self.block_palette.source_block_epoch = self.tabs[i].scene.block_epoch;
let doc = &self.tabs[i].scene.document;
let annotative = |name: &str| {
doc.block_records.get(name).is_some_and(|br| {
matches!(
doc.get_entity(br.block_entity_handle),
Some(codec::EntityType::Block(b)) if b.common.extended_data.records().iter().any(|r| r.application_name == "AcadAnnotative")
)
})
};
let entries: Vec<BlockEntry> = names
.into_iter()
.map(|name| BlockEntry {
wires: self.tabs[i].scene.block_preview_wires(&name),
annotative: annotative(&name),
dynamic: false,
name,
})
.collect();
let here = self.current_drawing_path();
for entry in &entries {
let key = format!("{here}|{}", entry.name.to_ascii_uppercase());
self.block_palette.ref_wires.insert(key, entry.wires.clone());
}
self.block_palette.blocks = entries;
let drawings: Vec<String> = self
.block_palette
.recent
.iter()
.chain(&self.block_palette.favorites)
.filter(|r| r.drawing && !self.block_palette.file_thumbs.contains_key(&r.source))
.map(|r| r.source.clone())
.collect();
for source in drawings {
let thumb = crate::io::thumbnail::read_handle(Path::new(&source));
self.block_palette.file_thumbs.insert(source, thumb);
}
}
/// Cheap per-update check: rebuild when the active drawing's definitions
/// changed, even when their names happen to stay the same.
pub(crate) fn refresh_block_palette_if_stale(&mut self) {
if !self.show_block_palette {
return;
}
let i = self.active_tab;
if self.block_palette.source_tab_id != Some(self.tabs[i].id)
|| self.block_palette.source_block_epoch != self.tabs[i].scene.block_epoch
{
self.refresh_block_palette();
}
}
/// What `-INSERT`'s name step knows about the drawing's blocks.
pub(crate) fn block_catalog(&self) -> BlockCatalog {
let doc = &self.tabs[self.active_tab].scene.document;
let host = doc.header.insertion_units;
let mut user = Vec::new();
let mut unnamed = 0;
let mut units = rustc_hash::FxHashMap::default();
for br in doc.block_records.iter() {
if br.flags.is_xref || br.name.eq_ignore_ascii_case("*Model_Space") || br.name.to_ascii_uppercase().starts_with("*PAPER_SPACE") {
continue;
}
if br.name.starts_with('*') {
unnamed += 1;
continue;
}
let factor = crate::app::properties::insert_unit_scale(host, br.units).unwrap_or(1.0);
units.insert(
br.name.to_ascii_uppercase(),
format!(
"Units: {} Conversion:{:>10}",
crate::app::properties::insunits_name(br.units),
crate::app::properties::format_unit_factor(factor)
),
);
user.push(br.name.clone());
}
user.sort_by_key(|n| n.to_ascii_uppercase());
let search_path = std::env::current_dir()
.map(|d| d.display().to_string())
.unwrap_or_default();
BlockCatalog { user, unnamed, units, search_path }
}
}
+41 -119
View File
@@ -2,7 +2,6 @@
#![allow(unused_imports)]
use super::util::*;
use crate::ui::window::block_palette::BlockPaletteMsg;
use super::{format_size, VIEWCUBE_HIT_SIZE};
use crate::app::helpers::{
parse_coord, polar_constrain_near, ucs_rotate_vec, ucs_to_wcs, ucs_z_axis,
@@ -373,21 +372,41 @@ pub(super) fn on_ribbon_tool_click(&mut self, tool_id: String, event: ModuleEven
/// one new block in the active drawing. Returns the new block's name, or an
/// error message. Nested block definitions are imported first so nested
/// INSERTs render (AutoCAD's "inserting a drawing imports its block defs").
fn import_file_as_block(&mut self, path: std::path::PathBuf) -> Result<String, String> {
pub(in crate::app) fn import_file_as_block(&mut self, path: std::path::PathBuf) -> Result<String, String> {
self.import_drawing_block(path, false)
}
/// [`Self::import_file_as_block`]; with `redefine` a block already named
/// after the file takes the file's contents instead of a new name.
pub(super) fn import_drawing_block(
&mut self,
path: std::path::PathBuf,
redefine: bool,
) -> Result<String, String> {
let doc = crate::io::load_file(&path).map_err(|e| e.to_string())?;
let stem = path
.file_stem()
.map(|s| s.to_string_lossy().into_owned())
.unwrap_or_else(|| "Block".to_string());
self.import_document_as_block(doc, stem)
self.import_document_block(doc, stem, redefine)
}
/// Define one block in the active drawing from a loaded `CadDocument`'s
/// model-space entities (base = the file's model-space insertion base).
#[cfg(test)]
fn import_document_as_block(
&mut self,
doc: codec::CadDocument,
stem: String,
) -> Result<String, String> {
self.import_document_block(doc, stem, false)
}
fn import_document_block(
&mut self,
doc: codec::CadDocument,
stem: String,
redefine: bool,
) -> Result<String, String> {
let i = self.active_tab;
// Model-space block record handle (Layout object first, name fallback).
@@ -441,7 +460,13 @@ pub(super) fn on_ribbon_tool_click(&mut self, tool_id: String, event: ModuleEven
doc.header.model_space_insertion_base.y,
doc.header.model_space_insertion_base.z,
);
let name = self.block_name_from_file(&stem);
let redefine = redefine
&& self.tabs[i].scene.document.block_records.get(stem.trim()).is_some();
let name = if redefine {
stem.trim().to_string()
} else {
self.block_name_from_file(&stem)
};
// Capture every table record needed by the top-level entities and their
// nested block definitions. Importing only the definitions leaves
// source-only layers, linetypes, and text/dimension styles dangling.
@@ -513,74 +538,22 @@ pub(super) fn on_ribbon_tool_click(&mut self, tool_id: String, event: ModuleEven
.scene
.define_block_raw(&def.name, def.base_point, def.entities);
}
self.tabs[i]
.scene
.define_block_from_owned_entities(entities, &name, base)?;
if redefine {
self.tabs[i].scene.redefine_block_raw(
&name,
codec::types::Vector3::new(base.x, base.y, base.z),
entities,
);
} else {
self.tabs[i]
.scene
.define_block_from_owned_entities(entities, &name, base)?;
}
self.tabs[i].scene.populate_meshes_from_document();
self.tabs[i].dirty = true;
Ok(name)
}
pub(super) fn on_block_palette(&mut self, m: crate::ui::window::block_palette::BlockPaletteMsg) -> iced::Task<Message> {
use crate::ui::window::block_palette::{BlockEntry, BlockPaletteMsg};
match m {
BlockPaletteMsg::Search(s) => {
self.block_palette.search = s;
iced::Task::none()
}
BlockPaletteMsg::CyclePreviewSize => {
self.block_palette.preview_size =
crate::ui::window::block_palette::cycle_preview_size(
self.block_palette.preview_size,
);
iced::Task::none()
}
BlockPaletteMsg::Refresh => {
self.refresh_block_palette();
iced::Task::none()
}
BlockPaletteMsg::PickFile => iced::Task::perform(
async {
let handle = rfd::AsyncFileDialog::new()
.set_title(crate::t!("Select Drawing to Insert as Block").as_ref())
.add_filter(crate::t!("DWG/DXF Files").as_ref(), &["dwg", "dxf", "DWG", "DXF"])
.pick_file()
.await;
match handle {
Some(h) => Ok(crate::sys::handle_path(&h)),
None => Err("Cancelled".to_string()),
}
},
|r| Message::BlockPalette(BlockPaletteMsg::FilePicked(r)),
),
BlockPaletteMsg::FilePicked(Ok(path)) => {
match self.import_file_as_block(path) {
Ok(name) => {
self.command_line
.push_output(&crate::tf!("Inserting \"{name}\" from file."));
self.refresh_block_palette();
self.start_block_placement(&name);
}
Err(e) if e != "Cancelled" => {
self.command_line.push_error(&crate::tf!("INSERT FILE: {e}"));
}
Err(_) => {}
}
iced::Task::none()
}
BlockPaletteMsg::FilePicked(Err(e)) => {
if e != "Cancelled" {
self.command_line.push_error(&e);
}
iced::Task::none()
}
BlockPaletteMsg::Insert(name) => {
self.start_block_placement(&name);
iced::Task::none()
}
}
}
/// Dock chrome interaction (grab / pin / resize / hover / move) applied to
/// whichever panel the message names.
pub(super) fn on_dock(&mut self, m: crate::ui::dock::DockMsg) -> iced::Task<Message> {
@@ -754,57 +727,6 @@ pub(super) fn on_ribbon_tool_click(&mut self, tool_id: String, event: ModuleEven
.count()
}
/// Start placing `name` through the INSERT command, skipping the name prompt.
fn start_block_placement(&mut self, name: &str) {
let i = self.active_tab;
let wires = self
.block_palette
.blocks
.iter()
.find(|b| b.name == name)
.map(|b| b.wires.clone())
.unwrap_or_else(|| self.tabs[i].scene.block_preview_wires(name));
use crate::modules::insert::insert_block::InsertBlockCommand;
let cmd = InsertBlockCommand::new_for_block(name.to_string(), wires, glam::Vec3::ZERO);
use crate::command::CadCommand;
self.reset_command_start_state(i);
self.command_line.push_info(&cmd.prompt());
self.tabs[i].active_cmd = Some(Box::new(cmd));
self.block_palette.placing = Some(name.to_string());
}
/// Rebuild the panel's block list + cached wires from the active drawing.
pub(crate) fn refresh_block_palette(&mut self) {
let i = self.active_tab;
let names = self.tabs[i].scene.custom_block_names();
self.block_palette.cached_names = names.clone();
self.block_palette.source_tab_id = Some(self.tabs[i].id);
self.block_palette.source_block_epoch = self.tabs[i].scene.block_epoch;
self.block_palette.blocks = names
.into_iter()
.map(|name| {
let wires = self.tabs[i].scene.block_preview_wires(&name);
crate::ui::window::block_palette::BlockEntry { name, wires }
})
.collect();
}
/// Cheap per-update check: rebuild when the active drawing's definitions
/// changed, even when their names happen to stay the same.
pub(crate) fn refresh_block_palette_if_stale(&mut self) {
if !self.show_block_palette {
return;
}
let i = self.active_tab;
// `block_epoch` advances for every block-definition change, so it
// avoids re-scanning every block name on unrelated application updates.
if self.block_palette.source_tab_id != Some(self.tabs[i].id)
|| self.block_palette.source_block_epoch != self.tabs[i].scene.block_epoch
{
self.refresh_block_palette();
}
}
/// Rebuild the Reference Manager's entry list from the active drawing.
/// Uses the tab's session sets (unloaded + stat baseline) so CLI and
/// palette agree; writes fresh load-time mtimes back into the tab's stat
@@ -1626,7 +1548,7 @@ mod tests {
assert!(app.block_palette.blocks.iter().any(|b| b.name == "Fixture"));
app.start_block_placement(&name);
let cmd = app.tabs[app.active_tab].active_cmd.as_ref().expect("INSERT running");
assert_eq!(cmd.name(), "INSERT");
assert_eq!(cmd.name(), "-INSERT");
assert_eq!(app.block_palette.placing.as_deref(), Some("Fixture"));
}
+19
View File
@@ -734,6 +734,14 @@ impl OpenCADStudio {
grid_beyond_limits: self.grid_beyond_limits,
block_mru: self.block_mru.clone(),
block_freq: self.block_freq.clone(),
block_recent: self.block_palette.recent.clone(),
block_favorites: self.block_palette.favorites.clone(),
block_libraries: self.block_palette.libraries.clone(),
block_palette_view: self.block_palette.view as u8,
block_insert: self.block_palette.options.clone(),
block_mru_list: self.block_mru_list,
block_redefine_mode: self.block_redefine_mode,
block_navigate: self.block_navigate.clone(),
}
}
@@ -854,6 +862,15 @@ impl OpenCADStudio {
.take(200)
.map(|(k, v)| (k.clone(), *v))
.collect();
self.block_mru_list = s.block_mru_list.min(100);
self.block_redefine_mode = s.block_redefine_mode.min(2);
self.block_navigate = s.block_navigate.clone();
self.block_palette.recent = s.block_recent.clone();
self.block_palette.favorites = s.block_favorites.clone();
self.block_palette.libraries = s.block_libraries.clone();
self.block_palette.view = crate::ui::window::block_palette::ViewMode::from_u8(s.block_palette_view);
self.block_palette.set_options(s.block_insert.clone());
self.trim_recent_blocks();
// Push restored display defaults onto every drawing tab that exists now.
// Tabs created later pick them up at their construction site.
for idx in 0..self.tabs.len() {
@@ -863,6 +880,8 @@ impl OpenCADStudio {
}
pub(crate) fn record_block_insert(&mut self, name: &str) {
self.insname = name.to_string();
self.note_recent_block(name);
let key = name.to_ascii_uppercase();
*self.block_freq.entry(key).or_insert(0) += 1;
self.block_mru.retain(|n| !n.eq_ignore_ascii_case(name));
+4
View File
@@ -92,6 +92,7 @@ fn reorder_insertion_index(from: usize, to: usize, after: bool, len: usize) -> O
mod command;
mod context_menu;
mod blocks_palette;
mod dialog;
mod dynamic;
mod file;
@@ -5768,6 +5769,9 @@ impl OpenCADStudio {
if self.tabs[self.active_tab].is_start {
self.ribbon.close_dropdown();
} else {
if id == crate::modules::insert::insert_block::GALLERY_ID {
self.refresh_block_palette();
}
self.ribbon.toggle_dropdown(&id);
}
Task::none()
+5
View File
@@ -3356,6 +3356,7 @@ impl OpenCADStudio {
pub(super) fn on_viewport_left_press(&mut self) -> Task<Message> {
let i = self.active_tab;
self.block_palette.pressed = None;
if let Some(command) = self.tabs[i].active_cmd.as_mut() {
command.set_ctrl(self.ctrl_down);
command.set_shift(self.shift_down);
@@ -3724,6 +3725,10 @@ impl OpenCADStudio {
pub(super) fn on_viewport_left_release(&mut self) -> Task<Message> {
let i = self.active_tab;
// A block dragged from the Blocks palette lands here.
if let Some(item) = self.take_block_drop() {
return self.drop_block(item);
}
if let Some(command) = self.tabs[i].active_cmd.as_mut() {
command.set_ctrl(self.ctrl_down);
command.set_shift(self.shift_down);
+13 -2
View File
@@ -2316,7 +2316,17 @@ bg={bg_ms:.1}ms n={view_count}"
} else {
Vec::new()
};
let dropdown_layer: Element<'_, Message> = self
let gallery_open = open_dropdown == Some(crate::modules::insert::insert_block::GALLERY_ID)
&& !self.tabs[self.active_tab].is_start;
let dropdown_layer: Element<'_, Message> = if gallery_open {
self.ribbon.place_dropdown(
crate::modules::insert::insert_block::GALLERY_ID,
crate::ui::window::block_palette::gallery(&self.block_palette),
crate::ui::window::block_palette::GALLERY_W,
self.win_size.0,
)
} else {
self
.ribbon
.dropdown_overlay(
&undo_labels,
@@ -2325,7 +2335,8 @@ bg={bg_ms:.1}ms n={view_count}"
self.tabs[self.active_tab].is_start,
&self.recent_colors,
)
.unwrap_or_else(|| iced::widget::Space::new().width(0).height(0).into());
.unwrap_or_else(|| iced::widget::Space::new().width(0).height(0).into())
};
let snap_override_layer: Element<'_, Message> = if let Some(pos) = self.snap_override_popup
{
+12
View File
@@ -1888,6 +1888,12 @@ pub enum CmdResult {
ReportError(String),
/// Reports an error and ends the command (`Lines are parallel.`).
CancelWithMessage(String),
/// Explode this block reference and commit its pieces; end the command
/// unless `keep_going` (repeated placement).
CommitExplodedInsert {
insert: EntityType,
keep_going: bool,
},
/// Print a measurement result, clear the current selection, and keep the command active.
ReportMeasurementAndDeselect(String),
/// Clear the current selection and keep the command active at its updated step.
@@ -2926,6 +2932,12 @@ pub trait CadCommand: Send {
None
}
/// After the host commits an ATTREQ insert: keep the command running
/// (repeated placement) instead of ending it.
fn attreq_continue(&self) -> bool {
false
}
/// Returns the INSERT entity built so far (pending attr fill) if this is an
/// ATTREQ-aware INSERT command waiting for attdef injection.
/// Called by the host after `AttreqNeeded` to commit the completed Insert.
+34
View File
@@ -163,6 +163,40 @@ pub(super) fn message_attribute(source: &str) -> Option<(&'static str, &'static
"Requires a positive number." => Some(("insert", "pcs-positive")),
"Requires a positive integer." => Some(("insert", "pcs-positive-int")),
"Traces the points on the kept side of a live section as lines." => Some(("insert", "pcs-help")),
"Add to Favorites" => Some(("insert", "bp-add-favorite")),
"Annotative / Dynamic" => Some(("insert", "bp-annotative-dynamic")),
"Auto-Placement" => Some(("insert", "bp-auto-placement")),
"Blocks from Libraries..." => Some(("insert", "bp-gallery-libraries")),
"Choose a library folder with …" => Some(("insert", "bp-choose-library")),
"Current Drawing" => Some(("insert", "bp-tab-current")),
"Current Drawing Blocks" => Some(("insert", "bp-heading-current")),
"Extra Large Icons" => Some(("insert", "bp-view-xl")),
"Favorite Blocks" => Some(("insert", "bp-heading-favorites")),
"Favorite Blocks..." => Some(("insert", "bp-gallery-favorites")),
"Favorites" => Some(("insert", "bp-tab-favorites")),
"Filter... (* ? wildcards)" => Some(("insert", "bp-filter")),
"Icon" => Some(("insert", "bp-icon")),
"Insertion Point" => Some(("insert", "bp-insertion-point")),
"Large Icons" => Some(("insert", "bp-view-large")),
"Libraries" => Some(("insert", "bp-tab-libraries")),
"Library Blocks" => Some(("insert", "bp-heading-libraries")),
"Medium Icons" => Some(("insert", "bp-view-medium")),
"Recent" => Some(("insert", "bp-tab-recent")),
"Recent Blocks" => Some(("insert", "bp-heading-recent")),
"Recent Blocks..." => Some(("insert", "bp-gallery-recent")),
"Redefine" => Some(("insert", "bp-redefine")),
"Remove Block" => Some(("insert", "bp-remove-block")),
"Remove from Favorites" => Some(("insert", "bp-remove-favorite")),
"Remove from List" => Some(("insert", "bp-remove-list")),
"Repeat Placement" => Some(("insert", "bp-repeat-placement")),
"Select a Block Library Folder" => Some(("insert", "bp-select-library")),
"Small Icons" => Some(("insert", "bp-view-small")),
"specified on screen" => Some(("insert", "bp-on-screen")),
"Block \"{}\" not found." => Some(("insert", "bp-block-not-found")),
"Block \"{name}\" redefined." => Some(("insert", "bp-block-redefined")),
"Block \"{name}\" has no reference in the drawing to edit." => Some(("insert", "bp-block-no-reference")),
"Block \"{name}\" is referenced in the drawing and cannot be removed." => Some(("insert", "bp-block-referenced")),
"Block \"{name}\" removed." => Some(("insert", "bp-block-removed")),
"Stylization" => Some(("properties", "stylization")),
"Color scheme" => Some(("properties", "color-scheme")),
"Scan Colors" => Some(("properties", "scan-colors")),
+1 -1
View File
@@ -202,7 +202,7 @@ impl CadModule for DrawModule {
title: "Block",
tools: vec![
RibbonItem::LargeTool(create_block::tool()),
RibbonItem::LargeTool(insert_block::tool()),
insert_block::gallery(),
],
},
RibbonGroup {
File diff suppressed because it is too large. Load diff
+1 -1
View File
@@ -105,7 +105,7 @@ impl CadModule for InsertModule {
title: "Block",
tools: vec![
RibbonItem::LargeTool(mview_block::tool()),
RibbonItem::LargeTool(insert_block::tool()),
insert_block::gallery(),
RibbonItem::Tool(create_block::tool()),
RibbonItem::Tool(edit_block::tool()),
RibbonItem::Tool(base_point::tool()),
+40
View File
@@ -1234,6 +1234,46 @@ impl Scene {
self.bump_geometry();
}
/// Replace the contents and base point of block `name`, defining it when
/// absent. References keep pointing at it and show the new contents.
pub fn redefine_block_raw(
&mut self,
name: &str,
base_point: codec::types::Vector3,
entities: Vec<EntityType>,
) {
let Some((br_handle, block_handle)) = self
.document
.block_records
.get(name)
.map(|br| (br.handle, br.block_entity_handle))
else {
return self.define_block_raw(name, base_point, entities);
};
let owned: Vec<Handle> = self
.document
.entities()
.filter(|e| {
e.common().owner_handle == br_handle
&& !matches!(e, EntityType::Block(_) | EntityType::BlockEnd(_))
})
.map(|e| e.common().handle)
.collect();
for h in owned {
self.document.remove_entity(h);
}
if let Some(EntityType::Block(block)) = self.document.get_entity_mut(block_handle) {
block.base_point = base_point;
}
for mut entity in entities {
Self::reset_clone_subhandles(&mut self.document, &mut entity);
entity.common_mut().handle = Handle::NULL;
entity.common_mut().owner_handle = br_handle;
let _ = self.document.add_entity(entity);
}
self.bump_geometry();
}
pub(super) fn synced_hatch_models(
&self,
target_block: Handle,
+14 -1
View File
@@ -20,7 +20,7 @@ use crate::modules::{CadModule, IconKind, RibbonGroup, RibbonItem};
use crate::plugin::all_ribbon_modules;
use crate::ui::properties::{linetype_display_name, lw_options, LinetypeItem};
mod widgets;
pub(crate) mod widgets;
mod draw_panel;
pub(crate) use draw_panel::tools as panel_tools;
mod modify_panel;
@@ -745,6 +745,19 @@ impl Ribbon {
// ── Dropdown overlay ──────────────────────────────────────────────────
/// Place `panel` (`w` wide) under dropdown `id`, closing on a click
/// outside it.
pub fn place_dropdown<'a>(
&self,
id: &str,
panel: Element<'a, Message>,
w: f32,
win_w: f32,
) -> Element<'a, Message> {
let (align_right, h_pad, top) = self.dd_anchor(id, w, win_w);
dropdown_backdrop(position_ribbon_dropdown(panel, align_right, h_pad, top))
}
pub fn dropdown_overlay(
&self,
undo_labels: &[String],
+2 -2
View File
@@ -446,7 +446,7 @@ pub(super) fn combo_btn_style(
}
}
pub(super) fn popup_row_style(theme: &Theme, status: button::Status) -> button::Style {
pub(crate) fn popup_row_style(theme: &Theme, status: button::Status) -> button::Style {
let palette = theme.palette();
let pair = if matches!(status, button::Status::Hovered | button::Status::Pressed) {
palette.background.weak
@@ -460,7 +460,7 @@ pub(super) fn popup_row_style(theme: &Theme, status: button::Status) -> button::
}
}
pub(super) fn popup_panel_style(theme: &Theme) -> container::Style {
pub(crate) fn popup_panel_style(theme: &Theme) -> container::Style {
let palette = theme.palette();
container::Style {
background: Some(Background::Color(palette.background.base.color)),
File diff suppressed because it is too large. Load diff
+3 -3
View File
@@ -233,13 +233,13 @@ pub fn click_page(selected: &mut Vec<usize>, anchor: &mut usize, page: usize, ct
// accent-coloured card titles, segmented choices and toggle chips, with the
// help button on the left of the footer and the named action on the right.
fn accent_text(theme: &Theme) -> text::Style {
pub(crate) fn accent_text(theme: &Theme) -> text::Style {
text::Style {
color: Some(theme.palette().primary.base.color),
}
}
fn card_style(theme: &Theme) -> container::Style {
pub(crate) fn card_style(theme: &Theme) -> container::Style {
container::Style {
background: Some(Background::Color(theme.palette().background.weak.color)),
border: Border {
@@ -251,7 +251,7 @@ fn card_style(theme: &Theme) -> container::Style {
}
}
fn well_style(theme: &Theme) -> container::Style {
pub(crate) fn well_style(theme: &Theme) -> container::Style {
container::Style {
background: Some(Background::Color(theme.palette().background.base.color)),
border: Border {