feat(styles): overhaul style managers

Unify editor layouts, live previews, comparisons, read-only behavior, and localized controls across all named style managers.

Refs #629
This commit is contained in:
Hakan Seven 2026-08-03 02:01:47 +03:00
commit 25515fa33c
27 changed files with 5480 additions and 1636 deletions

View file

@ -3193,3 +3193,185 @@ catalog-mod-shortcuts-4c54bbb78e = ── __ocs_arg_MOD__-Tastenkürzel ──
catalog-custom-overrides-shortcuts-set-07be563dec = ── Benutzerdefinierte Überschreibungen (SHORTCUTS SET) catalog-custom-overrides-shortcuts-set-07be563dec = ── Benutzerdefinierte Überschreibungen (SHORTCUTS SET)
catalog-function-keys-b5a2df24e9 = ── Funktionstasten ──────────────────────────────────── catalog-function-keys-b5a2df24e9 = ── Funktionstasten ────────────────────────────────────
catalog-other-keys-a882812cf2 = ── Weitere Tasten ───────────────────────────────────── catalog-other-keys-a882812cf2 = ── Weitere Tasten ─────────────────────────────────────
dimstyle-keep-text-between-extension-lines = Text immer zwischen Hilfslinien halten
dimstyle-angular-dimensions = Winkelbemaßungen
dimstyle-arc-length-symbol = Bogenlängensymbol
dimstyle-arrowheads = Pfeilspitzen
dimstyle-available-style = Verfügbarer Stil
dimstyle-baseline-spacing = Basislinienabstand
dimstyle-basic = Theoretisch genau
dimstyle-both-arrowheads = Beide Pfeilspitzen
dimstyle-center-mark = Mittelpunktmarkierung
dimstyle-center-mark-size = Größe der Mittelpunktmarkierung
dimstyle-center-marks = Mittelpunktmarkierungen
dimstyle-centerlines = Mittellinien
dimstyle-compare-with = Vergleichen mit
dimstyle-current-style = Aktueller Stil
dimstyle-deviation = Abweichung
dimstyle-different-sections = Abweichende Bereiche
dimstyle-dimension-line-color = Maßlinienfarbe
dimstyle-dimension-linetype = Maßlinientyp
dimstyle-dimension-lineweight = Maßlinienstärke
dimstyle-display-alternate-units = Alternative Einheiten anzeigen
dimstyle-draw-dimension-line-between-extension-lines = Maßlinie zwischen Hilfslinien zeichnen
dimstyle-extend-beyond-dimension-line = Über Maßlinie hinaus verlängern
dimstyle-extend-beyond-ticks = Über Schrägstriche hinaus verlängern
dimstyle-extension-line-color = Hilfslinienfarbe
dimstyle-extension-lineweight = Hilfslinienstärke
dimstyle-feet-and-inches = Fuß und Zoll
dimstyle-fine-tuning = Feinabstimmung
dimstyle-first-arrowhead = Erste Pfeilspitze
dimstyle-first-extension-linetype = Linientyp der ersten Hilfslinie
dimstyle-fit-options = Einpassungsoptionen
dimstyle-fixed-length = Feste Länge
dimstyle-fraction-format = Bruchformat
dimstyle-height-scale = Höhenskalierung
dimstyle-horizontal-placement = Horizontale Platzierung
dimstyle-keep-text-horizontal-inside = Text innen horizontal halten
dimstyle-keep-text-horizontal-outside = Text außen horizontal halten
dimstyle-leader-arrowhead = Führungslinien-Pfeilspitze
dimstyle-limits = Grenzmaße
dimstyle-linear-dimensions = Lineare Bemaßungen
dimstyle-lower-value = Unterer Wert
dimstyle-measurement-scale = Maßstab der Messwerte
dimstyle-measurement-template = Messwertvorlage
dimstyle-method = Methode
dimstyle-multiplier = Multiplikator
dimstyle-no-differences = Keine Unterschiede
dimstyle-not-applicable = Nicht zutreffend
dimstyle-offset-from-measured-point = Abstand vom gemessenen Punkt
dimstyle-place-text-manually = Text beim Erstellen der Bemaßung manuell platzieren
dimstyle-radius-jog-angle = Winkel des Radiusknicks (°)
dimstyle-read-left-to-right = Von links nach rechts lesen
dimstyle-referenced-read-only = Referenzierter Stil · Schreibgeschützt
dimstyle-round-off = Rundung
dimstyle-second-arrowhead = Zweite Pfeilspitze
dimstyle-second-extension-linetype = Linientyp der zweiten Hilfslinie
dimstyle-show-zero-feet-and-inches = Null Fuß und null Zoll anzeigen
dimstyle-show-zero-feet-suppress-zero-inches = Null Fuß anzeigen; null Zoll unterdrücken
dimstyle-suppress-arrows-when-no-fit = Pfeile unterdrücken, wenn sie nicht passen
dimstyle-suppress-first-dimension-line = Erste Maßlinie unterdrücken
dimstyle-suppress-first-extension-line = Erste Hilfslinie unterdrücken
dimstyle-suppress-second-dimension-line = Zweite Maßlinie unterdrücken
dimstyle-suppress-second-extension-line = Zweite Hilfslinie unterdrücken
dimstyle-suppress-zero-feet-and-inches = Null Fuß und null Zoll unterdrücken
dimstyle-suppress-zero-feet-show-zero-inches = Null Fuß unterdrücken; null Zoll anzeigen
dimstyle-symbols-and-arrows = Symbole und Pfeile
dimstyle-symmetrical = Symmetrisch
dimstyle-text-background = Texthintergrund
dimstyle-text-gap = Textabstand
dimstyle-fixed-text-height-note = Der gewählte Textstil legt die Höhe auf __ocs_arg_height__ fest.
dimstyle-tick-size = Schrägstrichgröße
dimstyle-tolerance-zero-suppression = Nullunterdrückung für Toleranzen
dimstyle-unit-format = Einheitenformat
dimstyle-upper-value = Oberer Wert
dimstyle-measurement-template-help = Verwenden Sie <> für den Messwert; Text davor und danach wird zum Präfix bzw. Suffix.
dimstyle-use-different-arrowheads = Unterschiedliche erste und zweite Pfeilspitzen verwenden
dimstyle-use-fixed-length-extension-lines = Hilfslinien mit fester Länge verwenden
dimstyle-used-in-drawing = In Zeichnung verwendet
dimstyle-vertical-offset = Vertikaler Abstand
dimstyle-vertical-placement = Vertikale Platzierung
dimstyle-vertical-position = Vertikale Position
dimstyle-when-space-is-limited = Bei begrenztem Platz
dimstyle-when-text-is-moved = Wenn Text verschoben wird
dimstyle-zero-suppression = Nullunterdrückung
dimstyle-add-a-leader = Führungslinie hinzufügen
dimstyle-automatic = Automatisch
dimstyle-by-block = Von Block
dimstyle-comma = Komma
dimstyle-degrees-minutes-seconds = Grad, Minuten, Sekunden
dimstyle-keep-dimension-line-with-text = Maßlinie mit Text verschieben
dimstyle-leading-and-trailing = Führende und nachfolgende Nullen
dimstyle-move-arrows-outside-first = Zuerst Pfeile nach außen verschieben
dimstyle-move-text-and-arrows-outside = Text und Pfeile nach außen verschieben
dimstyle-move-text-freely = Text frei verschieben
dimstyle-move-text-outside-first = Zuerst Text nach außen verschieben
dimstyle-period = Punkt
dimstyle-space = Leerzeichen
style-manager-alignment-spacing = Ausrichtungsabstand
style-manager-always-left-align = Immer links ausrichten
style-manager-attachment-direction = Anschlussrichtung
style-manager-background-fill-enabled = Hintergrundfüllung aktiviert
style-manager-big-font = Großschrift
style-manager-block-scale-x = Blockskalierung X
style-manager-block-scale-y = Blockskalierung Y
style-manager-block-scale-z = Blockskalierung Z
style-manager-borders = Rahmenlinien
style-manager-bottom-attachment = Unterer Anschluss
style-manager-break-gap-size = Unterbrechungsabstand
style-manager-caps-and-fill = Abschlüsse und Füllung
style-manager-content-type = Inhaltstyp
style-manager-data-row = Datenzeile
style-manager-data-type = Datentyp
style-manager-default-text = Standardtext
style-manager-different = Unterschiedlich
style-manager-display-joints = Verbindungen anzeigen
style-manager-drawing-height = Zeichnungshöhe
style-manager-drawing-scale = Zeichnungsmaßstab
style-manager-effects = Effekte
style-manager-elements = Elemente
style-manager-end-caps = Endabschlüsse
style-manager-fill-and-joints = Füllung und Verbindungen
style-manager-fill-enabled = Füllung aktiviert
style-manager-first-segment-angle = Winkel des ersten Segments
style-manager-fixed-height = Feste Höhe
style-manager-flow-direction = Flussrichtung
style-manager-font-file = Schriftdatei
style-manager-fonts = Schriftarten
style-manager-format-string = Formatzeichenfolge
style-manager-header = Kopfzeile
style-manager-header-row = Kopfzeile
style-manager-horizontal-margin = Horizontaler Rand
style-manager-inner-arcs = Innere Bögen
style-manager-inside-horizontal = Innen horizontal
style-manager-inside-vertical = Innen vertikal
style-manager-leader-draw-order = Zeichnungsreihenfolge der Führungslinie
style-manager-leader-note = Führungslinienhinweis
style-manager-left-attachment = Linker Anschluss
style-manager-line-color = Linienfarbe
style-manager-line-type = Linientyp
style-manager-line-weight = Linienstärke
style-manager-maximum-leader-points = Maximale Führungslinienpunkte
style-manager-multileader-draw-order = Zeichnungsreihenfolge der Multi-Führungslinie
style-manager-no-preview = Keine Vorschau
style-manager-oblique-angle = Schrägstellwinkel
style-manager-outer-arc = Außenbogen
style-manager-paper-height = Papierhöhe
style-manager-paper-text-height = Papiertexthöhe
style-manager-path-type = Pfadtyp
style-manager-right-attachment = Rechter Anschluss
style-manager-scale-factor = Skalierungsfaktor
style-manager-second-segment-angle = Winkel des zweiten Segments
style-manager-size-and-effects = Größe und Effekte
style-manager-start-caps = Startabschlüsse
style-manager-stroke-fonts = Strichschriftarten
style-manager-suppress-header-row = Kopfzeile unterdrücken
style-manager-suppress-title-row = Titelzeile unterdrücken
style-manager-system-font = Systemschriftart
style-manager-system-fonts = Systemschriftarten
style-manager-text-alignment = Textausrichtung
style-manager-text-angle = Textwinkel
style-manager-title-row = Titelzeile
style-manager-top-attachment = Oberer Anschluss
style-manager-unit-type = Einheitentyp
style-manager-vertical-margin = Vertikaler Rand
style-manager-font-family-placeholder = Schriftfamilie…
style-manager-bottom-to-top = Von unten nach oben
style-manager-arrowhead-first = Pfeilspitze zuerst
style-manager-content-first = Inhalt zuerst
style-manager-leader-first = Führungslinie zuerst
style-manager-straight = Gerade
style-manager-parallel-last-segment = Parallel zum letzten Segment
style-manager-top-of-top-line = Oberkante der obersten Zeile
style-manager-middle-of-top-line = Mitte der obersten Zeile
style-manager-middle-of-text = Textmitte
style-manager-middle-of-bottom-line = Mitte der untersten Zeile
style-manager-bottom-of-bottom-line = Unterkante der untersten Zeile
style-manager-bottom-line = Unterste Zeile
style-manager-underline-bottom-line = Unterste Zeile unterstreichen
style-manager-underline-top-line = Oberste Zeile unterstreichen
style-manager-underline-all-text = Gesamten Text unterstreichen
style-manager-center-of-text = Textzentrum
style-manager-center-of-text-overline = Textzentrum mit Oberstrich
style-manager-block-extents = Blockgrenzen
style-manager-base-point = Basispunkt

View file

@ -3195,3 +3195,185 @@ catalog-mod-shortcuts-4c54bbb78e = ── __ocs_arg_MOD__ Shortcuts ────
catalog-custom-overrides-shortcuts-set-07be563dec = ── Custom Overrides (SHORTCUTS SET) ────────────────── catalog-custom-overrides-shortcuts-set-07be563dec = ── Custom Overrides (SHORTCUTS SET) ──────────────────
catalog-function-keys-b5a2df24e9 = ── Function Keys ────────────────────────────────────── catalog-function-keys-b5a2df24e9 = ── Function Keys ──────────────────────────────────────
catalog-other-keys-a882812cf2 = ── Other Keys ────────────────────────────────────────── catalog-other-keys-a882812cf2 = ── Other Keys ──────────────────────────────────────────
dimstyle-keep-text-between-extension-lines = Always keep text between extension lines
dimstyle-angular-dimensions = Angular dimensions
dimstyle-arc-length-symbol = Arc length symbol
dimstyle-arrowheads = Arrowheads
dimstyle-available-style = Available style
dimstyle-baseline-spacing = Baseline spacing
dimstyle-basic = Basic
dimstyle-both-arrowheads = Both arrowheads
dimstyle-center-mark = Center mark
dimstyle-center-mark-size = Center mark size
dimstyle-center-marks = Center marks
dimstyle-centerlines = Centerlines
dimstyle-compare-with = Compare with
dimstyle-current-style = Current style
dimstyle-deviation = Deviation
dimstyle-different-sections = Different sections
dimstyle-dimension-line-color = Dimension line color
dimstyle-dimension-linetype = Dimension linetype
dimstyle-dimension-lineweight = Dimension lineweight
dimstyle-display-alternate-units = Display alternate units
dimstyle-draw-dimension-line-between-extension-lines = Draw dimension line between extension lines
dimstyle-extend-beyond-dimension-line = Extend beyond dimension line
dimstyle-extend-beyond-ticks = Extend beyond ticks
dimstyle-extension-line-color = Extension line color
dimstyle-extension-lineweight = Extension lineweight
dimstyle-feet-and-inches = Feet and inches
dimstyle-fine-tuning = Fine tuning
dimstyle-first-arrowhead = First arrowhead
dimstyle-first-extension-linetype = First extension linetype
dimstyle-fit-options = Fit options
dimstyle-fixed-length = Fixed length
dimstyle-fraction-format = Fraction format
dimstyle-height-scale = Height scale
dimstyle-horizontal-placement = Horizontal placement
dimstyle-keep-text-horizontal-inside = Keep text horizontal when inside
dimstyle-keep-text-horizontal-outside = Keep text horizontal when outside
dimstyle-leader-arrowhead = Leader arrowhead
dimstyle-limits = Limits
dimstyle-linear-dimensions = Linear dimensions
dimstyle-lower-value = Lower value
dimstyle-measurement-scale = Measurement scale
dimstyle-measurement-template = Measurement template
dimstyle-method = Method
dimstyle-multiplier = Multiplier
dimstyle-no-differences = No differences
dimstyle-not-applicable = Not applicable
dimstyle-offset-from-measured-point = Offset from measured point
dimstyle-place-text-manually = Place text manually while creating dimensions
dimstyle-radius-jog-angle = Radius jog angle (°)
dimstyle-read-left-to-right = Read left to right
dimstyle-referenced-read-only = Referenced style · Read only
dimstyle-round-off = Round off
dimstyle-second-arrowhead = Second arrowhead
dimstyle-second-extension-linetype = Second extension linetype
dimstyle-show-zero-feet-and-inches = Show zero feet and zero inches
dimstyle-show-zero-feet-suppress-zero-inches = Show zero feet; suppress zero inches
dimstyle-suppress-arrows-when-no-fit = Suppress arrows when they do not fit
dimstyle-suppress-first-dimension-line = Suppress first dimension line
dimstyle-suppress-first-extension-line = Suppress first extension line
dimstyle-suppress-second-dimension-line = Suppress second dimension line
dimstyle-suppress-second-extension-line = Suppress second extension line
dimstyle-suppress-zero-feet-and-inches = Suppress zero feet and zero inches
dimstyle-suppress-zero-feet-show-zero-inches = Suppress zero feet; show zero inches
dimstyle-symbols-and-arrows = Symbols and Arrows
dimstyle-symmetrical = Symmetrical
dimstyle-text-background = Text background
dimstyle-text-gap = Text gap
dimstyle-fixed-text-height-note = The selected text style fixes the height at __ocs_arg_height__.
dimstyle-tick-size = Tick size
dimstyle-tolerance-zero-suppression = Tolerance zero suppression
dimstyle-unit-format = Unit format
dimstyle-upper-value = Upper value
dimstyle-measurement-template-help = Use <> as the measured value; text before and after it becomes the prefix and suffix.
dimstyle-use-different-arrowheads = Use different first and second arrowheads
dimstyle-use-fixed-length-extension-lines = Use fixed-length extension lines
dimstyle-used-in-drawing = Used in drawing
dimstyle-vertical-offset = Vertical offset
dimstyle-vertical-placement = Vertical placement
dimstyle-vertical-position = Vertical position
dimstyle-when-space-is-limited = When space is limited
dimstyle-when-text-is-moved = When text is moved
dimstyle-zero-suppression = Zero suppression
dimstyle-add-a-leader = Add a leader
dimstyle-automatic = Automatic
dimstyle-by-block = By block
dimstyle-comma = Comma
dimstyle-degrees-minutes-seconds = Degrees, minutes, seconds
dimstyle-keep-dimension-line-with-text = Keep dimension line with text
dimstyle-leading-and-trailing = Leading and trailing
dimstyle-move-arrows-outside-first = Move arrows outside first
dimstyle-move-text-and-arrows-outside = Move text and arrows outside
dimstyle-move-text-freely = Move text freely
dimstyle-move-text-outside-first = Move text outside first
dimstyle-period = Period
dimstyle-space = Space
style-manager-alignment-spacing = Alignment spacing
style-manager-always-left-align = Always left align
style-manager-attachment-direction = Attachment direction
style-manager-background-fill-enabled = Background fill enabled
style-manager-big-font = Big font
style-manager-block-scale-x = Block scale X
style-manager-block-scale-y = Block scale Y
style-manager-block-scale-z = Block scale Z
style-manager-borders = Borders
style-manager-bottom-attachment = Bottom attachment
style-manager-break-gap-size = Break gap size
style-manager-caps-and-fill = Caps and Fill
style-manager-content-type = Content type
style-manager-data-row = Data Row
style-manager-data-type = Data type
style-manager-default-text = Default text
style-manager-different = Different
style-manager-display-joints = Display joints
style-manager-drawing-height = Drawing height
style-manager-drawing-scale = Drawing scale
style-manager-effects = Effects
style-manager-elements = Elements
style-manager-end-caps = End caps
style-manager-fill-and-joints = Fill and joints
style-manager-fill-enabled = Fill enabled
style-manager-first-segment-angle = First segment angle
style-manager-fixed-height = Fixed height
style-manager-flow-direction = Flow direction
style-manager-font-file = Font file
style-manager-fonts = Fonts
style-manager-format-string = Format string
style-manager-header = Header
style-manager-header-row = Header Row
style-manager-horizontal-margin = Horizontal margin
style-manager-inner-arcs = Inner arcs
style-manager-inside-horizontal = Inside horizontal
style-manager-inside-vertical = Inside vertical
style-manager-leader-draw-order = Leader draw order
style-manager-leader-note = Leader note
style-manager-left-attachment = Left attachment
style-manager-line-color = Line color
style-manager-line-type = Line type
style-manager-line-weight = Line weight
style-manager-maximum-leader-points = Maximum leader points
style-manager-multileader-draw-order = Multileader draw order
style-manager-no-preview = No preview
style-manager-oblique-angle = Oblique angle
style-manager-outer-arc = Outer arc
style-manager-paper-height = Paper height
style-manager-paper-text-height = Paper text height
style-manager-path-type = Path type
style-manager-right-attachment = Right attachment
style-manager-scale-factor = Scale factor
style-manager-second-segment-angle = Second segment angle
style-manager-size-and-effects = Size and Effects
style-manager-start-caps = Start caps
style-manager-stroke-fonts = Stroke fonts
style-manager-suppress-header-row = Suppress header row
style-manager-suppress-title-row = Suppress title row
style-manager-system-font = System font
style-manager-system-fonts = System fonts
style-manager-text-alignment = Text alignment
style-manager-text-angle = Text angle
style-manager-title-row = Title Row
style-manager-top-attachment = Top attachment
style-manager-unit-type = Unit type
style-manager-vertical-margin = Vertical margin
style-manager-font-family-placeholder = font family…
style-manager-bottom-to-top = Bottom to top
style-manager-arrowhead-first = Arrowhead first
style-manager-content-first = Content first
style-manager-leader-first = Leader first
style-manager-straight = Straight
style-manager-parallel-last-segment = Parallel to last segment
style-manager-top-of-top-line = Top of top line
style-manager-middle-of-top-line = Middle of top line
style-manager-middle-of-text = Middle of text
style-manager-middle-of-bottom-line = Middle of bottom line
style-manager-bottom-of-bottom-line = Bottom of bottom line
style-manager-bottom-line = Bottom line
style-manager-underline-bottom-line = Underline bottom line
style-manager-underline-top-line = Underline top line
style-manager-underline-all-text = Underline all text
style-manager-center-of-text = Center of text
style-manager-center-of-text-overline = Center of text with overline
style-manager-block-extents = Block extents
style-manager-base-point = Base point

View file

@ -3194,3 +3194,185 @@ catalog-mod-shortcuts-4c54bbb78e = ── Raccourcis __ocs_arg_MOD__ ───
catalog-custom-overrides-shortcuts-set-07be563dec = ── Remplacements personnalisés (SHORTCUTS SET) ─────── catalog-custom-overrides-shortcuts-set-07be563dec = ── Remplacements personnalisés (SHORTCUTS SET) ───────
catalog-function-keys-b5a2df24e9 = ── Touches de fonction ──────────────────────────────── catalog-function-keys-b5a2df24e9 = ── Touches de fonction ────────────────────────────────
catalog-other-keys-a882812cf2 = ── Autres touches ───────────────────────────────────── catalog-other-keys-a882812cf2 = ── Autres touches ─────────────────────────────────────
dimstyle-keep-text-between-extension-lines = Toujours conserver le texte entre les lignes d’attache
dimstyle-angular-dimensions = Cotes angulaires
dimstyle-arc-length-symbol = Symbole de longueur d’arc
dimstyle-arrowheads = Pointes de flèche
dimstyle-available-style = Style disponible
dimstyle-baseline-spacing = Espacement des lignes de base
dimstyle-basic = Théorique exacte
dimstyle-both-arrowheads = Deux pointes de flèche
dimstyle-center-mark = Marque de centre
dimstyle-center-mark-size = Taille de la marque de centre
dimstyle-center-marks = Marques de centre
dimstyle-centerlines = Axes
dimstyle-compare-with = Comparer avec
dimstyle-current-style = Style courant
dimstyle-deviation = Écarts
dimstyle-different-sections = Sections différentes
dimstyle-dimension-line-color = Couleur de ligne de cote
dimstyle-dimension-linetype = Type de ligne de cote
dimstyle-dimension-lineweight = Épaisseur de ligne de cote
dimstyle-display-alternate-units = Afficher les unités alternatives
dimstyle-draw-dimension-line-between-extension-lines = Tracer la ligne de cote entre les lignes d’attache
dimstyle-extend-beyond-dimension-line = Dépassement de la ligne de cote
dimstyle-extend-beyond-ticks = Dépassement des obliques
dimstyle-extension-line-color = Couleur de ligne d’attache
dimstyle-extension-lineweight = Épaisseur de ligne d’attache
dimstyle-feet-and-inches = Pieds et pouces
dimstyle-fine-tuning = Ajustement fin
dimstyle-first-arrowhead = Première pointe de flèche
dimstyle-first-extension-linetype = Type de la première ligne d’attache
dimstyle-fit-options = Options d’ajustement
dimstyle-fixed-length = Longueur fixe
dimstyle-fraction-format = Format de fraction
dimstyle-height-scale = Échelle de hauteur
dimstyle-horizontal-placement = Placement horizontal
dimstyle-keep-text-horizontal-inside = Conserver le texte horizontal à l’intérieur
dimstyle-keep-text-horizontal-outside = Conserver le texte horizontal à l’extérieur
dimstyle-leader-arrowhead = Pointe de flèche de repère
dimstyle-limits = Limites
dimstyle-linear-dimensions = Cotes linéaires
dimstyle-lower-value = Valeur inférieure
dimstyle-measurement-scale = Échelle de mesure
dimstyle-measurement-template = Modèle de mesure
dimstyle-method = Méthode
dimstyle-multiplier = Multiplicateur
dimstyle-no-differences = Aucune différence
dimstyle-not-applicable = Sans objet
dimstyle-offset-from-measured-point = Décalage depuis le point mesuré
dimstyle-place-text-manually = Placer le texte manuellement lors de la cotation
dimstyle-radius-jog-angle = Angle de la ligne brisée de rayon (°)
dimstyle-read-left-to-right = Lire de gauche à droite
dimstyle-referenced-read-only = Style référencé · Lecture seule
dimstyle-round-off = Arrondi
dimstyle-second-arrowhead = Deuxième pointe de flèche
dimstyle-second-extension-linetype = Type de la deuxième ligne d’attache
dimstyle-show-zero-feet-and-inches = Afficher zéro pied et zéro pouce
dimstyle-show-zero-feet-suppress-zero-inches = Afficher zéro pied ; supprimer zéro pouce
dimstyle-suppress-arrows-when-no-fit = Supprimer les flèches si elles ne tiennent pas
dimstyle-suppress-first-dimension-line = Supprimer la première ligne de cote
dimstyle-suppress-first-extension-line = Supprimer la première ligne d’attache
dimstyle-suppress-second-dimension-line = Supprimer la deuxième ligne de cote
dimstyle-suppress-second-extension-line = Supprimer la deuxième ligne d’attache
dimstyle-suppress-zero-feet-and-inches = Supprimer zéro pied et zéro pouce
dimstyle-suppress-zero-feet-show-zero-inches = Supprimer zéro pied ; afficher zéro pouce
dimstyle-symbols-and-arrows = Symboles et flèches
dimstyle-symmetrical = Symétrique
dimstyle-text-background = Arrière-plan du texte
dimstyle-text-gap = Espacement du texte
dimstyle-fixed-text-height-note = Le style de texte sélectionné fixe la hauteur à __ocs_arg_height__.
dimstyle-tick-size = Taille des obliques
dimstyle-tolerance-zero-suppression = Suppression des zéros de tolérance
dimstyle-unit-format = Format d’unité
dimstyle-upper-value = Valeur supérieure
dimstyle-measurement-template-help = Utilisez <> pour la valeur mesurée ; le texte placé avant et après devient le préfixe et le suffixe.
dimstyle-use-different-arrowheads = Utiliser des pointes différentes aux deux extrémités
dimstyle-use-fixed-length-extension-lines = Utiliser des lignes d’attache de longueur fixe
dimstyle-used-in-drawing = Utilisé dans le dessin
dimstyle-vertical-offset = Décalage vertical
dimstyle-vertical-placement = Placement vertical
dimstyle-vertical-position = Position verticale
dimstyle-when-space-is-limited = Lorsque l’espace est limité
dimstyle-when-text-is-moved = Lorsque le texte est déplacé
dimstyle-zero-suppression = Suppression des zéros
dimstyle-add-a-leader = Ajouter une ligne de repère
dimstyle-automatic = Automatique
dimstyle-by-block = Du bloc
dimstyle-comma = Virgule
dimstyle-degrees-minutes-seconds = Degrés, minutes, secondes
dimstyle-keep-dimension-line-with-text = Déplacer la ligne de cote avec le texte
dimstyle-leading-and-trailing = Zéros de début et de fin
dimstyle-move-arrows-outside-first = Déplacer d’abord les flèches à l’extérieur
dimstyle-move-text-and-arrows-outside = Déplacer le texte et les flèches à l’extérieur
dimstyle-move-text-freely = Déplacer le texte librement
dimstyle-move-text-outside-first = Déplacer d’abord le texte à l’extérieur
dimstyle-period = Point
dimstyle-space = Espace
style-manager-alignment-spacing = Espacement d’alignement
style-manager-always-left-align = Toujours aligner à gauche
style-manager-attachment-direction = Direction d’attachement
style-manager-background-fill-enabled = Remplissage d’arrière-plan activé
style-manager-big-font = Police grande taille
style-manager-block-scale-x = Échelle X du bloc
style-manager-block-scale-y = Échelle Y du bloc
style-manager-block-scale-z = Échelle Z du bloc
style-manager-borders = Bordures
style-manager-bottom-attachment = Attachement inférieur
style-manager-break-gap-size = Taille de l’espacement de coupure
style-manager-caps-and-fill = Extrémités et remplissage
style-manager-content-type = Type de contenu
style-manager-data-row = Ligne de données
style-manager-data-type = Type de données
style-manager-default-text = Texte par défaut
style-manager-different = Différent
style-manager-display-joints = Afficher les jonctions
style-manager-drawing-height = Hauteur dans le dessin
style-manager-drawing-scale = Échelle du dessin
style-manager-effects = Effets
style-manager-elements = Éléments
style-manager-end-caps = Extrémités de fin
style-manager-fill-and-joints = Remplissage et jonctions
style-manager-fill-enabled = Remplissage activé
style-manager-first-segment-angle = Angle du premier segment
style-manager-fixed-height = Hauteur fixe
style-manager-flow-direction = Sens de lecture
style-manager-font-file = Fichier de police
style-manager-fonts = Polices
style-manager-format-string = Chaîne de format
style-manager-header = En-tête
style-manager-header-row = Ligne d’en-tête
style-manager-horizontal-margin = Marge horizontale
style-manager-inner-arcs = Arcs intérieurs
style-manager-inside-horizontal = Intérieur horizontal
style-manager-inside-vertical = Intérieur vertical
style-manager-leader-draw-order = Ordre de tracé de la ligne de repère
style-manager-leader-note = Note de repère
style-manager-left-attachment = Attachement gauche
style-manager-line-color = Couleur de ligne
style-manager-line-type = Type de ligne
style-manager-line-weight = Épaisseur de ligne
style-manager-maximum-leader-points = Nombre maximal de points de repère
style-manager-multileader-draw-order = Ordre de tracé du repère multiple
style-manager-no-preview = Aucun aperçu
style-manager-oblique-angle = Angle d’inclinaison
style-manager-outer-arc = Arc extérieur
style-manager-paper-height = Hauteur sur papier
style-manager-paper-text-height = Hauteur de texte sur papier
style-manager-path-type = Type de trajectoire
style-manager-right-attachment = Attachement droit
style-manager-scale-factor = Facteur d’échelle
style-manager-second-segment-angle = Angle du deuxième segment
style-manager-size-and-effects = Taille et effets
style-manager-start-caps = Extrémités de départ
style-manager-stroke-fonts = Polices filaires
style-manager-suppress-header-row = Masquer la ligne d’en-tête
style-manager-suppress-title-row = Masquer la ligne de titre
style-manager-system-font = Police système
style-manager-system-fonts = Polices système
style-manager-text-alignment = Alignement du texte
style-manager-text-angle = Angle du texte
style-manager-title-row = Ligne de titre
style-manager-top-attachment = Attachement supérieur
style-manager-unit-type = Type d’unité
style-manager-vertical-margin = Marge verticale
style-manager-font-family-placeholder = famille de polices…
style-manager-bottom-to-top = De bas en haut
style-manager-arrowhead-first = Pointe de flèche en premier
style-manager-content-first = Contenu en premier
style-manager-leader-first = Ligne de repère en premier
style-manager-straight = Droite
style-manager-parallel-last-segment = Parallèle au dernier segment
style-manager-top-of-top-line = Haut de la ligne supérieure
style-manager-middle-of-top-line = Milieu de la ligne supérieure
style-manager-middle-of-text = Milieu du texte
style-manager-middle-of-bottom-line = Milieu de la ligne inférieure
style-manager-bottom-of-bottom-line = Bas de la ligne inférieure
style-manager-bottom-line = Ligne inférieure
style-manager-underline-bottom-line = Souligner la ligne inférieure
style-manager-underline-top-line = Souligner la ligne supérieure
style-manager-underline-all-text = Souligner tout le texte
style-manager-center-of-text = Centre du texte
style-manager-center-of-text-overline = Centre du texte avec surlignement
style-manager-block-extents = Limites du bloc
style-manager-base-point = Point de base

View file

@ -3183,3 +3183,185 @@ catalog-mod-shortcuts-4c54bbb78e = ── __ocs_arg_MOD__ शॉर्टकट
catalog-custom-overrides-shortcuts-set-07be563dec = ── कस्टम ओवरराइड (SHORTCUTS SET) ──────────────────── catalog-custom-overrides-shortcuts-set-07be563dec = ── कस्टम ओवरराइड (SHORTCUTS SET) ────────────────────
catalog-function-keys-b5a2df24e9 = ── फ़ंक्शन कुंजियाँ ─────────────────────────────────── catalog-function-keys-b5a2df24e9 = ── फ़ंक्शन कुंजियाँ ───────────────────────────────────
catalog-other-keys-a882812cf2 = ── अन्य कुंजियाँ ───────────────────────────────────── catalog-other-keys-a882812cf2 = ── अन्य कुंजियाँ ─────────────────────────────────────
dimstyle-keep-text-between-extension-lines = पाठ को हमेशा विस्तार रेखाओं के बीच रखें
dimstyle-angular-dimensions = कोणीय माप
dimstyle-arc-length-symbol = चाप लंबाई चिह्न
dimstyle-arrowheads = तीर शीर्ष
dimstyle-available-style = उपलब्ध शैली
dimstyle-baseline-spacing = आधार रेखा अंतर
dimstyle-basic = मूल
dimstyle-both-arrowheads = दोनों तीर शीर्ष
dimstyle-center-mark = केंद्र चिह्न
dimstyle-center-mark-size = केंद्र चिह्न का आकार
dimstyle-center-marks = केंद्र चिह्न
dimstyle-centerlines = केंद्र रेखाएँ
dimstyle-compare-with = इससे तुलना करें
dimstyle-current-style = वर्तमान शैली
dimstyle-deviation = विचलन
dimstyle-different-sections = भिन्न अनुभाग
dimstyle-dimension-line-color = माप रेखा का रंग
dimstyle-dimension-linetype = माप रेखा प्रकार
dimstyle-dimension-lineweight = माप रेखा मोटाई
dimstyle-display-alternate-units = वैकल्पिक इकाइयाँ दिखाएँ
dimstyle-draw-dimension-line-between-extension-lines = विस्तार रेखाओं के बीच माप रेखा बनाएँ
dimstyle-extend-beyond-dimension-line = माप रेखा से आगे बढ़ाएँ
dimstyle-extend-beyond-ticks = तिरछे चिह्नों से आगे बढ़ाएँ
dimstyle-extension-line-color = विस्तार रेखा का रंग
dimstyle-extension-lineweight = विस्तार रेखा मोटाई
dimstyle-feet-and-inches = फुट और इंच
dimstyle-fine-tuning = सूक्ष्म समायोजन
dimstyle-first-arrowhead = पहला तीर शीर्ष
dimstyle-first-extension-linetype = पहली विस्तार रेखा का प्रकार
dimstyle-fit-options = समायोजन विकल्प
dimstyle-fixed-length = निश्चित लंबाई
dimstyle-fraction-format = भिन्न प्रारूप
dimstyle-height-scale = ऊँचाई मापनी
dimstyle-horizontal-placement = क्षैतिज स्थान
dimstyle-keep-text-horizontal-inside = अंदर होने पर पाठ क्षैतिज रखें
dimstyle-keep-text-horizontal-outside = बाहर होने पर पाठ क्षैतिज रखें
dimstyle-leader-arrowhead = संकेतक तीर शीर्ष
dimstyle-limits = सीमाएँ
dimstyle-linear-dimensions = रैखिक माप
dimstyle-lower-value = निचला मान
dimstyle-measurement-scale = मापन मापनी
dimstyle-measurement-template = मापन साँचा
dimstyle-method = विधि
dimstyle-multiplier = गुणक
dimstyle-no-differences = कोई अंतर नहीं
dimstyle-not-applicable = लागू नहीं
dimstyle-offset-from-measured-point = मापे गए बिंदु से दूरी
dimstyle-place-text-manually = माप बनाते समय पाठ को स्वयं रखें
dimstyle-radius-jog-angle = त्रिज्या मोड़ कोण (°)
dimstyle-read-left-to-right = बाएँ से दाएँ पढ़ें
dimstyle-referenced-read-only = संदर्भित शैली · केवल पढ़ने योग्य
dimstyle-round-off = पूर्णांकन
dimstyle-second-arrowhead = दूसरा तीर शीर्ष
dimstyle-second-extension-linetype = दूसरी विस्तार रेखा का प्रकार
dimstyle-show-zero-feet-and-inches = शून्य फुट और शून्य इंच दिखाएँ
dimstyle-show-zero-feet-suppress-zero-inches = शून्य फुट दिखाएँ; शून्य इंच छिपाएँ
dimstyle-suppress-arrows-when-no-fit = जगह न होने पर तीर छिपाएँ
dimstyle-suppress-first-dimension-line = पहली माप रेखा छिपाएँ
dimstyle-suppress-first-extension-line = पहली विस्तार रेखा छिपाएँ
dimstyle-suppress-second-dimension-line = दूसरी माप रेखा छिपाएँ
dimstyle-suppress-second-extension-line = दूसरी विस्तार रेखा छिपाएँ
dimstyle-suppress-zero-feet-and-inches = शून्य फुट और शून्य इंच छिपाएँ
dimstyle-suppress-zero-feet-show-zero-inches = शून्य फुट छिपाएँ; शून्य इंच दिखाएँ
dimstyle-symbols-and-arrows = चिह्न और तीर
dimstyle-symmetrical = सममित
dimstyle-text-background = पाठ पृष्ठभूमि
dimstyle-text-gap = पाठ अंतर
dimstyle-fixed-text-height-note = चुनी गई पाठ शैली ऊँचाई को __ocs_arg_height__ पर स्थिर करती है।
dimstyle-tick-size = तिरछे चिह्न का आकार
dimstyle-tolerance-zero-suppression = सहनशीलता के शून्य छिपाना
dimstyle-unit-format = इकाई प्रारूप
dimstyle-upper-value = ऊपरी मान
dimstyle-measurement-template-help = मापे गए मान के लिए <> का उपयोग करें; इसके पहले और बाद का पाठ उपसर्ग और प्रत्यय बनता है।
dimstyle-use-different-arrowheads = पहले और दूसरे तीर शीर्ष अलग रखें
dimstyle-use-fixed-length-extension-lines = निश्चित लंबाई की विस्तार रेखाएँ उपयोग करें
dimstyle-used-in-drawing = आरेख में प्रयुक्त
dimstyle-vertical-offset = ऊर्ध्वाधर दूरी
dimstyle-vertical-placement = ऊर्ध्वाधर स्थान
dimstyle-vertical-position = ऊर्ध्वाधर स्थिति
dimstyle-when-space-is-limited = जब स्थान सीमित हो
dimstyle-when-text-is-moved = जब पाठ हटाया जाए
dimstyle-zero-suppression = शून्य छिपाना
dimstyle-add-a-leader = संकेतक रेखा जोड़ें
dimstyle-automatic = स्वचालित
dimstyle-by-block = ब्लॉक के अनुसार
dimstyle-comma = अल्पविराम
dimstyle-degrees-minutes-seconds = अंश, मिनट, सेकंड
dimstyle-keep-dimension-line-with-text = माप रेखा को पाठ के साथ रखें
dimstyle-leading-and-trailing = आरंभिक और अंतिम
dimstyle-move-arrows-outside-first = पहले तीर बाहर ले जाएँ
dimstyle-move-text-and-arrows-outside = पाठ और तीर बाहर ले जाएँ
dimstyle-move-text-freely = पाठ को स्वतंत्र रूप से ले जाएँ
dimstyle-move-text-outside-first = पहले पाठ बाहर ले जाएँ
dimstyle-period = दशमलव बिंदु
dimstyle-space = रिक्त स्थान
style-manager-alignment-spacing = संरेखण अंतराल
style-manager-always-left-align = हमेशा बाएँ संरेखित करें
style-manager-attachment-direction = संयोजन दिशा
style-manager-background-fill-enabled = पृष्ठभूमि भराव सक्षम
style-manager-big-font = बड़ा फ़ॉन्ट
style-manager-block-scale-x = ब्लॉक X स्केल
style-manager-block-scale-y = ब्लॉक Y स्केल
style-manager-block-scale-z = ब्लॉक Z स्केल
style-manager-borders = सीमाएँ
style-manager-bottom-attachment = निचला संयोजन
style-manager-break-gap-size = ब्रेक अंतराल आकार
style-manager-caps-and-fill = सिरे और भराव
style-manager-content-type = सामग्री प्रकार
style-manager-data-row = डेटा पंक्ति
style-manager-data-type = डेटा प्रकार
style-manager-default-text = डिफ़ॉल्ट टेक्स्ट
style-manager-different = भिन्न
style-manager-display-joints = जोड़ दिखाएँ
style-manager-drawing-height = ड्रॉइंग ऊँचाई
style-manager-drawing-scale = ड्रॉइंग स्केल
style-manager-effects = प्रभाव
style-manager-elements = अवयव
style-manager-end-caps = अंतिम सिरे
style-manager-fill-and-joints = भराव और जोड़
style-manager-fill-enabled = भराव सक्षम
style-manager-first-segment-angle = पहले खंड का कोण
style-manager-fixed-height = निश्चित ऊँचाई
style-manager-flow-direction = प्रवाह दिशा
style-manager-font-file = फ़ॉन्ट फ़ाइल
style-manager-fonts = फ़ॉन्ट
style-manager-format-string = प्रारूप स्ट्रिंग
style-manager-header = शीर्षलेख
style-manager-header-row = शीर्षलेख पंक्ति
style-manager-horizontal-margin = क्षैतिज हाशिया
style-manager-inner-arcs = आंतरिक चाप
style-manager-inside-horizontal = आंतरिक क्षैतिज
style-manager-inside-vertical = आंतरिक ऊर्ध्वाधर
style-manager-leader-draw-order = लीडर आरेखन क्रम
style-manager-leader-note = लीडर टिप्पणी
style-manager-left-attachment = बायाँ संयोजन
style-manager-line-color = रेखा रंग
style-manager-line-type = रेखा प्रकार
style-manager-line-weight = रेखा मोटाई
style-manager-maximum-leader-points = अधिकतम लीडर बिंदु
style-manager-multileader-draw-order = मल्टीलीडर आरेखन क्रम
style-manager-no-preview = कोई पूर्वावलोकन नहीं
style-manager-oblique-angle = तिरछा कोण
style-manager-outer-arc = बाहरी चाप
style-manager-paper-height = काग़ज़ ऊँचाई
style-manager-paper-text-height = काग़ज़ पर टेक्स्ट ऊँचाई
style-manager-path-type = पथ प्रकार
style-manager-right-attachment = दायाँ संयोजन
style-manager-scale-factor = स्केल गुणक
style-manager-second-segment-angle = दूसरे खंड का कोण
style-manager-size-and-effects = आकार और प्रभाव
style-manager-start-caps = आरंभिक सिरे
style-manager-stroke-fonts = रेखीय फ़ॉन्ट
style-manager-suppress-header-row = शीर्षलेख पंक्ति छिपाएँ
style-manager-suppress-title-row = शीर्षक पंक्ति छिपाएँ
style-manager-system-font = सिस्टम फ़ॉन्ट
style-manager-system-fonts = सिस्टम फ़ॉन्ट
style-manager-text-alignment = टेक्स्ट संरेखण
style-manager-text-angle = टेक्स्ट कोण
style-manager-title-row = शीर्षक पंक्ति
style-manager-top-attachment = ऊपरी संयोजन
style-manager-unit-type = इकाई प्रकार
style-manager-vertical-margin = ऊर्ध्वाधर हाशिया
style-manager-font-family-placeholder = फ़ॉन्ट परिवार…
style-manager-bottom-to-top = नीचे से ऊपर
style-manager-arrowhead-first = पहले तीर का सिरा
style-manager-content-first = पहले सामग्री
style-manager-leader-first = पहले लीडर
style-manager-straight = सीधा
style-manager-parallel-last-segment = अंतिम खंड के समांतर
style-manager-top-of-top-line = शीर्ष पंक्ति का ऊपरी भाग
style-manager-middle-of-top-line = शीर्ष पंक्ति का मध्य
style-manager-middle-of-text = टेक्स्ट का मध्य
style-manager-middle-of-bottom-line = निचली पंक्ति का मध्य
style-manager-bottom-of-bottom-line = निचली पंक्ति का निचला भाग
style-manager-bottom-line = निचली पंक्ति
style-manager-underline-bottom-line = निचली पंक्ति के नीचे रेखा
style-manager-underline-top-line = शीर्ष पंक्ति के नीचे रेखा
style-manager-underline-all-text = सभी टेक्स्ट के नीचे रेखा
style-manager-center-of-text = टेक्स्ट का केंद्र
style-manager-center-of-text-overline = ऊपरी रेखा सहित टेक्स्ट का केंद्र
style-manager-block-extents = ब्लॉक परिमिति
style-manager-base-point = आधार बिंदु

View file

@ -3195,3 +3195,185 @@ catalog-mod-shortcuts-4c54bbb78e = ── __ocs_arg_MOD__-sneltoetsen ───
catalog-custom-overrides-shortcuts-set-07be563dec = ── Aangepaste overschrijvingen (SHORTCUTS SET) ──────── catalog-custom-overrides-shortcuts-set-07be563dec = ── Aangepaste overschrijvingen (SHORTCUTS SET) ────────
catalog-function-keys-b5a2df24e9 = ── Functietoetsen ───────────────────────────────────── catalog-function-keys-b5a2df24e9 = ── Functietoetsen ─────────────────────────────────────
catalog-other-keys-a882812cf2 = ── Overige toetsen ──────────────────────────────────── catalog-other-keys-a882812cf2 = ── Overige toetsen ────────────────────────────────────
dimstyle-keep-text-between-extension-lines = Tekst altijd tussen hulplijnen houden
dimstyle-angular-dimensions = Hoekmaten
dimstyle-arc-length-symbol = Booglengtesymbool
dimstyle-arrowheads = Pijlpunten
dimstyle-available-style = Beschikbare stijl
dimstyle-baseline-spacing = Basislijnafstand
dimstyle-basic = Basis
dimstyle-both-arrowheads = Beide pijlpunten
dimstyle-center-mark = Middelpuntmarkering
dimstyle-center-mark-size = Grootte van middelpuntmarkering
dimstyle-center-marks = Middelpuntmarkeringen
dimstyle-centerlines = Hartlijnen
dimstyle-compare-with = Vergelijken met
dimstyle-current-style = Huidige stijl
dimstyle-deviation = Afwijking
dimstyle-different-sections = Afwijkende secties
dimstyle-dimension-line-color = Kleur maatlijn
dimstyle-dimension-linetype = Lijntype maatlijn
dimstyle-dimension-lineweight = Lijndikte maatlijn
dimstyle-display-alternate-units = Alternatieve eenheden weergeven
dimstyle-draw-dimension-line-between-extension-lines = Maatlijn tussen hulplijnen tekenen
dimstyle-extend-beyond-dimension-line = Voorbij maatlijn verlengen
dimstyle-extend-beyond-ticks = Voorbij schuine streepjes verlengen
dimstyle-extension-line-color = Kleur hulplijn
dimstyle-extension-lineweight = Lijndikte hulplijn
dimstyle-feet-and-inches = Voet en inch
dimstyle-fine-tuning = Fijnafstelling
dimstyle-first-arrowhead = Eerste pijlpunt
dimstyle-first-extension-linetype = Lijntype eerste hulplijn
dimstyle-fit-options = Inpasopties
dimstyle-fixed-length = Vaste lengte
dimstyle-fraction-format = Breuknotatie
dimstyle-height-scale = Hoogteschaal
dimstyle-horizontal-placement = Horizontale plaatsing
dimstyle-keep-text-horizontal-inside = Tekst binnen horizontaal houden
dimstyle-keep-text-horizontal-outside = Tekst buiten horizontaal houden
dimstyle-leader-arrowhead = Pijlpunt van verwijslijn
dimstyle-limits = Grenswaarden
dimstyle-linear-dimensions = Lineaire maten
dimstyle-lower-value = Onderwaarde
dimstyle-measurement-scale = Meetschaal
dimstyle-measurement-template = Meetwaardesjabloon
dimstyle-method = Methode
dimstyle-multiplier = Vermenigvuldigingsfactor
dimstyle-no-differences = Geen verschillen
dimstyle-not-applicable = Niet van toepassing
dimstyle-offset-from-measured-point = Afstand vanaf meetpunt
dimstyle-place-text-manually = Tekst handmatig plaatsen tijdens het bematen
dimstyle-radius-jog-angle = Hoek van radiusknik (°)
dimstyle-read-left-to-right = Van links naar rechts lezen
dimstyle-referenced-read-only = Gerefereerde stijl · Alleen-lezen
dimstyle-round-off = Afronding
dimstyle-second-arrowhead = Tweede pijlpunt
dimstyle-second-extension-linetype = Lijntype tweede hulplijn
dimstyle-show-zero-feet-and-inches = Nul voet en nul inch weergeven
dimstyle-show-zero-feet-suppress-zero-inches = Nul voet weergeven; nul inch onderdrukken
dimstyle-suppress-arrows-when-no-fit = Pijlen onderdrukken als ze niet passen
dimstyle-suppress-first-dimension-line = Eerste maatlijn onderdrukken
dimstyle-suppress-first-extension-line = Eerste hulplijn onderdrukken
dimstyle-suppress-second-dimension-line = Tweede maatlijn onderdrukken
dimstyle-suppress-second-extension-line = Tweede hulplijn onderdrukken
dimstyle-suppress-zero-feet-and-inches = Nul voet en nul inch onderdrukken
dimstyle-suppress-zero-feet-show-zero-inches = Nul voet onderdrukken; nul inch weergeven
dimstyle-symbols-and-arrows = Symbolen en pijlen
dimstyle-symmetrical = Symmetrisch
dimstyle-text-background = Tekstachtergrond
dimstyle-text-gap = Tekstafstand
dimstyle-fixed-text-height-note = De gekozen tekststijl legt de hoogte vast op __ocs_arg_height__.
dimstyle-tick-size = Grootte schuine streep
dimstyle-tolerance-zero-suppression = Nulonderdrukking tolerantie
dimstyle-unit-format = Eenheidsnotatie
dimstyle-upper-value = Bovenwaarde
dimstyle-measurement-template-help = Gebruik <> voor de meetwaarde; tekst ervoor en erna wordt het voor- en achtervoegsel.
dimstyle-use-different-arrowheads = Verschillende eerste en tweede pijlpunten gebruiken
dimstyle-use-fixed-length-extension-lines = Hulplijnen met vaste lengte gebruiken
dimstyle-used-in-drawing = Gebruikt in tekening
dimstyle-vertical-offset = Verticale afstand
dimstyle-vertical-placement = Verticale plaatsing
dimstyle-vertical-position = Verticale positie
dimstyle-when-space-is-limited = Wanneer de ruimte beperkt is
dimstyle-when-text-is-moved = Wanneer tekst wordt verplaatst
dimstyle-zero-suppression = Nulonderdrukking
dimstyle-add-a-leader = Verwijslijn toevoegen
dimstyle-automatic = Automatisch
dimstyle-by-block = Per blok
dimstyle-comma = Komma
dimstyle-degrees-minutes-seconds = Graden, minuten, seconden
dimstyle-keep-dimension-line-with-text = Maatlijn met tekst meeverplaatsen
dimstyle-leading-and-trailing = Voorloop- en eindnullen
dimstyle-move-arrows-outside-first = Eerst pijlen naar buiten verplaatsen
dimstyle-move-text-and-arrows-outside = Tekst en pijlen naar buiten verplaatsen
dimstyle-move-text-freely = Tekst vrij verplaatsen
dimstyle-move-text-outside-first = Eerst tekst naar buiten verplaatsen
dimstyle-period = Punt
dimstyle-space = Spatie
style-manager-alignment-spacing = Uitlijningsafstand
style-manager-always-left-align = Altijd links uitlijnen
style-manager-attachment-direction = Aanhechtingsrichting
style-manager-background-fill-enabled = Achtergrondvulling ingeschakeld
style-manager-big-font = Groot lettertype
style-manager-block-scale-x = Blokschaal X
style-manager-block-scale-y = Blokschaal Y
style-manager-block-scale-z = Blokschaal Z
style-manager-borders = Randen
style-manager-bottom-attachment = Onderste aanhechting
style-manager-break-gap-size = Grootte van onderbrekingsruimte
style-manager-caps-and-fill = Eindkappen en vulling
style-manager-content-type = Inhoudstype
style-manager-data-row = Gegevensrij
style-manager-data-type = Gegevenstype
style-manager-default-text = Standaardtekst
style-manager-different = Verschillend
style-manager-display-joints = Verbindingen weergeven
style-manager-drawing-height = Tekeninghoogte
style-manager-drawing-scale = Tekenschaal
style-manager-effects = Effecten
style-manager-elements = Elementen
style-manager-end-caps = Eindkappen
style-manager-fill-and-joints = Vulling en verbindingen
style-manager-fill-enabled = Vulling ingeschakeld
style-manager-first-segment-angle = Hoek van eerste segment
style-manager-fixed-height = Vaste hoogte
style-manager-flow-direction = Leesrichting
style-manager-font-file = Lettertypebestand
style-manager-fonts = Lettertypen
style-manager-format-string = Opmaakreeks
style-manager-header = Kolomkop
style-manager-header-row = Kolomkoprij
style-manager-horizontal-margin = Horizontale marge
style-manager-inner-arcs = Binnenbogen
style-manager-inside-horizontal = Horizontaal binnen
style-manager-inside-vertical = Verticaal binnen
style-manager-leader-draw-order = Tekenvolgorde van verwijslijn
style-manager-leader-note = Verwijslijnnotitie
style-manager-left-attachment = Linker aanhechting
style-manager-line-color = Lijnkleur
style-manager-line-type = Lijntype
style-manager-line-weight = Lijndikte
style-manager-maximum-leader-points = Maximumaantal verwijslijnpunten
style-manager-multileader-draw-order = Tekenvolgorde van multiverwijslijn
style-manager-no-preview = Geen voorbeeld
style-manager-oblique-angle = Schuintehoek
style-manager-outer-arc = Buitenboog
style-manager-paper-height = Papierhoogte
style-manager-paper-text-height = Teksthoogte op papier
style-manager-path-type = Padtype
style-manager-right-attachment = Rechter aanhechting
style-manager-scale-factor = Schaalfactor
style-manager-second-segment-angle = Hoek van tweede segment
style-manager-size-and-effects = Grootte en effecten
style-manager-start-caps = Startkappen
style-manager-stroke-fonts = Lijnlettertypen
style-manager-suppress-header-row = Kolomkoprij verbergen
style-manager-suppress-title-row = Titelrij verbergen
style-manager-system-font = Systeemlettertype
style-manager-system-fonts = Systeemlettertypen
style-manager-text-alignment = Tekstuitlijning
style-manager-text-angle = Teksthoek
style-manager-title-row = Titelrij
style-manager-top-attachment = Bovenste aanhechting
style-manager-unit-type = Eenheidstype
style-manager-vertical-margin = Verticale marge
style-manager-font-family-placeholder = lettertypefamilie…
style-manager-bottom-to-top = Van onder naar boven
style-manager-arrowhead-first = Pijlpunt eerst
style-manager-content-first = Inhoud eerst
style-manager-leader-first = Verwijslijn eerst
style-manager-straight = Recht
style-manager-parallel-last-segment = Evenwijdig aan laatste segment
style-manager-top-of-top-line = Bovenkant van bovenste regel
style-manager-middle-of-top-line = Midden van bovenste regel
style-manager-middle-of-text = Midden van tekst
style-manager-middle-of-bottom-line = Midden van onderste regel
style-manager-bottom-of-bottom-line = Onderkant van onderste regel
style-manager-bottom-line = Onderste regel
style-manager-underline-bottom-line = Onderste regel onderstrepen
style-manager-underline-top-line = Bovenste regel onderstrepen
style-manager-underline-all-text = Alle tekst onderstrepen
style-manager-center-of-text = Tekstmiddelpunt
style-manager-center-of-text-overline = Tekstmiddelpunt met bovenstreep
style-manager-block-extents = Blokgrenzen
style-manager-base-point = Basispunt

View file

@ -3198,3 +3198,185 @@ catalog-mod-shortcuts-4c54bbb78e = ── Сочетания __ocs_arg_MOD__ ─
catalog-custom-overrides-shortcuts-set-07be563dec = ── Пользовательские переопределения (SHORTCUTS SET) ─ catalog-custom-overrides-shortcuts-set-07be563dec = ── Пользовательские переопределения (SHORTCUTS SET) ─
catalog-function-keys-b5a2df24e9 = ── Функциональные клавиши ───────────────────────────── catalog-function-keys-b5a2df24e9 = ── Функциональные клавиши ─────────────────────────────
catalog-other-keys-a882812cf2 = ── Прочие клавиши ───────────────────────────────────── catalog-other-keys-a882812cf2 = ── Прочие клавиши ─────────────────────────────────────
dimstyle-keep-text-between-extension-lines = Всегда размещать текст между выносными линиями
dimstyle-angular-dimensions = Угловые размеры
dimstyle-arc-length-symbol = Символ длины дуги
dimstyle-arrowheads = Стрелки
dimstyle-available-style = Доступный стиль
dimstyle-baseline-spacing = Шаг базовых размеров
dimstyle-basic = Теоретически точный
dimstyle-both-arrowheads = Обе стрелки
dimstyle-center-mark = Маркер центра
dimstyle-center-mark-size = Размер маркера центра
dimstyle-center-marks = Маркеры центра
dimstyle-centerlines = Осевые линии
dimstyle-compare-with = Сравнить с
dimstyle-current-style = Текущий стиль
dimstyle-deviation = Отклонения
dimstyle-different-sections = Различающиеся разделы
dimstyle-dimension-line-color = Цвет размерной линии
dimstyle-dimension-linetype = Тип размерной линии
dimstyle-dimension-lineweight = Вес размерной линии
dimstyle-display-alternate-units = Показывать альтернативные единицы
dimstyle-draw-dimension-line-between-extension-lines = Рисовать размерную линию между выносными
dimstyle-extend-beyond-dimension-line = Выступ за размерную линию
dimstyle-extend-beyond-ticks = Выступ за засечки
dimstyle-extension-line-color = Цвет выносной линии
dimstyle-extension-lineweight = Вес выносной линии
dimstyle-feet-and-inches = Футы и дюймы
dimstyle-fine-tuning = Точная настройка
dimstyle-first-arrowhead = Первая стрелка
dimstyle-first-extension-linetype = Тип первой выносной линии
dimstyle-fit-options = Параметры размещения
dimstyle-fixed-length = Фиксированная длина
dimstyle-fraction-format = Формат дробей
dimstyle-height-scale = Масштаб высоты
dimstyle-horizontal-placement = Размещение по горизонтали
dimstyle-keep-text-horizontal-inside = Сохранять текст горизонтальным внутри
dimstyle-keep-text-horizontal-outside = Сохранять текст горизонтальным снаружи
dimstyle-leader-arrowhead = Стрелка выноски
dimstyle-limits = Предельные значения
dimstyle-linear-dimensions = Линейные размеры
dimstyle-lower-value = Нижнее значение
dimstyle-measurement-scale = Масштаб измерения
dimstyle-measurement-template = Шаблон значения
dimstyle-method = Способ
dimstyle-multiplier = Множитель
dimstyle-no-differences = Различий нет
dimstyle-not-applicable = Неприменимо
dimstyle-offset-from-measured-point = Отступ от измеряемой точки
dimstyle-place-text-manually = Размещать текст вручную при создании размера
dimstyle-radius-jog-angle = Угол излома радиуса (°)
dimstyle-read-left-to-right = Читать слева направо
dimstyle-referenced-read-only = Ссылочный стиль · Только чтение
dimstyle-round-off = Округление
dimstyle-second-arrowhead = Вторая стрелка
dimstyle-second-extension-linetype = Тип второй выносной линии
dimstyle-show-zero-feet-and-inches = Показывать нулевые футы и дюймы
dimstyle-show-zero-feet-suppress-zero-inches = Показывать нулевые футы; подавлять нулевые дюймы
dimstyle-suppress-arrows-when-no-fit = Подавлять стрелки, если они не помещаются
dimstyle-suppress-first-dimension-line = Подавлять первую размерную линию
dimstyle-suppress-first-extension-line = Подавлять первую выносную линию
dimstyle-suppress-second-dimension-line = Подавлять вторую размерную линию
dimstyle-suppress-second-extension-line = Подавлять вторую выносную линию
dimstyle-suppress-zero-feet-and-inches = Подавлять нулевые футы и дюймы
dimstyle-suppress-zero-feet-show-zero-inches = Подавлять нулевые футы; показывать нулевые дюймы
dimstyle-symbols-and-arrows = Символы и стрелки
dimstyle-symmetrical = Симметрично
dimstyle-text-background = Фон текста
dimstyle-text-gap = Отступ текста
dimstyle-fixed-text-height-note = Выбранный стиль текста фиксирует высоту: __ocs_arg_height__.
dimstyle-tick-size = Размер засечек
dimstyle-tolerance-zero-suppression = Подавление нулей допуска
dimstyle-unit-format = Формат единиц
dimstyle-upper-value = Верхнее значение
dimstyle-measurement-template-help = Используйте <> для измеренного значения; текст до и после него станет префиксом и суффиксом.
dimstyle-use-different-arrowheads = Использовать разные первую и вторую стрелки
dimstyle-use-fixed-length-extension-lines = Использовать выносные линии фиксированной длины
dimstyle-used-in-drawing = Используется в чертеже
dimstyle-vertical-offset = Вертикальный отступ
dimstyle-vertical-placement = Размещение по вертикали
dimstyle-vertical-position = Положение по вертикали
dimstyle-when-space-is-limited = При недостатке места
dimstyle-when-text-is-moved = При перемещении текста
dimstyle-zero-suppression = Подавление нулей
dimstyle-add-a-leader = Добавить выноску
dimstyle-automatic = Автоматически
dimstyle-by-block = По блоку
dimstyle-comma = Запятая
dimstyle-degrees-minutes-seconds = Градусы, минуты, секунды
dimstyle-keep-dimension-line-with-text = Перемещать размерную линию вместе с текстом
dimstyle-leading-and-trailing = Ведущие и конечные
dimstyle-move-arrows-outside-first = Сначала вынести стрелки
dimstyle-move-text-and-arrows-outside = Вынести текст и стрелки
dimstyle-move-text-freely = Свободно перемещать текст
dimstyle-move-text-outside-first = Сначала вынести текст
dimstyle-period = Точка
dimstyle-space = Пробел
style-manager-alignment-spacing = Интервал выравнивания
style-manager-always-left-align = Всегда выравнивать по левому краю
style-manager-attachment-direction = Направление присоединения
style-manager-background-fill-enabled = Фоновая заливка включена
style-manager-big-font = Большой шрифт
style-manager-block-scale-x = Масштаб блока X
style-manager-block-scale-y = Масштаб блока Y
style-manager-block-scale-z = Масштаб блока Z
style-manager-borders = Границы
style-manager-bottom-attachment = Нижнее присоединение
style-manager-break-gap-size = Размер зазора разрыва
style-manager-caps-and-fill = Торцы и заливка
style-manager-content-type = Тип содержимого
style-manager-data-row = Строка данных
style-manager-data-type = Тип данных
style-manager-default-text = Текст по умолчанию
style-manager-different = Отличается
style-manager-display-joints = Показывать стыки
style-manager-drawing-height = Высота в чертеже
style-manager-drawing-scale = Масштаб чертежа
style-manager-effects = Эффекты
style-manager-elements = Элементы
style-manager-end-caps = Конечные торцы
style-manager-fill-and-joints = Заливка и стыки
style-manager-fill-enabled = Заливка включена
style-manager-first-segment-angle = Угол первого сегмента
style-manager-fixed-height = Фиксированная высота
style-manager-flow-direction = Направление заполнения
style-manager-font-file = Файл шрифта
style-manager-fonts = Шрифты
style-manager-format-string = Строка формата
style-manager-header = Заголовок столбцов
style-manager-header-row = Строка заголовков
style-manager-horizontal-margin = Горизонтальное поле
style-manager-inner-arcs = Внутренние дуги
style-manager-inside-horizontal = Внутренняя горизонтальная
style-manager-inside-vertical = Внутренняя вертикальная
style-manager-leader-draw-order = Порядок отрисовки выноски
style-manager-leader-note = Примечание выноски
style-manager-left-attachment = Левое присоединение
style-manager-line-color = Цвет линии
style-manager-line-type = Тип линии
style-manager-line-weight = Вес линии
style-manager-maximum-leader-points = Максимум точек выноски
style-manager-multileader-draw-order = Порядок отрисовки мультивыноски
style-manager-no-preview = Нет предпросмотра
style-manager-oblique-angle = Угол наклона
style-manager-outer-arc = Внешняя дуга
style-manager-paper-height = Высота на бумаге
style-manager-paper-text-height = Высота текста на бумаге
style-manager-path-type = Тип траектории
style-manager-right-attachment = Правое присоединение
style-manager-scale-factor = Масштабный коэффициент
style-manager-second-segment-angle = Угол второго сегмента
style-manager-size-and-effects = Размер и эффекты
style-manager-start-caps = Начальные торцы
style-manager-stroke-fonts = Штриховые шрифты
style-manager-suppress-header-row = Скрыть строку заголовков
style-manager-suppress-title-row = Скрыть строку названия
style-manager-system-font = Системный шрифт
style-manager-system-fonts = Системные шрифты
style-manager-text-alignment = Выравнивание текста
style-manager-text-angle = Угол текста
style-manager-title-row = Строка названия
style-manager-top-attachment = Верхнее присоединение
style-manager-unit-type = Тип единиц
style-manager-vertical-margin = Вертикальное поле
style-manager-font-family-placeholder = семейство шрифтов…
style-manager-bottom-to-top = Снизу вверх
style-manager-arrowhead-first = Сначала стрелка
style-manager-content-first = Сначала содержимое
style-manager-leader-first = Сначала выноска
style-manager-straight = Прямая
style-manager-parallel-last-segment = Параллельно последнему сегменту
style-manager-top-of-top-line = Верх верхней строки
style-manager-middle-of-top-line = Середина верхней строки
style-manager-middle-of-text = Середина текста
style-manager-middle-of-bottom-line = Середина нижней строки
style-manager-bottom-of-bottom-line = Низ нижней строки
style-manager-bottom-line = Нижняя строка
style-manager-underline-bottom-line = Подчеркнуть нижнюю строку
style-manager-underline-top-line = Подчеркнуть верхнюю строку
style-manager-underline-all-text = Подчеркнуть весь текст
style-manager-center-of-text = Центр текста
style-manager-center-of-text-overline = Центр текста с надчёркиванием
style-manager-block-extents = Габариты блока
style-manager-base-point = Базовая точка

View file

@ -3118,3 +3118,185 @@ catalog-mod-shortcuts-4c54bbb78e = ── __ocs_arg_MOD__ Kısayolları ──
catalog-custom-overrides-shortcuts-set-07be563dec = ── Özel Geçersiz Kılmalar (SHORTCUTS SET) ───────────── catalog-custom-overrides-shortcuts-set-07be563dec = ── Özel Geçersiz Kılmalar (SHORTCUTS SET) ─────────────
catalog-function-keys-b5a2df24e9 = ── İşlev Tuşları ─────────────────────────────────────── catalog-function-keys-b5a2df24e9 = ── İşlev Tuşları ───────────────────────────────────────
catalog-other-keys-a882812cf2 = ── Diğer Tuşlar ──────────────────────────────────────── catalog-other-keys-a882812cf2 = ── Diğer Tuşlar ────────────────────────────────────────
dimstyle-keep-text-between-extension-lines = Metni her zaman uzatma çizgileri arasında tut
dimstyle-angular-dimensions = Açısal ölçüler
dimstyle-arc-length-symbol = Yay uzunluğu simgesi
dimstyle-arrowheads = Ok uçları
dimstyle-available-style = Kullanılabilir stil
dimstyle-baseline-spacing = Taban çizgisi aralığı
dimstyle-basic = Temel
dimstyle-both-arrowheads = Her iki ok ucu
dimstyle-center-mark = Merkez işareti
dimstyle-center-mark-size = Merkez işareti boyutu
dimstyle-center-marks = Merkez işaretleri
dimstyle-centerlines = Merkez çizgileri
dimstyle-compare-with = Karşılaştır
dimstyle-current-style = Geçerli stil
dimstyle-deviation = Sapma
dimstyle-different-sections = Farklı bölümler
dimstyle-dimension-line-color = Ölçü çizgisi rengi
dimstyle-dimension-linetype = Ölçü çizgisi türü
dimstyle-dimension-lineweight = Ölçü çizgisi kalınlığı
dimstyle-display-alternate-units = Alternatif birimleri göster
dimstyle-draw-dimension-line-between-extension-lines = Uzatma çizgileri arasına ölçü çizgisi çiz
dimstyle-extend-beyond-dimension-line = Ölçü çizgisini aşma
dimstyle-extend-beyond-ticks = İşaretleri aşma
dimstyle-extension-line-color = Uzatma çizgisi rengi
dimstyle-extension-lineweight = Uzatma çizgisi kalınlığı
dimstyle-feet-and-inches = Fit ve inç
dimstyle-fine-tuning = İnce ayar
dimstyle-first-arrowhead = Birinci ok ucu
dimstyle-first-extension-linetype = Birinci uzatma çizgisi türü
dimstyle-fit-options = Sığdırma seçenekleri
dimstyle-fixed-length = Sabit uzunluk
dimstyle-fraction-format = Kesir biçimi
dimstyle-height-scale = Yükseklik ölçeği
dimstyle-horizontal-placement = Yatay yerleşim
dimstyle-keep-text-horizontal-inside = İçerideyken metni yatay tut
dimstyle-keep-text-horizontal-outside = Dışarıdayken metni yatay tut
dimstyle-leader-arrowhead = Kılavuz ok ucu
dimstyle-limits = Sınırlar
dimstyle-linear-dimensions = Doğrusal ölçüler
dimstyle-lower-value = Alt değer
dimstyle-measurement-scale = Ölçüm ölçeği
dimstyle-measurement-template = Ölçüm şablonu
dimstyle-method = Yöntem
dimstyle-multiplier = Çarpan
dimstyle-no-differences = Fark yok
dimstyle-not-applicable = Uygulanamaz
dimstyle-offset-from-measured-point = Ölçülen noktadan uzaklık
dimstyle-place-text-manually = Ölçü oluştururken metni elle yerleştir
dimstyle-radius-jog-angle = Yarıçap kırılma açısı (°)
dimstyle-read-left-to-right = Soldan sağa oku
dimstyle-referenced-read-only = Başvurulan stil · Salt okunur
dimstyle-round-off = Yuvarlama
dimstyle-second-arrowhead = İkinci ok ucu
dimstyle-second-extension-linetype = İkinci uzatma çizgisi türü
dimstyle-show-zero-feet-and-inches = Sıfır fit ve sıfır inçi göster
dimstyle-show-zero-feet-suppress-zero-inches = Sıfır fiti göster; sıfır inçi gizle
dimstyle-suppress-arrows-when-no-fit = Sığmadığında okları gizle
dimstyle-suppress-first-dimension-line = Birinci ölçü çizgisini gizle
dimstyle-suppress-first-extension-line = Birinci uzatma çizgisini gizle
dimstyle-suppress-second-dimension-line = İkinci ölçü çizgisini gizle
dimstyle-suppress-second-extension-line = İkinci uzatma çizgisini gizle
dimstyle-suppress-zero-feet-and-inches = Sıfır fit ve sıfır inçi gizle
dimstyle-suppress-zero-feet-show-zero-inches = Sıfır fiti gizle; sıfır inçi göster
dimstyle-symbols-and-arrows = Simgeler ve Oklar
dimstyle-symmetrical = Simetrik
dimstyle-text-background = Metin arka planı
dimstyle-text-gap = Metin boşluğu
dimstyle-fixed-text-height-note = Seçili metin stili yüksekliği __ocs_arg_height__ olarak sabitler.
dimstyle-tick-size = İşaret boyutu
dimstyle-tolerance-zero-suppression = Tolerans sıfırlarını gizleme
dimstyle-unit-format = Birim biçimi
dimstyle-upper-value = Üst değer
dimstyle-measurement-template-help = Ölçülen değer için <> kullanın; önündeki ve arkasındaki metin, ön ek ve son ek olur.
dimstyle-use-different-arrowheads = Birinci ve ikinci ok uçlarını farklı kullan
dimstyle-use-fixed-length-extension-lines = Sabit uzunlukta uzatma çizgileri kullan
dimstyle-used-in-drawing = Çizimde kullanılıyor
dimstyle-vertical-offset = Dikey uzaklık
dimstyle-vertical-placement = Dikey yerleşim
dimstyle-vertical-position = Dikey konum
dimstyle-when-space-is-limited = Alan yetersiz olduğunda
dimstyle-when-text-is-moved = Metin taşındığında
dimstyle-zero-suppression = Sıfırları gizleme
dimstyle-add-a-leader = Kılavuz ekle
dimstyle-automatic = Otomatik
dimstyle-by-block = Bloğa göre
dimstyle-comma = Virgül
dimstyle-degrees-minutes-seconds = Derece, dakika, saniye
dimstyle-keep-dimension-line-with-text = Ölçü çizgisini metinle birlikte taşı
dimstyle-leading-and-trailing = Baştaki ve sondaki
dimstyle-move-arrows-outside-first = Önce okları dışarı taşı
dimstyle-move-text-and-arrows-outside = Metni ve okları dışarı taşı
dimstyle-move-text-freely = Metni serbestçe taşı
dimstyle-move-text-outside-first = Önce metni dışarı taşı
dimstyle-period = Nokta
dimstyle-space = Boşluk
style-manager-alignment-spacing = Hizalama aralığı
style-manager-always-left-align = Her zaman sola hizala
style-manager-attachment-direction = Bağlantı yönü
style-manager-background-fill-enabled = Arka plan dolgusu etkin
style-manager-big-font = Büyük yazı tipi
style-manager-block-scale-x = Blok X ölçeği
style-manager-block-scale-y = Blok Y ölçeği
style-manager-block-scale-z = Blok Z ölçeği
style-manager-borders = Kenarlar
style-manager-bottom-attachment = Alt bağlantı
style-manager-break-gap-size = Kesinti boşluğu boyutu
style-manager-caps-and-fill = Kapaklar ve Dolgu
style-manager-content-type = İçerik türü
style-manager-data-row = Veri Satırı
style-manager-data-type = Veri türü
style-manager-default-text = Varsayılan metin
style-manager-different = Farklı
style-manager-display-joints = Birleşimleri göster
style-manager-drawing-height = Çizim yüksekliği
style-manager-drawing-scale = Çizim ölçeği
style-manager-effects = Etkiler
style-manager-elements = Elemanlar
style-manager-end-caps = Bitiş kapakları
style-manager-fill-and-joints = Dolgu ve birleşimler
style-manager-fill-enabled = Dolgu etkin
style-manager-first-segment-angle = Birinci segment açısı
style-manager-fixed-height = Sabit yükseklik
style-manager-flow-direction = Akış yönü
style-manager-font-file = Yazı tipi dosyası
style-manager-fonts = Yazı tipleri
style-manager-format-string = Biçim dizgesi
style-manager-header = Sütun başlığı
style-manager-header-row = Sütun Başlığı Satırı
style-manager-horizontal-margin = Yatay kenar boşluğu
style-manager-inner-arcs = İç yaylar
style-manager-inside-horizontal = Yatay iç kenar
style-manager-inside-vertical = Dikey iç kenar
style-manager-leader-draw-order = Lider çizim sırası
style-manager-leader-note = Lider notu
style-manager-left-attachment = Sol bağlantı
style-manager-line-color = Çizgi rengi
style-manager-line-type = Çizgi türü
style-manager-line-weight = Çizgi kalınlığı
style-manager-maximum-leader-points = En fazla lider noktası
style-manager-multileader-draw-order = Çoklu lider çizim sırası
style-manager-no-preview = Önizleme yok
style-manager-oblique-angle = Eğiklik açısı
style-manager-outer-arc = Dış yay
style-manager-paper-height = Kağıt yüksekliği
style-manager-paper-text-height = Kağıt metin yüksekliği
style-manager-path-type = Yol türü
style-manager-right-attachment = Sağ bağlantı
style-manager-scale-factor = Ölçek çarpanı
style-manager-second-segment-angle = İkinci segment açısı
style-manager-size-and-effects = Boyut ve Etkiler
style-manager-start-caps = Başlangıç kapakları
style-manager-stroke-fonts = Çizgi yazı tipleri
style-manager-suppress-header-row = Sütun başlığı satırını gizle
style-manager-suppress-title-row = Tablo başlığı satırını gizle
style-manager-system-font = Sistem yazı tipi
style-manager-system-fonts = Sistem yazı tipleri
style-manager-text-alignment = Metin hizalaması
style-manager-text-angle = Metin açısı
style-manager-title-row = Tablo Başlığı Satırı
style-manager-top-attachment = Üst bağlantı
style-manager-unit-type = Birim türü
style-manager-vertical-margin = Dikey kenar boşluğu
style-manager-font-family-placeholder = yazı tipi ailesi…
style-manager-bottom-to-top = Aşağıdan yukarıya
style-manager-arrowhead-first = Önce ok ucu
style-manager-content-first = Önce içerik
style-manager-leader-first = Önce lider
style-manager-straight = Düz
style-manager-parallel-last-segment = Son segmente paralel
style-manager-top-of-top-line = Üst satırın üstü
style-manager-middle-of-top-line = Üst satırın ortası
style-manager-middle-of-text = Metnin ortası
style-manager-middle-of-bottom-line = Alt satırın ortası
style-manager-bottom-of-bottom-line = Alt satırın altı
style-manager-bottom-line = Alt satır
style-manager-underline-bottom-line = Alt satırın altını çiz
style-manager-underline-top-line = Üst satırın altını çiz
style-manager-underline-all-text = Tüm metnin altını çiz
style-manager-center-of-text = Metnin merkezi
style-manager-center-of-text-overline = Üst çizgili metin merkezi
style-manager-block-extents = Blok sınırları
style-manager-base-point = Taban noktası

View file

@ -3180,3 +3180,185 @@ catalog-mod-shortcuts-4c54bbb78e = ── __ocs_arg_MOD__ 快捷键 ────
catalog-custom-overrides-shortcuts-set-07be563dec = ── 自定义覆盖 (SHORTCUTS SET) ────────────────── catalog-custom-overrides-shortcuts-set-07be563dec = ── 自定义覆盖 (SHORTCUTS SET) ──────────────────
catalog-function-keys-b5a2df24e9 = ── 功能键 ────────────────────────────────────── catalog-function-keys-b5a2df24e9 = ── 功能键 ──────────────────────────────────────
catalog-other-keys-a882812cf2 = ── 其他按键 ────────────────────────────────────────── catalog-other-keys-a882812cf2 = ── 其他按键 ──────────────────────────────────────────
dimstyle-keep-text-between-extension-lines = 始终将文字置于尺寸界线之间
dimstyle-angular-dimensions = 角度标注
dimstyle-arc-length-symbol = 弧长符号
dimstyle-arrowheads = 箭头
dimstyle-available-style = 可用样式
dimstyle-baseline-spacing = 基线间距
dimstyle-basic = 基本尺寸
dimstyle-both-arrowheads = 两端箭头
dimstyle-center-mark = 圆心标记
dimstyle-center-mark-size = 圆心标记大小
dimstyle-center-marks = 圆心标记
dimstyle-centerlines = 中心线
dimstyle-compare-with = 比较对象
dimstyle-current-style = 当前样式
dimstyle-deviation = 偏差
dimstyle-different-sections = 存在差异的分区
dimstyle-dimension-line-color = 尺寸线颜色
dimstyle-dimension-linetype = 尺寸线线型
dimstyle-dimension-lineweight = 尺寸线线宽
dimstyle-display-alternate-units = 显示换算单位
dimstyle-draw-dimension-line-between-extension-lines = 在尺寸界线之间绘制尺寸线
dimstyle-extend-beyond-dimension-line = 超出尺寸线
dimstyle-extend-beyond-ticks = 超出斜线
dimstyle-extension-line-color = 尺寸界线颜色
dimstyle-extension-lineweight = 尺寸界线线宽
dimstyle-feet-and-inches = 英尺和英寸
dimstyle-fine-tuning = 精细调整
dimstyle-first-arrowhead = 第一个箭头
dimstyle-first-extension-linetype = 第一条尺寸界线线型
dimstyle-fit-options = 调整选项
dimstyle-fixed-length = 固定长度
dimstyle-fraction-format = 分数格式
dimstyle-height-scale = 高度比例
dimstyle-horizontal-placement = 水平位置
dimstyle-keep-text-horizontal-inside = 位于内部时保持文字水平
dimstyle-keep-text-horizontal-outside = 位于外部时保持文字水平
dimstyle-leader-arrowhead = 引线箭头
dimstyle-limits = 极限尺寸
dimstyle-linear-dimensions = 线性标注
dimstyle-lower-value = 下偏差
dimstyle-measurement-scale = 测量比例
dimstyle-measurement-template = 测量值模板
dimstyle-method = 方式
dimstyle-multiplier = 换算系数
dimstyle-no-differences = 无差异
dimstyle-not-applicable = 不适用
dimstyle-offset-from-measured-point = 与测量点的偏移
dimstyle-place-text-manually = 创建标注时手动放置文字
dimstyle-radius-jog-angle = 半径折弯角度 (°)
dimstyle-read-left-to-right = 从左向右阅读
dimstyle-referenced-read-only = 参照样式 · 只读
dimstyle-round-off = 舍入值
dimstyle-second-arrowhead = 第二个箭头
dimstyle-second-extension-linetype = 第二条尺寸界线线型
dimstyle-show-zero-feet-and-inches = 显示零英尺和零英寸
dimstyle-show-zero-feet-suppress-zero-inches = 显示零英尺;抑制零英寸
dimstyle-suppress-arrows-when-no-fit = 箭头无法容纳时将其隐藏
dimstyle-suppress-first-dimension-line = 隐藏第一条尺寸线
dimstyle-suppress-first-extension-line = 隐藏第一条尺寸界线
dimstyle-suppress-second-dimension-line = 隐藏第二条尺寸线
dimstyle-suppress-second-extension-line = 隐藏第二条尺寸界线
dimstyle-suppress-zero-feet-and-inches = 抑制零英尺和零英寸
dimstyle-suppress-zero-feet-show-zero-inches = 抑制零英尺;显示零英寸
dimstyle-symbols-and-arrows = 符号和箭头
dimstyle-symmetrical = 对称
dimstyle-text-background = 文字背景
dimstyle-text-gap = 文字间距
dimstyle-fixed-text-height-note = 所选文字样式将高度固定为 __ocs_arg_height__。
dimstyle-tick-size = 斜线大小
dimstyle-tolerance-zero-suppression = 公差零值抑制
dimstyle-unit-format = 单位格式
dimstyle-upper-value = 上偏差
dimstyle-measurement-template-help = 使用 <> 表示测量值;其前后的文字将分别成为前缀和后缀。
dimstyle-use-different-arrowheads = 两端使用不同箭头
dimstyle-use-fixed-length-extension-lines = 使用固定长度的尺寸界线
dimstyle-used-in-drawing = 已在图形中使用
dimstyle-vertical-offset = 垂直偏移
dimstyle-vertical-placement = 垂直位置
dimstyle-vertical-position = 垂直定位
dimstyle-when-space-is-limited = 空间不足时
dimstyle-when-text-is-moved = 移动文字时
dimstyle-zero-suppression = 零值抑制
dimstyle-add-a-leader = 添加引线
dimstyle-automatic = 自动
dimstyle-by-block = 随块
dimstyle-comma = 逗号
dimstyle-degrees-minutes-seconds = 度、分、秒
dimstyle-keep-dimension-line-with-text = 尺寸线随文字移动
dimstyle-leading-and-trailing = 前导和后续
dimstyle-move-arrows-outside-first = 优先将箭头移到外部
dimstyle-move-text-and-arrows-outside = 将文字和箭头移到外部
dimstyle-move-text-freely = 自由移动文字
dimstyle-move-text-outside-first = 优先将文字移到外部
dimstyle-period = 句点
dimstyle-space = 空格
style-manager-alignment-spacing = 对齐间距
style-manager-always-left-align = 始终左对齐
style-manager-attachment-direction = 附着方向
style-manager-background-fill-enabled = 启用背景填充
style-manager-big-font = 大字体
style-manager-block-scale-x = 块 X 比例
style-manager-block-scale-y = 块 Y 比例
style-manager-block-scale-z = 块 Z 比例
style-manager-borders = 边框
style-manager-bottom-attachment = 底部附着
style-manager-break-gap-size = 打断间隙大小
style-manager-caps-and-fill = 端盖和填充
style-manager-content-type = 内容类型
style-manager-data-row = 数据行
style-manager-data-type = 数据类型
style-manager-default-text = 默认文字
style-manager-different = 不同
style-manager-display-joints = 显示接合处
style-manager-drawing-height = 图形高度
style-manager-drawing-scale = 图形比例
style-manager-effects = 效果
style-manager-elements = 元素
style-manager-end-caps = 终点端盖
style-manager-fill-and-joints = 填充和接合处
style-manager-fill-enabled = 启用填充
style-manager-first-segment-angle = 第一段角度
style-manager-fixed-height = 固定高度
style-manager-flow-direction = 排列方向
style-manager-font-file = 字体文件
style-manager-fonts = 字体
style-manager-format-string = 格式字符串
style-manager-header = 列标题
style-manager-header-row = 列标题行
style-manager-horizontal-margin = 水平边距
style-manager-inner-arcs = 内弧
style-manager-inside-horizontal = 内部水平
style-manager-inside-vertical = 内部垂直
style-manager-leader-draw-order = 引线绘制顺序
style-manager-leader-note = 引线注释
style-manager-left-attachment = 左侧附着
style-manager-line-color = 线颜色
style-manager-line-type = 线型
style-manager-line-weight = 线宽
style-manager-maximum-leader-points = 最大引线点数
style-manager-multileader-draw-order = 多重引线绘制顺序
style-manager-no-preview = 无预览
style-manager-oblique-angle = 倾斜角
style-manager-outer-arc = 外弧
style-manager-paper-height = 图纸高度
style-manager-paper-text-height = 图纸文字高度
style-manager-path-type = 路径类型
style-manager-right-attachment = 右侧附着
style-manager-scale-factor = 比例因子
style-manager-second-segment-angle = 第二段角度
style-manager-size-and-effects = 大小和效果
style-manager-start-caps = 起点端盖
style-manager-stroke-fonts = 线性字体
style-manager-suppress-header-row = 隐藏列标题行
style-manager-suppress-title-row = 隐藏标题行
style-manager-system-font = 系统字体
style-manager-system-fonts = 系统字体
style-manager-text-alignment = 文字对齐
style-manager-text-angle = 文字角度
style-manager-title-row = 标题行
style-manager-top-attachment = 顶部附着
style-manager-unit-type = 单位类型
style-manager-vertical-margin = 垂直边距
style-manager-font-family-placeholder = 字体族…
style-manager-bottom-to-top = 从下到上
style-manager-arrowhead-first = 先绘制箭头
style-manager-content-first = 先绘制内容
style-manager-leader-first = 先绘制引线
style-manager-straight = 直线
style-manager-parallel-last-segment = 平行于最后一段
style-manager-top-of-top-line = 顶行顶部
style-manager-middle-of-top-line = 顶行中部
style-manager-middle-of-text = 文字中部
style-manager-middle-of-bottom-line = 底行中部
style-manager-bottom-of-bottom-line = 底行底部
style-manager-bottom-line = 底行
style-manager-underline-bottom-line = 底行下划线
style-manager-underline-top-line = 顶行下划线
style-manager-underline-all-text = 所有文字下划线
style-manager-center-of-text = 文字中心
style-manager-center-of-text-overline = 带上划线的文字中心
style-manager-block-extents = 块范围
style-manager-base-point = 基点

View file

@ -670,9 +670,18 @@ pub(super) struct OpenCADStudio {
// ── MLineStyle Dialog ───────────────────────────────────────────────── // ── MLineStyle Dialog ─────────────────────────────────────────────────
mlstyle_selected: String, mlstyle_selected: String,
mlstyle_tab: u8,
mlstyle_compare: String,
mln_description: String,
mln_start_angle: String,
mln_end_angle: String,
mln_fill_color: String,
mln_elements: Vec<[String; 3]>,
// ── MLeaderStyle Dialog ─────────────────────────────────────────────── // ── MLeaderStyle Dialog ───────────────────────────────────────────────
mleaderstyle_selected: String, mleaderstyle_selected: String,
mleaderstyle_tab: u8,
mleaderstyle_compare: String,
/// Colour field whose expanded palette is open (line/text/block). /// Colour field whose expanded palette is open (line/text/block).
mls_color_open: Option<&'static str>, mls_color_open: Option<&'static str>,
mls_landing_distance: String, mls_landing_distance: String,
@ -697,6 +706,8 @@ pub(super) struct OpenCADStudio {
// ── TableStyle Dialog ───────────────────────────────────────────────── // ── TableStyle Dialog ─────────────────────────────────────────────────
tablestyle_selected: String, tablestyle_selected: String,
tablestyle_tab: u8,
tablestyle_compare: String,
/// Edit buffers for the table style's general margins. /// Edit buffers for the table style's general margins.
ts_hmargin: String, ts_hmargin: String,
ts_vmargin: String, ts_vmargin: String,
@ -732,6 +743,8 @@ pub(super) struct OpenCADStudio {
// ── TextStyle Font Browser ──────────────────────────────────────────── // ── TextStyle Font Browser ────────────────────────────────────────────
textstyle_selected: String, textstyle_selected: String,
textstyle_tab: u8,
textstyle_compare: String,
/// Edit buffer for font file name. /// Edit buffer for font file name.
textstyle_font: String, textstyle_font: String,
/// Edit buffer for width factor. /// Edit buffer for width factor.
@ -868,8 +881,10 @@ pub(super) struct OpenCADStudio {
dimstyle_selected: String, dimstyle_selected: String,
/// Which colour field currently has its expanded palette open (if any). /// Which colour field currently has its expanded palette open (if any).
ds_color_open: Option<DsField>, ds_color_open: Option<DsField>,
/// Active tab: 0=Lines, 1=Arrows, 2=Text, 3=Scale/Units, 4=Tolerances. /// Active property group in the dimension style manager.
dimstyle_tab: u8, dimstyle_tab: u8,
/// Style used by the manager's comparison summary.
dimstyle_compare: String,
// Edit buffers (strings while typing): // Edit buffers (strings while typing):
ds_dimdle: String, ds_dimdle: String,
ds_dimdli: String, ds_dimdli: String,
@ -2438,6 +2453,8 @@ pub enum Message {
#[allow(dead_code)] #[allow(dead_code)]
TextStyleDialogClose, TextStyleDialogClose,
TextStyleDialogSelect(String), TextStyleDialogSelect(String),
TextStyleDialogTab(u8),
TextStyleDialogCompare(String),
TextStyleDialogSetCurrent, TextStyleDialogSetCurrent,
TextStyleDialogNew, TextStyleDialogNew,
TextStyleDialogCopy, TextStyleDialogCopy,
@ -2465,6 +2482,8 @@ pub enum Message {
#[allow(dead_code)] #[allow(dead_code)]
TableStyleDialogClose, TableStyleDialogClose,
TableStyleDialogSelect(String), TableStyleDialogSelect(String),
TableStyleDialogTab(u8),
TableStyleDialogCompare(String),
TableStyleDialogNew, TableStyleDialogNew,
TableStyleDialogCopy, TableStyleDialogCopy,
TableStyleDialogDelete, TableStyleDialogDelete,
@ -2523,16 +2542,32 @@ pub enum Message {
#[allow(dead_code)] #[allow(dead_code)]
MlStyleDialogClose, MlStyleDialogClose,
MlStyleDialogSelect(String), MlStyleDialogSelect(String),
MlStyleDialogTab(u8),
MlStyleDialogCompare(String),
MlStyleDialogSetCurrent, MlStyleDialogSetCurrent,
MlStyleApply, MlStyleApply,
MlStyleDialogNew, MlStyleDialogNew,
MlStyleDialogCopy, MlStyleDialogCopy,
MlStyleDialogDelete, MlStyleDialogDelete,
MlStyleEdit {
field: &'static str,
value: String,
},
MlStyleToggle(&'static str),
MlStyleElementEdit {
index: usize,
field: &'static str,
value: String,
},
MlStyleElementAdd,
MlStyleElementDelete(usize),
// ── MLeaderStyle Dialog ─────────────────────────────────────────────── // ── MLeaderStyle Dialog ───────────────────────────────────────────────
MLeaderStyleDialogOpen, MLeaderStyleDialogOpen,
#[allow(dead_code)] #[allow(dead_code)]
MLeaderStyleDialogClose, MLeaderStyleDialogClose,
MLeaderStyleDialogSelect(String), MLeaderStyleDialogSelect(String),
MLeaderStyleDialogTab(u8),
MLeaderStyleDialogCompare(String),
MLeaderStyleDialogSetCurrent, MLeaderStyleDialogSetCurrent,
MLeaderStyleDialogNew, MLeaderStyleDialogNew,
MLeaderStyleDialogCopy, MLeaderStyleDialogCopy,
@ -2565,6 +2600,8 @@ pub enum Message {
DimStyleDialogSelect(String), DimStyleDialogSelect(String),
/// Switch the active tab. /// Switch the active tab.
DimStyleDialogTab(u8), DimStyleDialogTab(u8),
/// Change the style used by the comparison summary.
DimStyleDialogCompare(String),
/// Create a new empty style (prompts via command line). /// Create a new empty style (prompts via command line).
DimStyleDialogNew, DimStyleDialogNew,
DimStyleDialogCopy, DimStyleDialogCopy,
@ -2575,6 +2612,14 @@ pub enum Message {
// Field edit messages: // Field edit messages:
DsEdit(DsField, String), DsEdit(DsField, String),
DsToggle(DsField), DsToggle(DsField),
/// Select the mutually-exclusive tolerance presentation.
DsToleranceMode(String),
/// Change the feet/inches mode in a zero-suppression bit field.
DsZeroBase(DsField, i16),
/// Toggle leading/trailing suppression in a zero-suppression bit field.
DsZeroFlag(DsField, i16),
/// Select no center mark, a mark, or centerlines.
DsCenterMarkMode(String),
/// Toggle the expanded colour palette for a DimStyle colour field. /// Toggle the expanded colour palette for a DimStyle colour field.
DsColorMore(DsField), DsColorMore(DsField),
/// Open the `iced_aw` colour picker for a field and its current colour. /// Open the `iced_aw` colour picker for a field and its current colour.
@ -2887,6 +2932,8 @@ impl OpenCADStudio {
style_rename_buf: String::new(), style_rename_buf: String::new(),
style_stage: None, style_stage: None,
textstyle_selected: "Standard".to_string(), textstyle_selected: "Standard".to_string(),
textstyle_tab: 0,
textstyle_compare: String::new(),
textstyle_font: String::new(), textstyle_font: String::new(),
textstyle_width: "1.0".to_string(), textstyle_width: "1.0".to_string(),
textstyle_oblique: "0.0".to_string(), textstyle_oblique: "0.0".to_string(),
@ -2895,6 +2942,8 @@ impl OpenCADStudio {
textstyle_ttf: String::new(), textstyle_ttf: String::new(),
// TableStyle dialog // TableStyle dialog
tablestyle_selected: "Standard".to_string(), tablestyle_selected: "Standard".to_string(),
tablestyle_tab: 0,
tablestyle_compare: String::new(),
ts_hmargin: "1.5".to_string(), ts_hmargin: "1.5".to_string(),
ts_vmargin: "1.5".to_string(), ts_vmargin: "1.5".to_string(),
ts_description: String::new(), ts_description: String::new(),
@ -2911,8 +2960,17 @@ impl OpenCADStudio {
ts_border_spacing: Default::default(), ts_border_spacing: Default::default(),
// MLineStyle dialog // MLineStyle dialog
mlstyle_selected: "Standard".to_string(), mlstyle_selected: "Standard".to_string(),
mlstyle_tab: 0,
mlstyle_compare: String::new(),
mln_description: String::new(),
mln_start_angle: "90".to_string(),
mln_end_angle: "90".to_string(),
mln_fill_color: "256".to_string(),
mln_elements: Vec::new(),
// MLeaderStyle dialog // MLeaderStyle dialog
mleaderstyle_selected: "Standard".to_string(), mleaderstyle_selected: "Standard".to_string(),
mleaderstyle_tab: 0,
mleaderstyle_compare: String::new(),
mls_color_open: None, mls_color_open: None,
mls_landing_distance: String::new(), mls_landing_distance: String::new(),
mls_landing_gap: String::new(), mls_landing_gap: String::new(),
@ -2937,6 +2995,7 @@ impl OpenCADStudio {
dimstyle_selected: "Standard".to_string(), dimstyle_selected: "Standard".to_string(),
ds_color_open: None, ds_color_open: None,
dimstyle_tab: 0, dimstyle_tab: 0,
dimstyle_compare: String::new(),
ds_dimdle: "0".to_string(), ds_dimdle: "0".to_string(),
ds_dimdli: "3.75".to_string(), ds_dimdli: "3.75".to_string(),
ds_dimgap: "0.625".to_string(), ds_dimgap: "0.625".to_string(),

View file

@ -301,7 +301,7 @@ impl OpenCADStudio {
} }
} }
fn style_in_use(&self, kind: StyleKind, name: &str) -> bool { pub(super) fn style_in_use(&self, kind: StyleKind, name: &str) -> bool {
use acadrust::entities::EntityType; use acadrust::entities::EntityType;
let i = self.active_tab; let i = self.active_tab;
@ -613,6 +613,20 @@ impl OpenCADStudio {
pub(super) fn style_delete(&mut self, kind: StyleKind) { pub(super) fn style_delete(&mut self, kind: StyleKind) {
let name = self.style_selected(kind); let name = self.style_selected(kind);
if matches!(kind, StyleKind::Dim)
&& self.tabs[self.active_tab]
.scene
.document
.dim_styles
.get(&name)
.is_some_and(|style| {
style.xref_reference || style.xref_dependent || !style.xref_handle.is_null()
})
{
self.command_line
.push_error(crate::t!("Referenced styles are read only.").as_ref());
return;
}
if name.eq_ignore_ascii_case("Standard") { if name.eq_ignore_ascii_case("Standard") {
self.command_line self.command_line
.push_error(crate::t!("Cannot delete the Standard style.").as_ref()); .push_error(crate::t!("Cannot delete the Standard style.").as_ref());
@ -657,6 +671,20 @@ impl OpenCADStudio {
if new.is_empty() || new.eq_ignore_ascii_case(&old) { if new.is_empty() || new.eq_ignore_ascii_case(&old) {
return; return;
} }
if matches!(kind, StyleKind::Dim)
&& self.tabs[self.active_tab]
.scene
.document
.dim_styles
.get(&old)
.is_some_and(|style| {
style.xref_reference || style.xref_dependent || !style.xref_handle.is_null()
})
{
self.command_line
.push_error(crate::t!("Referenced styles are read only.").as_ref());
return;
}
if old.eq_ignore_ascii_case("Standard") { if old.eq_ignore_ascii_case("Standard") {
self.command_line self.command_line
.push_error(crate::t!("Cannot rename the Standard style.").as_ref()); .push_error(crate::t!("Cannot rename the Standard style.").as_ref());

View file

@ -5408,11 +5408,25 @@ impl OpenCADStudio {
self.load_textstyle_bufs(i); self.load_textstyle_bufs(i);
Task::none() Task::none()
} }
Message::TextStyleDialogTab(tab) => {
self.textstyle_tab = tab;
Task::none()
}
Message::TextStyleDialogCompare(name) => {
self.textstyle_compare = name;
Task::none()
}
Message::TextStyleDialogSetCurrent => { Message::TextStyleDialogSetCurrent => {
// Staged: persists on Apply. // Staged: persists on Apply.
let i = self.active_tab; let i = self.active_tab;
let name = self.textstyle_selected.clone(); let name = self.textstyle_selected.clone();
if self.tabs[i].scene.document.text_styles.get(&name).is_some() { if self.tabs[i]
.scene
.document
.text_styles
.get(&name)
.is_some_and(|style| !style.xref_dependent)
{
self.tabs[i].scene.document.header.current_text_style_name = name.clone(); self.tabs[i].scene.document.header.current_text_style_name = name.clone();
self.sync_ribbon_styles(); self.sync_ribbon_styles();
self.command_line self.command_line
@ -5468,9 +5482,13 @@ impl OpenCADStudio {
let i = self.active_tab; let i = self.active_tab;
let name = self.textstyle_selected.clone(); let name = self.textstyle_selected.clone();
if let Some(s) = self.tabs[i].scene.document.text_styles.get_mut(&name) { if let Some(s) = self.tabs[i].scene.document.text_styles.get_mut(&name) {
if s.xref_dependent {
return Task::none();
}
match field { match field {
"backward" => s.flags.backward = !s.flags.backward, "backward" => s.flags.backward = !s.flags.backward,
"upside_down" => s.flags.upside_down = !s.flags.upside_down, "upside_down" => s.flags.upside_down = !s.flags.upside_down,
"vertical" => s.is_vertical = !s.is_vertical,
"annotative" => s.annotative = !s.annotative, "annotative" => s.annotative = !s.annotative,
_ => {} _ => {}
} }
@ -5481,8 +5499,17 @@ impl OpenCADStudio {
Message::TextStyleFontPick(font_file) => { Message::TextStyleFontPick(font_file) => {
// Staged: update the buffer + live style; persist on Apply. // Staged: update the buffer + live style; persist on Apply.
let i = self.active_tab; let i = self.active_tab;
self.textstyle_font = font_file.clone();
let name = self.textstyle_selected.clone(); let name = self.textstyle_selected.clone();
if self.tabs[i]
.scene
.document
.text_styles
.get(&name)
.is_some_and(|style| style.xref_dependent)
{
return Task::none();
}
self.textstyle_font = font_file.clone();
if let Some(s) = self.tabs[i].scene.document.text_styles.get_mut(&name) { if let Some(s) = self.tabs[i].scene.document.text_styles.get_mut(&name) {
s.font_file = font_file; s.font_file = font_file;
} }
@ -5516,12 +5543,23 @@ impl OpenCADStudio {
Task::none() Task::none()
} }
Message::TableStyleDialogSelect(name) => { Message::TableStyleDialogSelect(name) => {
for row in 0..3 {
let _ = self.on_table_style_cell_apply(row);
}
self.stage_tablestyle_bufs(); self.stage_tablestyle_bufs();
self.tablestyle_selected = name; self.tablestyle_selected = name;
let i = self.active_tab; let i = self.active_tab;
self.load_tablestyle_bufs(i); self.load_tablestyle_bufs(i);
Task::none() Task::none()
} }
Message::TableStyleDialogTab(tab) => {
self.tablestyle_tab = tab;
Task::none()
}
Message::TableStyleDialogCompare(name) => {
self.tablestyle_compare = name;
Task::none()
}
Message::TableStyleEdit { field, value } => { Message::TableStyleEdit { field, value } => {
match field { match field {
@ -5534,6 +5572,9 @@ impl OpenCADStudio {
} }
Message::TableStyleApply => { Message::TableStyleApply => {
for row in 0..3 {
let _ = self.on_table_style_cell_apply(row);
}
self.stage_tablestyle_bufs(); self.stage_tablestyle_bufs();
self.style_stage_commit(); self.style_stage_commit();
Task::none() Task::none()
@ -5728,7 +5769,18 @@ impl OpenCADStudio {
Task::none() Task::none()
} }
Message::MlStyleDialogSelect(name) => { Message::MlStyleDialogSelect(name) => {
self.stage_mlstyle_bufs();
self.mlstyle_selected = name; self.mlstyle_selected = name;
let i = self.active_tab;
self.load_mlstyle_bufs(i);
Task::none()
}
Message::MlStyleDialogTab(tab) => {
self.mlstyle_tab = tab;
Task::none()
}
Message::MlStyleDialogCompare(name) => {
self.mlstyle_compare = name;
Task::none() Task::none()
} }
Message::MlStyleDialogSetCurrent => { Message::MlStyleDialogSetCurrent => {
@ -5749,13 +5801,8 @@ impl OpenCADStudio {
} }
Task::none() Task::none()
} }
// Placeholder so the Multiline manager has the same Set Current +
// Apply pair as every other style manager. The editor is currently
// read-only, so there is nothing to apply yet — wire this up when
// editable MLineStyle properties land.
Message::MlStyleApply => { Message::MlStyleApply => {
// Multiline styles have no editable properties yet; Apply still self.stage_mlstyle_bufs();
// commits any staged structural / current-style changes.
self.style_stage_commit(); self.style_stage_commit();
Task::none() Task::none()
} }
@ -5771,6 +5818,66 @@ impl OpenCADStudio {
self.style_delete(crate::app::StyleKind::MLine); self.style_delete(crate::app::StyleKind::MLine);
Task::none() Task::none()
} }
Message::MlStyleEdit { field, value } => {
match field {
"description" => self.mln_description = value,
"start_angle" => self.mln_start_angle = value,
"end_angle" => self.mln_end_angle = value,
"fill_color" => self.mln_fill_color = value,
_ => {}
}
self.stage_mlstyle_bufs();
Task::none()
}
Message::MlStyleToggle(field) => {
let i = self.active_tab;
if let Some(style) = self.mlstyle_mut(i) {
match field {
"fill" => style.flags.fill_on = !style.flags.fill_on,
"joints" => style.flags.display_joints = !style.flags.display_joints,
"start_square" => style.flags.start_square_cap = !style.flags.start_square_cap,
"start_inner" => style.flags.start_inner_arcs_cap = !style.flags.start_inner_arcs_cap,
"start_round" => style.flags.start_round_cap = !style.flags.start_round_cap,
"end_square" => style.flags.end_square_cap = !style.flags.end_square_cap,
"end_inner" => style.flags.end_inner_arcs_cap = !style.flags.end_inner_arcs_cap,
"end_round" => style.flags.end_round_cap = !style.flags.end_round_cap,
_ => {}
}
}
Task::none()
}
Message::MlStyleElementEdit { index, field, value } => {
if let Some(element) = self.mln_elements.get_mut(index) {
match field {
"offset" => element[0] = value,
"color" => element[1] = value,
"linetype" => element[2] = value,
_ => {}
}
}
self.stage_mlstyle_bufs();
Task::none()
}
Message::MlStyleElementAdd => {
let i = self.active_tab;
if let Some(style) = self.mlstyle_mut(i) {
style
.elements
.push(acadrust::objects::MLineStyleElement::default());
}
self.load_mlstyle_bufs(i);
Task::none()
}
Message::MlStyleElementDelete(index) => {
let i = self.active_tab;
if let Some(style) = self.mlstyle_mut(i) {
if style.elements.len() > 1 && index < style.elements.len() {
style.elements.remove(index);
}
}
self.load_mlstyle_bufs(i);
Task::none()
}
// ── MLeaderStyle Dialog ─────────────────────────────────────────── // ── MLeaderStyle Dialog ───────────────────────────────────────────
Message::MLeaderStyleDialogOpen => self.on_mleader_style_dialog_open(), Message::MLeaderStyleDialogOpen => self.on_mleader_style_dialog_open(),
@ -5785,6 +5892,14 @@ impl OpenCADStudio {
self.load_mleaderstyle_bufs(i); self.load_mleaderstyle_bufs(i);
Task::none() Task::none()
} }
Message::MLeaderStyleDialogTab(tab) => {
self.mleaderstyle_tab = tab;
Task::none()
}
Message::MLeaderStyleDialogCompare(name) => {
self.mleaderstyle_compare = name;
Task::none()
}
Message::MLeaderStyleDialogSetCurrent => self.on_mleader_style_dialog_set_current(), Message::MLeaderStyleDialogSetCurrent => self.on_mleader_style_dialog_set_current(),
Message::MLeaderStyleDialogNew => { Message::MLeaderStyleDialogNew => {
self.style_new(crate::app::StyleKind::MLeader); self.style_new(crate::app::StyleKind::MLeader);
@ -5864,6 +5979,10 @@ impl OpenCADStudio {
self.dimstyle_tab = tab; self.dimstyle_tab = tab;
Task::none() Task::none()
} }
Message::DimStyleDialogCompare(name) => {
self.dimstyle_compare = name;
Task::none()
}
Message::DimStyleDialogNew => { Message::DimStyleDialogNew => {
self.style_new(crate::app::StyleKind::Dim); self.style_new(crate::app::StyleKind::Dim);
Task::none() Task::none()
@ -5875,6 +5994,19 @@ impl OpenCADStudio {
Message::DimStyleDialogSetCurrent => { Message::DimStyleDialogSetCurrent => {
// Staged: persists on Apply. // Staged: persists on Apply.
let i = self.active_tab; let i = self.active_tab;
let read_only = self.tabs[i]
.scene
.document
.dim_styles
.get(&self.dimstyle_selected)
.is_some_and(|style| {
style.xref_reference
|| style.xref_dependent
|| !style.xref_handle.is_null()
});
if read_only {
return Task::none();
}
self.tabs[i].scene.document.header.current_dimstyle_name = self.tabs[i].scene.document.header.current_dimstyle_name =
self.dimstyle_selected.clone(); self.dimstyle_selected.clone();
self.sync_ribbon_styles(); self.sync_ribbon_styles();
@ -5894,7 +6026,75 @@ impl OpenCADStudio {
Task::none() Task::none()
} }
Message::DsToggle(field) => { Message::DsToggle(field) => {
let separate_arrows = field == crate::app::DsField::Dimsah;
self.apply_ds_toggle(field); self.apply_ds_toggle(field);
if separate_arrows && self.ds_dimsah {
let i = self.active_tab;
if let Some(style) = self.tabs[i]
.scene
.document
.dim_styles
.get_mut(&self.dimstyle_selected)
{
if style.dimblk1.is_null() {
style.dimblk1 = style.dimblk;
}
if style.dimblk2.is_null() {
style.dimblk2 = style.dimblk1;
}
}
}
Task::none()
}
Message::DsToleranceMode(mode) => {
self.ds_dimlim = mode == "limits";
self.ds_dimtol = matches!(mode.as_str(), "symmetrical" | "deviation");
if mode == "symmetrical" {
self.ds_dimtm = self.ds_dimtp.clone();
}
let gap = self.ds_dimgap.trim().parse::<f64>().unwrap_or(0.625).abs();
self.ds_dimgap = if mode == "basic" {
format!("-{}", gap.max(f64::EPSILON))
} else {
format!("{}", gap)
};
Task::none()
}
Message::DsZeroBase(field, base) => {
let current = match &field {
crate::app::DsField::Dimzin => &self.ds_dimzin,
crate::app::DsField::Dimaltz => &self.ds_dimaltz,
crate::app::DsField::Dimalttz => &self.ds_dimalttz,
crate::app::DsField::Dimtzin => &self.ds_dimtzin,
_ => return Task::none(),
}
.trim()
.parse::<i16>()
.unwrap_or(0);
self.apply_ds_edit(field, ((current & !3) | (base & 3)).to_string());
Task::none()
}
Message::DsZeroFlag(field, bit) => {
let current = match &field {
crate::app::DsField::Dimzin => &self.ds_dimzin,
crate::app::DsField::Dimaltz => &self.ds_dimaltz,
crate::app::DsField::Dimalttz => &self.ds_dimalttz,
crate::app::DsField::Dimtzin => &self.ds_dimtzin,
_ => return Task::none(),
}
.trim()
.parse::<i16>()
.unwrap_or(0);
self.apply_ds_edit(field, (current ^ bit).to_string());
Task::none()
}
Message::DsCenterMarkMode(mode) => {
let size = self.ds_dimcen.trim().parse::<f64>().unwrap_or(0.09).abs();
self.ds_dimcen = match mode.as_str() {
"mark" => size.max(f64::EPSILON).to_string(),
"lines" => format!("-{}", size.max(f64::EPSILON)),
_ => "0".to_string(),
};
Task::none() Task::none()
} }
Message::DsColorMore(field) => { Message::DsColorMore(field) => {

View file

@ -22,6 +22,91 @@ use iced::{mouse, Point, Task};
impl OpenCADStudio { impl OpenCADStudio {
pub(in crate::app) fn mlstyle_mut(
&mut self,
tab: usize,
) -> Option<&mut acadrust::objects::MLineStyle> {
use acadrust::objects::ObjectType;
let name = self.mlstyle_selected.clone();
self.tabs[tab]
.scene
.document
.objects
.values_mut()
.find_map(|object| match object {
ObjectType::MLineStyle(style) if style.name == name => Some(style),
_ => None,
})
}
pub(in crate::app) fn load_mlstyle_bufs(&mut self, tab: usize) {
use acadrust::objects::ObjectType;
let name = self.mlstyle_selected.clone();
let Some((description, start_angle, end_angle, fill_color, elements)) = self.tabs[tab]
.scene
.document
.objects
.values()
.find_map(|object| match object {
ObjectType::MLineStyle(style) if style.name == name => Some((
style.description.clone(),
style.start_angle.to_degrees(),
style.end_angle.to_degrees(),
style.fill_color.index().unwrap_or(256),
style
.elements
.iter()
.map(|element| {
[
format!("{}", element.offset),
element.color.index().unwrap_or(256).to_string(),
element.linetype.clone(),
]
})
.collect::<Vec<_>>(),
)),
_ => None,
})
else {
return;
};
self.mln_description = description;
self.mln_start_angle = format!("{start_angle:.4}");
self.mln_end_angle = format!("{end_angle:.4}");
self.mln_fill_color = fill_color.to_string();
self.mln_elements = elements;
}
pub(in crate::app) fn stage_mlstyle_bufs(&mut self) {
let tab = self.active_tab;
let description = self.mln_description.clone();
let start_angle = self.mln_start_angle.trim().parse::<f64>().ok();
let end_angle = self.mln_end_angle.trim().parse::<f64>().ok();
let fill_color = self.mln_fill_color.trim().parse::<i16>().ok();
let elements = self.mln_elements.clone();
if let Some(style) = self.mlstyle_mut(tab) {
style.description = description;
if let Some(value) = start_angle {
style.start_angle = value.to_radians();
}
if let Some(value) = end_angle {
style.end_angle = value.to_radians();
}
if let Some(value) = fill_color {
style.fill_color = acadrust::types::Color::from_index(value);
}
for (element, values) in style.elements.iter_mut().zip(elements) {
if let Ok(value) = values[0].trim().parse::<f64>() {
element.offset = value;
}
if let Ok(value) = values[1].trim().parse::<i16>() {
element.color = acadrust::types::Color::from_index(value);
}
element.linetype = values[2].clone();
}
}
}
pub(in crate::app) fn tablestyle_mut(&mut self, tab: usize) -> Option<&mut acadrust::objects::TableStyle> { pub(in crate::app) fn tablestyle_mut(&mut self, tab: usize) -> Option<&mut acadrust::objects::TableStyle> {
use acadrust::objects::ObjectType; use acadrust::objects::ObjectType;
let name = self.tablestyle_selected.clone(); let name = self.tablestyle_selected.clone();
@ -163,8 +248,8 @@ impl OpenCADStudio {
self.mls_text_height = format!("{:.4}", s.text_height); self.mls_text_height = format!("{:.4}", s.text_height);
self.mls_scale_factor = format!("{:.4}", s.scale_factor); self.mls_scale_factor = format!("{:.4}", s.scale_factor);
self.mls_break_gap = format!("{:.4}", s.break_gap_size); self.mls_break_gap = format!("{:.4}", s.break_gap_size);
self.mls_first_seg_angle = format!("{:.4}", s.first_segment_angle); self.mls_first_seg_angle = format!("{:.4}", s.first_segment_angle.to_degrees());
self.mls_second_seg_angle = format!("{:.4}", s.second_segment_angle); self.mls_second_seg_angle = format!("{:.4}", s.second_segment_angle.to_degrees());
self.mls_max_points = s.max_leader_points.to_string(); self.mls_max_points = s.max_leader_points.to_string();
self.mls_default_text = s.default_text.clone(); self.mls_default_text = s.default_text.clone();
self.mls_line_color = s self.mls_line_color = s
@ -184,7 +269,7 @@ impl OpenCADStudio {
.index() .index()
.map(|c| c.to_string()) .map(|c| c.to_string())
.unwrap_or_default(); .unwrap_or_default();
self.mls_block_rotation = format!("{:.4}", s.block_content_rotation); self.mls_block_rotation = format!("{:.4}", s.block_content_rotation.to_degrees());
self.mls_block_scale_x = format!("{:.4}", s.block_content_scale_x); self.mls_block_scale_x = format!("{:.4}", s.block_content_scale_x);
self.mls_block_scale_y = format!("{:.4}", s.block_content_scale_y); self.mls_block_scale_y = format!("{:.4}", s.block_content_scale_y);
self.mls_block_scale_z = format!("{:.4}", s.block_content_scale_z); self.mls_block_scale_z = format!("{:.4}", s.block_content_scale_z);
@ -273,6 +358,20 @@ impl OpenCADStudio {
pub(in crate::app) fn apply_dimstyle_bufs(&mut self, tab: usize) { pub(in crate::app) fn apply_dimstyle_bufs(&mut self, tab: usize) {
let doc = &mut self.tabs[tab].scene.document; let doc = &mut self.tabs[tab].scene.document;
let read_only = doc
.dim_styles
.get(&self.dimstyle_selected)
.is_some_and(|style| {
style.xref_reference || style.xref_dependent || !style.xref_handle.is_null()
});
if read_only {
return;
}
let text_style_handle = doc
.text_styles
.get(&self.ds_dimtxsty)
.map(|style| style.handle)
.unwrap_or(acadrust::types::Handle::NULL);
let Some(ds) = doc.dim_styles.get_mut(&self.dimstyle_selected) else { let Some(ds) = doc.dim_styles.get_mut(&self.dimstyle_selected) else {
return; return;
}; };
@ -323,6 +422,7 @@ impl OpenCADStudio {
ds.dimlim = self.ds_dimlim; ds.dimlim = self.ds_dimlim;
ds.dimpost = self.ds_dimpost.clone(); ds.dimpost = self.ds_dimpost.clone();
ds.dimtxsty = self.ds_dimtxsty.clone(); ds.dimtxsty = self.ds_dimtxsty.clone();
ds.dimtxsty_handle = text_style_handle;
ds.annotative = self.ds_annotative; ds.annotative = self.ds_annotative;
set_i16!(dimclrd, self.ds_dimclrd); set_i16!(dimclrd, self.ds_dimclrd);
set_i16!(dimlwd, self.ds_dimlwd); set_i16!(dimlwd, self.ds_dimlwd);
@ -392,7 +492,15 @@ impl OpenCADStudio {
Dimlunit => self.ds_dimlunit = val, Dimlunit => self.ds_dimlunit = val,
Dimdec => self.ds_dimdec = val, Dimdec => self.ds_dimdec = val,
Dimpost => self.ds_dimpost = val, Dimpost => self.ds_dimpost = val,
Dimtp => self.ds_dimtp = val, Dimtp => {
let symmetrical = self.ds_dimtol
&& !self.ds_dimlim
&& self.ds_dimtp.trim() == self.ds_dimtm.trim();
self.ds_dimtp = val.clone();
if symmetrical {
self.ds_dimtm = val;
}
}
Dimtm => self.ds_dimtm = val, Dimtm => self.ds_dimtm = val,
Dimtdec => self.ds_dimtdec = val, Dimtdec => self.ds_dimtdec = val,
Dimtfac => self.ds_dimtfac = val, Dimtfac => self.ds_dimtfac = val,
@ -518,6 +626,9 @@ pub(super) fn on_text_style_dialog_open(&mut self) -> Task<Message> {
let bigfont = self.textstyle_bigfont.clone(); let bigfont = self.textstyle_bigfont.clone();
let ttf = self.textstyle_ttf.clone(); let ttf = self.textstyle_ttf.clone();
if let Some(s) = self.tabs[i].scene.document.text_styles.get_mut(&name) { if let Some(s) = self.tabs[i].scene.document.text_styles.get_mut(&name) {
if s.xref_dependent {
return;
}
s.font_file = font; s.font_file = font;
s.big_font_file = bigfont; s.big_font_file = bigfont;
s.true_type_font = ttf; s.true_type_font = ttf;
@ -629,6 +740,7 @@ pub(super) fn on_text_style_dialog_open(&mut self) -> Task<Message> {
.unwrap_or_else(|| "Standard".to_string()) .unwrap_or_else(|| "Standard".to_string())
}; };
self.active_modal = Some(crate::app::ModalKind::MlStyle); self.active_modal = Some(crate::app::ModalKind::MlStyle);
self.load_mlstyle_bufs(i);
self.style_stage_begin(); self.style_stage_begin();
Task::none() Task::none()
} }
@ -818,7 +930,10 @@ pub(super) fn on_text_style_dialog_open(&mut self) -> Task<Message> {
pub(super) fn on_mleader_style_set_handle(&mut self, field: &'static str, value: String) -> Task<Message> { pub(super) fn on_mleader_style_set_handle(&mut self, field: &'static str, value: String) -> Task<Message> {
let i = self.active_tab; let i = self.active_tab;
let doc = &self.tabs[i].scene.document; let doc = &self.tabs[i].scene.document;
let handle: Option<acadrust::types::Handle> = if value == "None" { let handle: Option<acadrust::types::Handle> = if value == "None"
|| value == "ByBlock"
|| value == "Closed filled"
{
None None
} else { } else {
match field { match field {
@ -897,10 +1012,10 @@ pub(super) fn on_text_style_dialog_open(&mut self) -> Task<Message> {
s.break_gap_size = v; s.break_gap_size = v;
} }
if let Some(v) = fsa { if let Some(v) = fsa {
s.first_segment_angle = v; s.first_segment_angle = v.to_radians();
} }
if let Some(v) = ssa { if let Some(v) = ssa {
s.second_segment_angle = v; s.second_segment_angle = v.to_radians();
} }
if let Some(v) = mp { if let Some(v) = mp {
s.max_leader_points = v; s.max_leader_points = v;
@ -920,7 +1035,7 @@ pub(super) fn on_text_style_dialog_open(&mut self) -> Task<Message> {
s.block_content_color = acadrust::types::Color::from_index(v); s.block_content_color = acadrust::types::Color::from_index(v);
} }
if let Some(v) = brot { if let Some(v) = brot {
s.block_content_rotation = v; s.block_content_rotation = v.to_radians();
} }
if let Some(v) = bsx { if let Some(v) = bsx {
s.block_content_scale_x = v; s.block_content_scale_x = v;
@ -962,6 +1077,14 @@ pub(super) fn on_text_style_dialog_open(&mut self) -> Task<Message> {
.unwrap_or_else(|| "Standard".to_string()) .unwrap_or_else(|| "Standard".to_string())
}; };
self.dimstyle_selected = selected.clone(); self.dimstyle_selected = selected.clone();
self.dimstyle_compare = self.tabs[i]
.scene
.document
.dim_styles
.iter()
.map(|style| style.name.clone())
.find(|name| !name.eq_ignore_ascii_case(&selected))
.unwrap_or_default();
self.load_dimstyle_bufs(i); self.load_dimstyle_bufs(i);
self.active_modal = Some(crate::app::ModalKind::DimStyle); self.active_modal = Some(crate::app::ModalKind::DimStyle);
self.style_stage_begin(); self.style_stage_begin();
@ -1018,6 +1141,11 @@ pub(super) fn on_text_style_dialog_open(&mut self) -> Task<Message> {
"dimltex_handle" | "dimltex1_handle" | "dimltex2_handle" "dimltex_handle" | "dimltex1_handle" | "dimltex2_handle"
); );
let doc = &self.tabs[i].scene.document; let doc = &self.tabs[i].scene.document;
if doc.dim_styles.get(&name).is_some_and(|style| {
style.xref_reference || style.xref_dependent || !style.xref_handle.is_null()
}) {
return Task::none();
}
let handle = if value == "Default" || value == "ByBlock" { let handle = if value == "Default" || value == "ByBlock" {
acadrust::types::Handle::NULL acadrust::types::Handle::NULL
} else if is_lt { } else if is_lt {
@ -1037,7 +1165,10 @@ pub(super) fn on_text_style_dialog_open(&mut self) -> Task<Message> {
if let Some(ds) = self.tabs[i].scene.document.dim_styles.get_mut(&name) { if let Some(ds) = self.tabs[i].scene.document.dim_styles.get_mut(&name) {
match field { match field {
"dimblk" => ds.dimblk = handle, "dimblk" => ds.dimblk = handle,
"dimblk1" => ds.dimblk1 = handle, "dimblk1" => {
ds.dimblk1 = handle;
ds.dimblk2 = handle;
}
"dimblk2" => ds.dimblk2 = handle, "dimblk2" => ds.dimblk2 = handle,
"dimldrblk" => ds.dimldrblk = handle, "dimldrblk" => ds.dimldrblk = handle,
"dimltex_handle" => ds.dimltex_handle = handle, "dimltex_handle" => ds.dimltex_handle = handle,

View file

@ -362,6 +362,7 @@ impl OpenCADStudio {
), ),
super::super::ModalKind::TextStyle => { super::super::ModalKind::TextStyle => {
let tab = &self.tabs[self.active_tab]; let tab = &self.tabs[self.active_tab];
let doc = &tab.scene.document;
let styles: Vec<String> = tab let styles: Vec<String> = tab
.scene .scene
.document .document
@ -369,23 +370,77 @@ impl OpenCADStudio {
.iter() .iter()
.map(|s| s.name.clone()) .map(|s| s.name.clone())
.collect(); .collect();
let (backward, upside_down, annotative) = tab let selected_style = doc
.scene
.document
.text_styles .text_styles
.get(&self.textstyle_selected) .get(&self.textstyle_selected);
.map(|s| (s.flags.backward, s.flags.upside_down, s.annotative)) let (backward, upside_down, vertical, annotative, read_only) = selected_style
.unwrap_or((false, false, false)); .map(|style| {
(
style.flags.backward,
style.flags.upside_down,
style.is_vertical,
style.annotative,
style.xref_dependent,
)
})
.unwrap_or((false, false, false, false, false));
let in_use = self.style_in_use(
crate::app::StyleKind::Text,
&self.textstyle_selected,
);
let compare_opts: Vec<String> = styles
.iter()
.filter(|name| !name.eq_ignore_ascii_case(&self.textstyle_selected))
.cloned()
.collect();
let compare_name = compare_opts
.iter()
.find(|name| name.eq_ignore_ascii_case(&self.textstyle_compare))
.cloned()
.or_else(|| compare_opts.first().cloned())
.unwrap_or_default();
let mut comparison_sections = Vec::new();
if let (Some(a), Some(b)) = (selected_style, doc.text_styles.get(&compare_name)) {
if (
&a.font_file,
&a.big_font_file,
&a.true_type_font,
a.is_shape_file,
) != (
&b.font_file,
&b.big_font_file,
&b.true_type_font,
b.is_shape_file,
) {
comparison_sections.push(crate::i18n::translate("Fonts").into_owned());
}
if (a.height, a.width_factor, a.oblique_angle, a.last_height)
!= (b.height, b.width_factor, b.oblique_angle, b.last_height)
{
comparison_sections.push(crate::i18n::translate("Size").into_owned());
}
if (a.flags, a.is_vertical, a.annotative)
!= (b.flags, b.is_vertical, b.annotative)
{
comparison_sections.push(crate::i18n::translate("Effects").into_owned());
}
}
sized_flow( sized_flow(
ex, ex,
860, 960,
480, 680,
|flow| { |flow| {
crate::ui::style::textstyle::view_window( crate::ui::style::textstyle::view_window(
crate::ui::style::textstyle::TextStyleView { crate::ui::style::textstyle::TextStyleView {
styles: styles.clone(), styles: styles.clone(),
selected: &self.textstyle_selected, selected: &self.textstyle_selected,
current: &tab.scene.document.header.current_text_style_name, current: &tab.scene.document.header.current_text_style_name,
tab: self.textstyle_tab,
compare_name: compare_name.clone(),
compare_opts: compare_opts.clone(),
comparison_sections: comparison_sections.clone(),
read_only,
in_use,
font_buf: &self.textstyle_font, font_buf: &self.textstyle_font,
width_buf: &self.textstyle_width, width_buf: &self.textstyle_width,
oblique_buf: &self.textstyle_oblique, oblique_buf: &self.textstyle_oblique,
@ -394,6 +449,7 @@ impl OpenCADStudio {
ttf_buf: &self.textstyle_ttf, ttf_buf: &self.textstyle_ttf,
backward, backward,
upside_down, upside_down,
vertical,
annotative, annotative,
rename_active: self.style_rename.as_deref(), rename_active: self.style_rename.as_deref(),
rename_buf: &self.style_rename_buf, rename_buf: &self.style_rename_buf,
@ -420,18 +476,60 @@ impl OpenCADStudio {
ObjectType::MLineStyle(s) if s.name == self.mlstyle_selected => Some(s), ObjectType::MLineStyle(s) if s.name == self.mlstyle_selected => Some(s),
_ => None, _ => None,
}); });
let compare_opts: Vec<String> = styles
.iter()
.filter(|name| !name.eq_ignore_ascii_case(&self.mlstyle_selected))
.cloned()
.collect();
let compare_name = compare_opts
.iter()
.find(|name| name.eq_ignore_ascii_case(&self.mlstyle_compare))
.cloned()
.or_else(|| compare_opts.first().cloned())
.unwrap_or_default();
let compare_style = tab.scene.document.objects.values().find_map(|object| match object {
ObjectType::MLineStyle(style) if style.name == compare_name => Some(style),
_ => None,
});
let mut comparison_sections = Vec::new();
if let (Some(a), Some(b)) = (selected_style, compare_style) {
if (&a.description, a.flags, a.fill_color, a.start_angle, a.end_angle)
!= (&b.description, b.flags, b.fill_color, b.start_angle, b.end_angle)
{
comparison_sections.push(crate::i18n::translate("Caps and Fill").into_owned());
}
if a.elements != b.elements {
comparison_sections.push(crate::i18n::translate("Elements").into_owned());
}
}
let in_use = self.style_in_use(
crate::app::StyleKind::MLine,
&self.mlstyle_selected,
);
sized_flow( sized_flow(
ex, ex,
620, 960,
420, 680,
|flow| { |flow| {
crate::ui::style::mlstyle::view_window( crate::ui::style::mlstyle::view_window(
styles.clone(), crate::ui::style::mlstyle::MlStyleView {
&self.mlstyle_selected, styles: styles.clone(),
selected_style, selected: &self.mlstyle_selected,
tab.scene.document.header.multiline_style.clone(), style: selected_style,
self.style_rename.as_deref(), current: tab.scene.document.header.multiline_style.clone(),
&self.style_rename_buf, tab: self.mlstyle_tab,
compare_name: compare_name.clone(),
compare_opts: compare_opts.clone(),
comparison_sections: comparison_sections.clone(),
in_use,
description: &self.mln_description,
start_angle: &self.mln_start_angle,
end_angle: &self.mln_end_angle,
fill_color: &self.mln_fill_color,
elements: &self.mln_elements,
rename_active: self.style_rename.as_deref(),
rename_buf: &self.style_rename_buf,
},
flow, flow,
) )
}, },
@ -454,32 +552,89 @@ impl OpenCADStudio {
ObjectType::TableStyle(s) if s.name == self.tablestyle_selected => Some(s), ObjectType::TableStyle(s) if s.name == self.tablestyle_selected => Some(s),
_ => None, _ => None,
}); });
let compare_opts: Vec<String> = styles
.iter()
.filter(|name| !name.eq_ignore_ascii_case(&self.tablestyle_selected))
.cloned()
.collect();
let compare_name = compare_opts
.iter()
.find(|name| name.eq_ignore_ascii_case(&self.tablestyle_compare))
.cloned()
.or_else(|| compare_opts.first().cloned())
.unwrap_or_default();
let compare_style = tab.scene.document.objects.values().find_map(|object| match object {
ObjectType::TableStyle(style) if style.name == compare_name => Some(style),
_ => None,
});
let mut comparison_sections = Vec::new();
if let (Some(a), Some(b)) = (selected_style, compare_style) {
if (
&a.description,
a.flow_direction,
a.horizontal_margin,
a.vertical_margin,
a.title_suppressed,
a.header_suppressed,
a.annotative,
) != (
&b.description,
b.flow_direction,
b.horizontal_margin,
b.vertical_margin,
b.title_suppressed,
b.header_suppressed,
b.annotative,
) {
comparison_sections.push(crate::i18n::translate("General").into_owned());
}
for (label, different) in [
("Data Row", a.data_row_style != b.data_row_style),
("Header Row", a.header_row_style != b.header_row_style),
("Title Row", a.title_row_style != b.title_row_style),
] {
if different {
comparison_sections.push(crate::i18n::translate(label).into_owned());
}
}
}
let in_use = self.style_in_use(
crate::app::StyleKind::Table,
&self.tablestyle_selected,
);
sized_flow( sized_flow(
ex, ex,
620, 960,
420, 680,
|flow| { |flow| {
crate::ui::style::tablestyle::view_window( crate::ui::style::tablestyle::view_window(
styles.clone(), crate::ui::style::tablestyle::TableStyleView {
&self.tablestyle_selected, styles: styles.clone(),
&self.ribbon.active_table_style, selected: &self.tablestyle_selected,
selected_style, current: &tab.scene.document.header.current_table_style_name,
&self.ts_hmargin, style: selected_style,
&self.ts_vmargin, tab: self.tablestyle_tab,
&self.ts_description, compare_name: compare_name.clone(),
&self.ts_cell_textstyle, compare_opts: compare_opts.clone(),
&self.ts_cell_height, comparison_sections: comparison_sections.clone(),
&self.ts_cell_textcolor, in_use,
&self.ts_cell_fillcolor, hmargin: &self.ts_hmargin,
&self.ts_cell_datatype, vmargin: &self.ts_vmargin,
&self.ts_cell_unittype, description: &self.ts_description,
&self.ts_cell_format, cell_textstyle: &self.ts_cell_textstyle,
&self.ts_border_lw, cell_height: &self.ts_cell_height,
&self.ts_border_color, cell_textcolor: &self.ts_cell_textcolor,
&self.ts_border_spacing, cell_fillcolor: &self.ts_cell_fillcolor,
self.style_rename.as_deref(), cell_datatype: &self.ts_cell_datatype,
&self.style_rename_buf, cell_unittype: &self.ts_cell_unittype,
self.ts_color_open, cell_format: &self.ts_cell_format,
border_lw: &self.ts_border_lw,
border_color: &self.ts_border_color,
border_spacing: &self.ts_border_spacing,
rename_active: self.style_rename.as_deref(),
rename_buf: &self.style_rename_buf,
color_open: self.ts_color_open,
},
flow, flow,
) )
}, },
@ -507,7 +662,9 @@ impl OpenCADStudio {
let doc = &tab.scene.document; let doc = &tab.scene.document;
let mut block_opts: Vec<String> = vec!["None".to_string()]; let mut block_opts: Vec<String> = vec!["None".to_string()];
block_opts.extend(doc.block_records.iter().map(|b| b.name.clone())); block_opts.extend(doc.block_records.iter().map(|b| b.name.clone()));
let mut lt_opts: Vec<String> = vec!["None".to_string()]; let mut arrow_opts: Vec<String> = vec!["Closed filled".to_string()];
arrow_opts.extend(doc.block_records.iter().map(|b| b.name.clone()));
let mut lt_opts: Vec<String> = vec!["ByBlock".to_string()];
lt_opts.extend(doc.line_types.iter().map(|lt| lt.name.clone())); lt_opts.extend(doc.line_types.iter().map(|lt| lt.name.clone()));
let mut textstyle_opts: Vec<String> = vec!["None".to_string()]; let mut textstyle_opts: Vec<String> = vec!["None".to_string()];
textstyle_opts.extend(doc.text_styles.iter().map(|t| t.name.clone())); textstyle_opts.extend(doc.text_styles.iter().map(|t| t.name.clone()));
@ -529,8 +686,8 @@ impl OpenCADStudio {
.iter() .iter()
.find(|lt| lt.handle == h) .find(|lt| lt.handle == h)
.map(|lt| lt.name.clone()) .map(|lt| lt.name.clone())
.unwrap_or_else(|| "None".to_string()), .unwrap_or_else(|| "ByBlock".to_string()),
None => "None".to_string(), None => "ByBlock".to_string(),
} }
}; };
let opt_ts = |h: Option<acadrust::types::Handle>| -> String { let opt_ts = |h: Option<acadrust::types::Handle>| -> String {
@ -548,16 +705,153 @@ impl OpenCADStudio {
match selected_style { match selected_style {
Some(s) => ( Some(s) => (
opt_lt(s.line_type_handle), opt_lt(s.line_type_handle),
opt_block(s.arrowhead_handle), s.arrowhead_handle
.and_then(|handle| {
doc.block_records
.iter()
.find(|record| record.handle == handle)
.map(|record| record.name.clone())
})
.unwrap_or_else(|| "Closed filled".to_string()),
opt_ts(s.text_style_handle), opt_ts(s.text_style_handle),
opt_block(s.block_content_handle), opt_block(s.block_content_handle),
), ),
None => Default::default(), None => Default::default(),
}; };
let compare_opts: Vec<String> = styles
.iter()
.filter(|name| !name.eq_ignore_ascii_case(&self.mleaderstyle_selected))
.cloned()
.collect();
let compare_name = compare_opts
.iter()
.find(|name| name.eq_ignore_ascii_case(&self.mleaderstyle_compare))
.cloned()
.or_else(|| compare_opts.first().cloned())
.unwrap_or_default();
let compare_style = doc.objects.values().find_map(|object| match object {
ObjectType::MultiLeaderStyle(style) if style.name == compare_name => Some(style),
_ => None,
});
let mut comparison_sections = Vec::new();
if let (Some(a), Some(b)) = (selected_style, compare_style) {
if (
&a.description,
a.path_type,
a.line_color,
a.line_type_handle,
a.line_weight,
a.arrowhead_handle,
a.arrowhead_size,
a.break_gap_size,
) != (
&b.description,
b.path_type,
b.line_color,
b.line_type_handle,
b.line_weight,
b.arrowhead_handle,
b.arrowhead_size,
b.break_gap_size,
) {
comparison_sections.push(crate::i18n::translate("Leader Format").into_owned());
}
if (
a.enable_landing,
a.enable_dogleg,
a.landing_distance,
a.landing_gap,
a.scale_factor,
a.align_space,
a.max_leader_points,
a.first_segment_angle,
a.second_segment_angle,
a.leader_draw_order,
a.multileader_draw_order,
a.is_annotative,
) != (
b.enable_landing,
b.enable_dogleg,
b.landing_distance,
b.landing_gap,
b.scale_factor,
b.align_space,
b.max_leader_points,
b.first_segment_angle,
b.second_segment_angle,
b.leader_draw_order,
b.multileader_draw_order,
b.is_annotative,
) {
comparison_sections.push(crate::i18n::translate("Leader Structure").into_owned());
}
if (
a.content_type,
&a.default_text,
a.text_style_handle,
a.text_height,
a.text_color,
a.text_angle_type,
a.text_alignment,
) != (
b.content_type,
&b.default_text,
b.text_style_handle,
b.text_height,
b.text_color,
b.text_angle_type,
b.text_alignment,
) || (
a.text_left_attachment,
a.text_right_attachment,
a.text_top_attachment,
a.text_bottom_attachment,
a.text_attachment_direction,
a.text_frame,
a.text_always_left,
) != (
b.text_left_attachment,
b.text_right_attachment,
b.text_top_attachment,
b.text_bottom_attachment,
b.text_attachment_direction,
b.text_frame,
b.text_always_left,
) {
comparison_sections.push(crate::i18n::translate("Content").into_owned());
}
if (
a.block_content_handle,
a.block_content_color,
a.block_content_connection,
a.block_content_rotation,
a.block_content_scale_x,
a.block_content_scale_y,
a.block_content_scale_z,
a.enable_block_scale,
a.enable_block_rotation,
) != (
b.block_content_handle,
b.block_content_color,
b.block_content_connection,
b.block_content_rotation,
b.block_content_scale_x,
b.block_content_scale_y,
b.block_content_scale_z,
b.enable_block_scale,
b.enable_block_rotation,
) {
comparison_sections.push(crate::i18n::translate("Block Content").into_owned());
}
}
let in_use = self.style_in_use(
crate::app::StyleKind::MLeader,
&self.mleaderstyle_selected,
);
sized_flow( sized_flow(
ex, ex,
560, 960,
560, 680,
|flow| { |flow| {
crate::ui::style::mleaderstyle::view_window( crate::ui::style::mleaderstyle::view_window(
crate::ui::style::mleaderstyle::MLeaderStyleView { crate::ui::style::mleaderstyle::MLeaderStyleView {
@ -565,6 +859,11 @@ impl OpenCADStudio {
selected: &self.mleaderstyle_selected, selected: &self.mleaderstyle_selected,
style: selected_style, style: selected_style,
current: tab.active_mleader_style.clone(), current: tab.active_mleader_style.clone(),
tab: self.mleaderstyle_tab,
compare_name: compare_name.clone(),
compare_opts: compare_opts.clone(),
comparison_sections: comparison_sections.clone(),
in_use,
landing_distance: &self.mls_landing_distance, landing_distance: &self.mls_landing_distance,
landing_gap: &self.mls_landing_gap, landing_gap: &self.mls_landing_gap,
arrowhead_size: &self.mls_arrowhead_size, arrowhead_size: &self.mls_arrowhead_size,
@ -585,6 +884,7 @@ impl OpenCADStudio {
block_scale_y: &self.mls_block_scale_y, block_scale_y: &self.mls_block_scale_y,
block_scale_z: &self.mls_block_scale_z, block_scale_z: &self.mls_block_scale_z,
block_opts: block_opts.clone(), block_opts: block_opts.clone(),
arrow_opts: arrow_opts.clone(),
lt_opts: lt_opts.clone(), lt_opts: lt_opts.clone(),
textstyle_opts: textstyle_opts.clone(), textstyle_opts: textstyle_opts.clone(),
line_type_name: line_type_name.clone(), line_type_name: line_type_name.clone(),
@ -630,6 +930,13 @@ impl OpenCADStudio {
); );
let mut lt_opts: Vec<String> = vec!["ByBlock".to_string()]; let mut lt_opts: Vec<String> = vec!["ByBlock".to_string()];
lt_opts.extend(doc.line_types.iter().map(|lt| lt.name.clone())); lt_opts.extend(doc.line_types.iter().map(|lt| lt.name.clone()));
let text_style_opts: Vec<String> =
doc.text_styles.iter().map(|style| style.name.clone()).collect();
let text_style_fixed_height = doc
.text_styles
.get(&self.ds_dimtxsty)
.map(|style| style.height)
.filter(|height| *height > 0.0);
let blk_name = |h: acadrust::types::Handle| -> String { let blk_name = |h: acadrust::types::Handle| -> String {
if h.is_null() { if h.is_null() {
"Default".to_string() "Default".to_string()
@ -653,6 +960,70 @@ impl OpenCADStudio {
} }
}; };
let ds_sel = doc.dim_styles.get(&self.dimstyle_selected); let ds_sel = doc.dim_styles.get(&self.dimstyle_selected);
let read_only = ds_sel.is_some_and(|style| {
style.xref_reference || style.xref_dependent || !style.xref_handle.is_null()
});
let in_use = doc.entities().any(|entity| {
matches!(entity, acadrust::EntityType::Dimension(dimension)
if dimension.base().style_name.eq_ignore_ascii_case(&self.dimstyle_selected))
});
let compare_opts: Vec<String> = styles
.iter()
.filter(|name| !name.eq_ignore_ascii_case(&self.dimstyle_selected))
.cloned()
.collect();
let compare_name = compare_opts
.iter()
.find(|name| name.eq_ignore_ascii_case(&self.dimstyle_compare))
.cloned()
.or_else(|| compare_opts.first().cloned())
.unwrap_or_default();
let mut comparison_sections = Vec::new();
if let (Some(a), Some(b)) = (ds_sel, doc.dim_styles.get(&compare_name)) {
if (a.dimdle, a.dimdli, a.dimgap, a.dimclrd, a.dimlwd, a.dimsd1, a.dimsd2)
!= (b.dimdle, b.dimdli, b.dimgap, b.dimclrd, b.dimlwd, b.dimsd1, b.dimsd2)
|| (a.dimexe, a.dimexo, a.dimclre, a.dimlwe, a.dimse1, a.dimse2, a.dimfxl, a.dimfxlon)
!= (b.dimexe, b.dimexo, b.dimclre, b.dimlwe, b.dimse1, b.dimse2, b.dimfxl, b.dimfxlon)
|| (a.dimltex_handle, a.dimltex1_handle, a.dimltex2_handle)
!= (b.dimltex_handle, b.dimltex1_handle, b.dimltex2_handle)
{
comparison_sections.push(crate::i18n::translate("Lines").into_owned());
}
if (a.dimasz, a.dimblk, a.dimblk1, a.dimblk2, a.dimldrblk, a.dimsah, a.dimcen, a.dimtsz)
!= (b.dimasz, b.dimblk, b.dimblk1, b.dimblk2, b.dimldrblk, b.dimsah, b.dimcen, b.dimtsz)
|| (a.dimarcsym, a.dimjogang) != (b.dimarcsym, b.dimjogang)
{
comparison_sections.push(crate::i18n::translate("Symbols and Arrows").into_owned());
}
if (a.dimclrt, a.dimtxt, &a.dimtxsty, a.dimjust, a.dimtad, a.dimtvp)
!= (b.dimclrt, b.dimtxt, &b.dimtxsty, b.dimjust, b.dimtad, b.dimtvp)
|| (a.dimtih, a.dimtoh, a.dimtfill, a.dimtfillclr, a.dimtxtdirection)
!= (b.dimtih, b.dimtoh, b.dimtfill, b.dimtfillclr, b.dimtxtdirection)
{
comparison_sections.push(crate::i18n::translate("Text").into_owned());
}
if (a.dimatfit, a.dimtix, a.dimsoxd, a.dimtmove, a.dimupt, a.dimtofl, a.dimscale, a.annotative)
!= (b.dimatfit, b.dimtix, b.dimsoxd, b.dimtmove, b.dimupt, b.dimtofl, b.dimscale, b.annotative)
{
comparison_sections.push(crate::i18n::translate("Fit").into_owned());
}
if (a.dimlfac, a.dimlunit, a.dimdec, &a.dimpost, a.dimdsep, a.dimrnd, a.dimzin, a.dimfrac)
!= (b.dimlfac, b.dimlunit, b.dimdec, &b.dimpost, b.dimdsep, b.dimrnd, b.dimzin, b.dimfrac)
|| (a.dimaunit, a.dimadec, a.dimazin) != (b.dimaunit, b.dimadec, b.dimazin)
{
comparison_sections.push(crate::i18n::translate("Primary Units").into_owned());
}
if (a.dimalt, a.dimaltf, a.dimaltd, a.dimaltu, a.dimalttd, a.dimaltrnd, &a.dimapost, a.dimaltz, a.dimalttz)
!= (b.dimalt, b.dimaltf, b.dimaltd, b.dimaltu, b.dimalttd, b.dimaltrnd, &b.dimapost, b.dimaltz, b.dimalttz)
{
comparison_sections.push(crate::i18n::translate("Alternate Units").into_owned());
}
if (a.dimtol, a.dimlim, a.dimtp, a.dimtm, a.dimtdec, a.dimtfac, a.dimtolj, a.dimtzin)
!= (b.dimtol, b.dimlim, b.dimtp, b.dimtm, b.dimtdec, b.dimtfac, b.dimtolj, b.dimtzin)
{
comparison_sections.push(crate::i18n::translate("Tolerances").into_owned());
}
}
let ( let (
dimblk_name, dimblk_name,
dimblk1_name, dimblk1_name,
@ -673,7 +1044,7 @@ impl OpenCADStudio {
), ),
None => Default::default(), None => Default::default(),
}; };
sized_flow(ex, 720, 560, |flow| { sized_flow(ex, 960, 680, |flow| {
crate::ui::style::dimstyle::view_window( crate::ui::style::dimstyle::view_window(
styles.clone(), styles.clone(),
&self.dimstyle_selected, &self.dimstyle_selected,
@ -734,14 +1105,12 @@ impl OpenCADStudio {
dimtmove: &self.ds_dimtmove, dimtmove: &self.ds_dimtmove,
dimupt: self.ds_dimupt, dimupt: self.ds_dimupt,
dimtofl: self.ds_dimtofl, dimtofl: self.ds_dimtofl,
dimfit: &self.ds_dimfit,
dimdsep: &self.ds_dimdsep, dimdsep: &self.ds_dimdsep,
dimrnd: &self.ds_dimrnd, dimrnd: &self.ds_dimrnd,
dimzin: &self.ds_dimzin, dimzin: &self.ds_dimzin,
dimfrac: &self.ds_dimfrac, dimfrac: &self.ds_dimfrac,
dimaunit: &self.ds_dimaunit, dimaunit: &self.ds_dimaunit,
dimadec: &self.ds_dimadec, dimadec: &self.ds_dimadec,
dimunit: &self.ds_dimunit,
dimazin: &self.ds_dimazin, dimazin: &self.ds_dimazin,
dimalt: self.ds_dimalt, dimalt: self.ds_dimalt,
dimaltf: &self.ds_dimaltf, dimaltf: &self.ds_dimaltf,
@ -763,6 +1132,13 @@ impl OpenCADStudio {
dimltex2_name: dimltex2_name.clone(), dimltex2_name: dimltex2_name.clone(),
block_opts: block_opts.clone(), block_opts: block_opts.clone(),
lt_opts: lt_opts.clone(), lt_opts: lt_opts.clone(),
text_style_opts: text_style_opts.clone(),
text_style_fixed_height,
compare_name: compare_name.clone(),
compare_opts: compare_opts.clone(),
comparison_sections: comparison_sections.clone(),
read_only,
in_use,
color_open: self.ds_color_open.clone(), color_open: self.ds_color_open.clone(),
}, },
self.style_rename.as_deref(), self.style_rename.as_deref(),

View file

@ -13,12 +13,79 @@ use acadrust::{CadDocument, Handle};
// DXF group codes of the dimension variables surfaced on the leader panel. // DXF group codes of the dimension variables surfaced on the leader panel.
pub const DIMSCALE: i16 = 40; // overall scale (real) pub const DIMSCALE: i16 = 40; // overall scale (real)
pub const DIMPOST: i16 = 3;
pub const DIMAPOST: i16 = 4;
pub const DIMASZ: i16 = 41; // arrow size (real) pub const DIMASZ: i16 = 41; // arrow size (real)
pub const DIMTAD: i16 = 77; // text vertical pos (int16) pub const DIMTAD: i16 = 77; // text vertical pos (int16)
pub const DIMCLRD: i16 = 176; // dim line colour (int16 = ACI index) pub const DIMCLRD: i16 = 176; // dim line colour (int16 = ACI index)
pub const DIMGAP: i16 = 147; // text offset / gap (real) pub const DIMGAP: i16 = 147; // text offset / gap (real)
pub const DIMLWD: i16 = 371; // dim line lineweight (int16) pub const DIMLWD: i16 = 371; // dim line lineweight (int16)
pub const DIMLDRBLK: i16 = 341; // leader arrow block (handle) pub const DIMLDRBLK: i16 = 341; // leader arrow block (handle)
pub const DIMEXO: i16 = 42;
pub const DIMDLI: i16 = 43;
pub const DIMEXE: i16 = 44;
pub const DIMRND: i16 = 45;
pub const DIMDLE: i16 = 46;
pub const DIMTP: i16 = 47;
pub const DIMTM: i16 = 48;
pub const DIMFXL: i16 = 49;
pub const DIMJOGANG: i16 = 50;
pub const DIMTOL: i16 = 71;
pub const DIMLIM: i16 = 72;
pub const DIMTIH: i16 = 73;
pub const DIMTOH: i16 = 74;
pub const DIMSE1: i16 = 75;
pub const DIMSE2: i16 = 76;
pub const DIMZIN: i16 = 78;
pub const DIMAZIN: i16 = 79;
pub const DIMARCSYM: i16 = 90;
pub const DIMTXT: i16 = 140;
pub const DIMCEN: i16 = 141;
pub const DIMTSZ: i16 = 142;
pub const DIMALTF: i16 = 143;
pub const DIMLFAC: i16 = 144;
pub const DIMTVP: i16 = 145;
pub const DIMTFAC: i16 = 146;
pub const DIMALTRND: i16 = 148;
pub const DIMALT: i16 = 170;
pub const DIMALTD: i16 = 171;
pub const DIMTOFL: i16 = 172;
pub const DIMSAH: i16 = 173;
pub const DIMTIX: i16 = 174;
pub const DIMSOXD: i16 = 175;
pub const DIMCLRE: i16 = 177;
pub const DIMCLRT: i16 = 178;
pub const DIMADEC: i16 = 179;
pub const DIMDEC: i16 = 271;
pub const DIMTDEC: i16 = 272;
pub const DIMALTU: i16 = 273;
pub const DIMALTTD: i16 = 274;
pub const DIMAUNIT: i16 = 275;
pub const DIMFRAC: i16 = 276;
pub const DIMLUNIT: i16 = 277;
pub const DIMDSEP: i16 = 278;
pub const DIMTMOVE: i16 = 279;
pub const DIMJUST: i16 = 280;
pub const DIMSD1: i16 = 281;
pub const DIMSD2: i16 = 282;
pub const DIMTOLJ: i16 = 283;
pub const DIMTZIN: i16 = 284;
pub const DIMALTZ: i16 = 285;
pub const DIMALTTZ: i16 = 286;
pub const DIMUPT: i16 = 288;
pub const DIMATFIT: i16 = 289;
pub const DIMFXLON: i16 = 290;
pub const DIMTFILL: i16 = 69;
pub const DIMTFILLCLR: i16 = 70;
pub const DIMTXTDIRECTION: i16 = 295;
pub const DIMTXSTY: i16 = 340;
pub const DIMBLK: i16 = 342;
pub const DIMBLK1: i16 = 343;
pub const DIMBLK2: i16 = 344;
pub const DIMLTYPE: i16 = 345;
pub const DIMLTEX1: i16 = 346;
pub const DIMLTEX2: i16 = 347;
pub const DIMLWE: i16 = 372;
/// Every (code, value) override present in the `ACAD`/`DSTYLE` record. /// Every (code, value) override present in the `ACAD`/`DSTYLE` record.
pub fn pairs(xd: &ExtendedData) -> Vec<(i16, XDataValue)> { pub fn pairs(xd: &ExtendedData) -> Vec<(i16, XDataValue)> {
@ -90,6 +157,16 @@ pub fn handle(xd: &ExtendedData, code: i16) -> Option<Handle> {
}) })
} }
pub fn string(xd: &ExtendedData, code: i16) -> Option<String> {
pairs(xd)
.into_iter()
.find(|(c, _)| *c == code)
.and_then(|(_, value)| match value {
XDataValue::String(text) => Some(text),
_ => None,
})
}
fn write_pairs(doc: &mut CadDocument, handle: Handle, pairs: Vec<(i16, XDataValue)>) { fn write_pairs(doc: &mut CadDocument, handle: Handle, pairs: Vec<(i16, XDataValue)>) {
let Some(entity) = doc.get_entity(handle) else { let Some(entity) = doc.get_entity(handle) else {
return; return;

View file

@ -1389,27 +1389,6 @@ fn apply_dimension_breaks(
*lines = output; *lines = output;
} }
pub(crate) fn uses_custom_arrow_blocks(document: &CadDocument, dim: &Dimension) -> bool {
let style_name = &dim.base().style_name;
let Some(style) = document.dim_styles.iter().find(|style| {
style.name.eq_ignore_ascii_case(style_name)
|| (style_name.trim().is_empty() && style.name.eq_ignore_ascii_case("Standard"))
}) else {
return false;
};
if style.dimtsz > 1e-9 {
return false;
}
let is_custom = |handle| {
crate::scene::convert::tessellate::arrow_block_is_custom(document, handle)
};
if style.dimsah {
is_custom(style.dimblk1) || is_custom(style.dimblk2)
} else {
is_custom(style.dimblk)
}
}
pub trait DimensionTess { pub trait DimensionTess {
fn tessellate( fn tessellate(
&self, &self,
@ -1481,10 +1460,126 @@ fn tessellate_dimension_inner(
// recursive call goes through the LOD ladder, not the truck path.) // recursive call goes through the LOD ladder, not the truck path.)
let style_name = &dim.base().style_name; let style_name = &dim.base().style_name;
let style = document.dim_styles.iter().find(|s| { let source_style = document.dim_styles.iter().find(|s| {
s.name.eq_ignore_ascii_case(style_name) s.name.eq_ignore_ascii_case(style_name)
|| (style_name.trim().is_empty() && s.name.eq_ignore_ascii_case("Standard")) || (style_name.trim().is_empty() && s.name.eq_ignore_ascii_case("Standard"))
}); });
let mut effective_style = source_style.cloned();
if let Some(style) = &mut effective_style {
use crate::entities::dim_override as ov;
let data = &dim.base().common.extended_data;
macro_rules! real {
($field:ident, $code:ident) => {
if let Some(value) = ov::real(data, ov::$code) {
style.$field = value;
}
};
}
macro_rules! int {
($field:ident, $code:ident) => {
if let Some(value) = ov::int(data, ov::$code) {
style.$field = value;
}
};
}
macro_rules! flag {
($field:ident, $code:ident) => {
if let Some(value) = ov::int(data, ov::$code) {
style.$field = value != 0;
}
};
}
macro_rules! handle {
($field:ident, $code:ident) => {
if let Some(value) = ov::handle(data, ov::$code) {
style.$field = value;
}
};
}
real!(dimscale, DIMSCALE);
real!(dimasz, DIMASZ);
real!(dimexo, DIMEXO);
real!(dimdli, DIMDLI);
real!(dimexe, DIMEXE);
real!(dimrnd, DIMRND);
real!(dimdle, DIMDLE);
real!(dimtp, DIMTP);
real!(dimtm, DIMTM);
real!(dimfxl, DIMFXL);
real!(dimjogang, DIMJOGANG);
real!(dimtxt, DIMTXT);
real!(dimcen, DIMCEN);
real!(dimtsz, DIMTSZ);
real!(dimaltf, DIMALTF);
real!(dimlfac, DIMLFAC);
real!(dimtvp, DIMTVP);
real!(dimtfac, DIMTFAC);
real!(dimgap, DIMGAP);
real!(dimaltrnd, DIMALTRND);
flag!(dimtol, DIMTOL);
flag!(dimlim, DIMLIM);
flag!(dimtih, DIMTIH);
flag!(dimtoh, DIMTOH);
flag!(dimse1, DIMSE1);
flag!(dimse2, DIMSE2);
flag!(dimalt, DIMALT);
flag!(dimtofl, DIMTOFL);
flag!(dimsah, DIMSAH);
flag!(dimtix, DIMTIX);
flag!(dimsoxd, DIMSOXD);
flag!(dimsd1, DIMSD1);
flag!(dimsd2, DIMSD2);
flag!(dimupt, DIMUPT);
flag!(dimfxlon, DIMFXLON);
flag!(dimtxtdirection, DIMTXTDIRECTION);
int!(dimzin, DIMZIN);
int!(dimazin, DIMAZIN);
int!(dimarcsym, DIMARCSYM);
int!(dimclrd, DIMCLRD);
int!(dimclre, DIMCLRE);
int!(dimclrt, DIMCLRT);
int!(dimadec, DIMADEC);
int!(dimaltd, DIMALTD);
int!(dimdec, DIMDEC);
int!(dimtdec, DIMTDEC);
int!(dimaltu, DIMALTU);
int!(dimalttd, DIMALTTD);
int!(dimaunit, DIMAUNIT);
int!(dimfrac, DIMFRAC);
int!(dimlunit, DIMLUNIT);
int!(dimdsep, DIMDSEP);
int!(dimtmove, DIMTMOVE);
int!(dimjust, DIMJUST);
int!(dimtolj, DIMTOLJ);
int!(dimtzin, DIMTZIN);
int!(dimaltz, DIMALTZ);
int!(dimalttz, DIMALTTZ);
int!(dimatfit, DIMATFIT);
int!(dimtfill, DIMTFILL);
int!(dimtfillclr, DIMTFILLCLR);
int!(dimlwd, DIMLWD);
int!(dimlwe, DIMLWE);
handle!(dimldrblk, DIMLDRBLK);
handle!(dimblk, DIMBLK);
handle!(dimblk1, DIMBLK1);
handle!(dimblk2, DIMBLK2);
handle!(dimltex_handle, DIMLTYPE);
handle!(dimltex1_handle, DIMLTEX1);
handle!(dimltex2_handle, DIMLTEX2);
if let Some(value) = ov::string(data, ov::DIMPOST) {
style.dimpost = value;
}
if let Some(value) = ov::string(data, ov::DIMAPOST) {
style.dimapost = value;
}
if let Some(value) = ov::handle(data, ov::DIMTXSTY) {
style.dimtxsty_handle = value;
if let Some(record) = document.text_styles.iter().find(|record| record.handle == value) {
style.dimtxsty = record.name.clone();
}
}
}
let style = effective_style.as_ref();
// DIMSCALE rule: // DIMSCALE rule:
// dimstyle.dimscale > 0 → final multiplier; ignore anno_scale. // dimstyle.dimscale > 0 → final multiplier; ignore anno_scale.
@ -1655,7 +1750,7 @@ fn tessellate_dimension_inner(
// preserved on save: // preserved on save:
// - base.insertion_point: legacy anchor reference; render uses // - base.insertion_point: legacy anchor reference; render uses
// text_middle_point + dim-line geometry instead. // text_middle_point + dim-line geometry instead.
// - base.block_name: AutoCAD-style "*D..." anonymous block name for // - base.block_name: generated anonymous block name for
// the dim graphics — we re-tessellate so don't need it. // the dim graphics — we re-tessellate so don't need it.
// - base.version: DXF format marker (metadata only). // - base.version: DXF format marker (metadata only).
let _ = ( let _ = (
@ -2391,7 +2486,7 @@ fn append_linear_dimension(
// DIMDLE: dim line overshoots the ext line by `dimdle` at each end, // DIMDLE: dim line overshoots the ext line by `dimdle` at each end,
// but only when ticks are in use (DIMTSZ > 0). With arrowheads this // but only when ticks are in use (DIMTSZ > 0). With arrowheads this
// is ignored, matching AutoCAD. // is ignored.
let dle = if params.ticks { params.dimdle } else { 0.0 }; let dle = if params.ticks { params.dimdle } else { 0.0 };
let dir_d1_to_d2 = normalized_or(d2 - d1, axis); let dir_d1_to_d2 = normalized_or(d2 - d1, axis);
let d1_out = d1 - dir_d1_to_d2 * dle; let d1_out = d1 - dir_d1_to_d2 * dle;
@ -2404,30 +2499,50 @@ fn append_linear_dimension(
let gap = (d2 - d1).length(); let gap = (d2 - d1).length();
let arrows_outside = !params.ticks && params.arrow_len > 1e-6 && gap < 2.0 * params.arrow_len; let arrows_outside = !params.ticks && params.arrow_len > 1e-6 && gap < 2.0 * params.arrow_len;
// DIMSD1/DIMSD2: when *both* set, omit the dim line entirely. AutoCAD // The two suppression flags address the portions at the first and second
// splits at text otherwise — without that pivot info, leave as-is. // measured points independently. The text gap is also the natural split;
if !(suppress.dim1 && suppress.dim2) { // when there is no gap, split at the projected text position or midpoint.
// Break the dim line where the text sits on it, so a DIMTFILL let line_dir = normalized_or(d2_out - d1_out, axis);
// background reads over the line (2-D fills draw under all wires, so the let line_len = (d2_out - d1_out).length();
// line is gapped rather than z-masked). let mut split = line_len * 0.5;
let mut drew = false; let mut left_end = split;
if let Some((tc, half_w, half_h)) = params.text_break { let mut right_start = split;
let dir = normalized_or(d2_out - d1_out, axis); if let Some((text_center, half_width, half_height)) = params.text_break {
let len = (d2_out - d1_out).length(); let along = (text_center - d1_out).dot(line_dir);
let along = (tc - d1_out).dot(dir); if along > 0.0 && along < line_len {
let perp_dist = (tc - (d1_out + dir * along)).length(); split = along;
if perp_dist < half_h && along - half_w > 0.0 && along + half_w < len { left_end = split;
add_segment(&mut g.dim_lines, d1_out, d1_out + dir * (along - half_w)); right_start = split;
add_segment(&mut g.dim_lines, d1_out + dir * (along + half_w), d2_out); let perpendicular = (text_center - (d1_out + line_dir * along)).length();
drew = true; if perpendicular < half_height
&& along - half_width > 0.0
&& along + half_width < line_len
{
left_end = along - half_width;
right_start = along + half_width;
} }
} }
if !drew { }
add_segment(&mut g.dim_lines, d1_out, d2_out); if !suppress.dim1 && left_end > 1e-6 {
add_segment(
&mut g.dim_lines,
d1_out,
d1_out + line_dir * left_end,
);
}
if !suppress.dim2 && line_len - right_start > 1e-6 {
add_segment(
&mut g.dim_lines,
d1_out + line_dir * right_start,
d2_out,
);
} }
if arrows_outside && !params.dimsoxd { if arrows_outside && !params.dimsoxd {
let stub = params.arrow_len * 2.0; let stub = params.arrow_len * 2.0;
if !suppress.dim1 {
add_segment(&mut g.dim_lines, d1 - dir_d1_to_d2 * stub, d1); add_segment(&mut g.dim_lines, d1 - dir_d1_to_d2 * stub, d1);
}
if !suppress.dim2 {
add_segment(&mut g.dim_lines, d2, d2 + dir_d1_to_d2 * stub); add_segment(&mut g.dim_lines, d2, d2 + dir_d1_to_d2 * stub);
} }
} }
@ -2838,8 +2953,8 @@ fn dimension_text_parts(
let base = dim.base(); let base = dim.base();
let is_angular = matches!(dim, Dimension::Angular2Ln(_) | Dimension::Angular3Pt(_)); let is_angular = matches!(dim, Dimension::Angular2Ln(_) | Dimension::Angular3Pt(_));
// Auto-generated body (the value AutoCAD would emit if the user did not // Auto-generated body used when the user did not override it. Built first
// override it). Built first so user_text "<>" substitution can re-use it. // so user_text "<>" substitution can re-use it.
let primary_raw = if is_angular { let primary_raw = if is_angular {
format_angular_value(dim.measurement(), style) format_angular_value(dim.measurement(), style)
} else { } else {
@ -3168,7 +3283,7 @@ fn format_fractional(value: f64, dimfrac: i16) -> String {
} }
/// Power-of-two denominator for architectural / fractional output. DIMFRAC is /// Power-of-two denominator for architectural / fractional output. DIMFRAC is
/// the exponent-ish knob AutoCAD pairs with DIMLUNIT; clamp it to a readable /// the exponent-like fraction-format knob; clamp it to a readable
/// range (16ths … 1024ths). /// range (16ths … 1024ths).
fn arch_denom(dimfrac: i16) -> u64 { fn arch_denom(dimfrac: i16) -> u64 {
let exp = (dimfrac.clamp(0, 8) as u32).max(2) + 2; // 4..=10 → 16..=1024 let exp = (dimfrac.clamp(0, 8) as u32).max(2) + 2; // 4..=10 → 16..=1024

View file

@ -2982,6 +2982,188 @@ pub(super) fn message_id(source: &str) -> Option<&'static str> {
"── Custom Overrides (SHORTCUTS SET) ──────────────────" => Some("catalog-custom-overrides-shortcuts-set-07be563dec"), "── Custom Overrides (SHORTCUTS SET) ──────────────────" => Some("catalog-custom-overrides-shortcuts-set-07be563dec"),
"── Function Keys ──────────────────────────────────────" => Some("catalog-function-keys-b5a2df24e9"), "── Function Keys ──────────────────────────────────────" => Some("catalog-function-keys-b5a2df24e9"),
"── Other Keys ──────────────────────────────────────────" => Some("catalog-other-keys-a882812cf2"), "── Other Keys ──────────────────────────────────────────" => Some("catalog-other-keys-a882812cf2"),
"Always keep text between extension lines" => Some("dimstyle-keep-text-between-extension-lines"),
"Angular dimensions" => Some("dimstyle-angular-dimensions"),
"Arc length symbol" => Some("dimstyle-arc-length-symbol"),
"Arrowheads" => Some("dimstyle-arrowheads"),
"Available style" => Some("dimstyle-available-style"),
"Baseline spacing" => Some("dimstyle-baseline-spacing"),
"Basic" => Some("dimstyle-basic"),
"Both arrowheads" => Some("dimstyle-both-arrowheads"),
"Center mark" => Some("dimstyle-center-mark"),
"Center mark size" => Some("dimstyle-center-mark-size"),
"Center marks" => Some("dimstyle-center-marks"),
"Centerlines" => Some("dimstyle-centerlines"),
"Compare with" => Some("dimstyle-compare-with"),
"Current style" => Some("dimstyle-current-style"),
"Deviation" => Some("dimstyle-deviation"),
"Different sections" => Some("dimstyle-different-sections"),
"Dimension line color" => Some("dimstyle-dimension-line-color"),
"Dimension linetype" => Some("dimstyle-dimension-linetype"),
"Dimension lineweight" => Some("dimstyle-dimension-lineweight"),
"Display alternate units" => Some("dimstyle-display-alternate-units"),
"Draw dimension line between extension lines" => Some("dimstyle-draw-dimension-line-between-extension-lines"),
"Extend beyond dimension line" => Some("dimstyle-extend-beyond-dimension-line"),
"Extend beyond ticks" => Some("dimstyle-extend-beyond-ticks"),
"Extension line color" => Some("dimstyle-extension-line-color"),
"Extension lineweight" => Some("dimstyle-extension-lineweight"),
"Feet and inches" => Some("dimstyle-feet-and-inches"),
"Fine tuning" => Some("dimstyle-fine-tuning"),
"First arrowhead" => Some("dimstyle-first-arrowhead"),
"First extension linetype" => Some("dimstyle-first-extension-linetype"),
"Fit options" => Some("dimstyle-fit-options"),
"Fixed length" => Some("dimstyle-fixed-length"),
"Fraction format" => Some("dimstyle-fraction-format"),
"Height scale" => Some("dimstyle-height-scale"),
"Horizontal placement" => Some("dimstyle-horizontal-placement"),
"Keep text horizontal when inside" => Some("dimstyle-keep-text-horizontal-inside"),
"Keep text horizontal when outside" => Some("dimstyle-keep-text-horizontal-outside"),
"Leader arrowhead" => Some("dimstyle-leader-arrowhead"),
"Limits" => Some("dimstyle-limits"),
"Linear dimensions" => Some("dimstyle-linear-dimensions"),
"Lower value" => Some("dimstyle-lower-value"),
"Measurement scale" => Some("dimstyle-measurement-scale"),
"Measurement template" => Some("dimstyle-measurement-template"),
"Method" => Some("dimstyle-method"),
"Multiplier" => Some("dimstyle-multiplier"),
"No differences" => Some("dimstyle-no-differences"),
"Not applicable" => Some("dimstyle-not-applicable"),
"Offset from measured point" => Some("dimstyle-offset-from-measured-point"),
"Place text manually while creating dimensions" => Some("dimstyle-place-text-manually"),
"Radius jog angle (°)" => Some("dimstyle-radius-jog-angle"),
"Read left to right" => Some("dimstyle-read-left-to-right"),
"Referenced style · Read only" => Some("dimstyle-referenced-read-only"),
"Round off" => Some("dimstyle-round-off"),
"Second arrowhead" => Some("dimstyle-second-arrowhead"),
"Second extension linetype" => Some("dimstyle-second-extension-linetype"),
"Show zero feet and zero inches" => Some("dimstyle-show-zero-feet-and-inches"),
"Show zero feet; suppress zero inches" => Some("dimstyle-show-zero-feet-suppress-zero-inches"),
"Suppress arrows when they do not fit" => Some("dimstyle-suppress-arrows-when-no-fit"),
"Suppress first dimension line" => Some("dimstyle-suppress-first-dimension-line"),
"Suppress first extension line" => Some("dimstyle-suppress-first-extension-line"),
"Suppress second dimension line" => Some("dimstyle-suppress-second-dimension-line"),
"Suppress second extension line" => Some("dimstyle-suppress-second-extension-line"),
"Suppress zero feet and zero inches" => Some("dimstyle-suppress-zero-feet-and-inches"),
"Suppress zero feet; show zero inches" => Some("dimstyle-suppress-zero-feet-show-zero-inches"),
"Symbols and Arrows" => Some("dimstyle-symbols-and-arrows"),
"Symmetrical" => Some("dimstyle-symmetrical"),
"Text background" => Some("dimstyle-text-background"),
"Text gap" => Some("dimstyle-text-gap"),
"The selected text style fixes the height at %{height}." => Some("dimstyle-fixed-text-height-note"),
"Tick size" => Some("dimstyle-tick-size"),
"Tolerance zero suppression" => Some("dimstyle-tolerance-zero-suppression"),
"Unit format" => Some("dimstyle-unit-format"),
"Upper value" => Some("dimstyle-upper-value"),
"Use <> as the measured value; text before and after it becomes the prefix and suffix." => Some("dimstyle-measurement-template-help"),
"Use different first and second arrowheads" => Some("dimstyle-use-different-arrowheads"),
"Use fixed-length extension lines" => Some("dimstyle-use-fixed-length-extension-lines"),
"Used in drawing" => Some("dimstyle-used-in-drawing"),
"Vertical offset" => Some("dimstyle-vertical-offset"),
"Vertical placement" => Some("dimstyle-vertical-placement"),
"Vertical position" => Some("dimstyle-vertical-position"),
"When space is limited" => Some("dimstyle-when-space-is-limited"),
"When text is moved" => Some("dimstyle-when-text-is-moved"),
"Zero suppression" => Some("dimstyle-zero-suppression"),
"Add a leader" => Some("dimstyle-add-a-leader"),
"Automatic" => Some("dimstyle-automatic"),
"By block" => Some("dimstyle-by-block"),
"Comma" => Some("dimstyle-comma"),
"Degrees, minutes, seconds" => Some("dimstyle-degrees-minutes-seconds"),
"Keep dimension line with text" => Some("dimstyle-keep-dimension-line-with-text"),
"Leading and trailing" => Some("dimstyle-leading-and-trailing"),
"Move arrows outside first" => Some("dimstyle-move-arrows-outside-first"),
"Move text and arrows outside" => Some("dimstyle-move-text-and-arrows-outside"),
"Move text freely" => Some("dimstyle-move-text-freely"),
"Move text outside first" => Some("dimstyle-move-text-outside-first"),
"Period" => Some("dimstyle-period"),
"Space" => Some("dimstyle-space"),
"Alignment spacing" => Some("style-manager-alignment-spacing"),
"Always left align" => Some("style-manager-always-left-align"),
"Attachment direction" => Some("style-manager-attachment-direction"),
"Background fill enabled" => Some("style-manager-background-fill-enabled"),
"Big font" => Some("style-manager-big-font"),
"Block scale X" => Some("style-manager-block-scale-x"),
"Block scale Y" => Some("style-manager-block-scale-y"),
"Block scale Z" => Some("style-manager-block-scale-z"),
"Borders" => Some("style-manager-borders"),
"Bottom attachment" => Some("style-manager-bottom-attachment"),
"Break gap size" => Some("style-manager-break-gap-size"),
"Caps and Fill" => Some("style-manager-caps-and-fill"),
"Content type" => Some("style-manager-content-type"),
"Data Row" => Some("style-manager-data-row"),
"Data type" => Some("style-manager-data-type"),
"Default text" => Some("style-manager-default-text"),
"Different" => Some("style-manager-different"),
"Display joints" => Some("style-manager-display-joints"),
"Drawing height" => Some("style-manager-drawing-height"),
"Drawing scale" => Some("style-manager-drawing-scale"),
"Effects" => Some("style-manager-effects"),
"Elements" => Some("style-manager-elements"),
"End caps" => Some("style-manager-end-caps"),
"Fill and joints" => Some("style-manager-fill-and-joints"),
"Fill enabled" => Some("style-manager-fill-enabled"),
"First segment angle" => Some("style-manager-first-segment-angle"),
"Fixed height" => Some("style-manager-fixed-height"),
"Flow direction" => Some("style-manager-flow-direction"),
"Font file" => Some("style-manager-font-file"),
"Fonts" => Some("style-manager-fonts"),
"Format string" => Some("style-manager-format-string"),
"Header" => Some("style-manager-header"),
"Header Row" => Some("style-manager-header-row"),
"Horizontal margin" => Some("style-manager-horizontal-margin"),
"Inner arcs" => Some("style-manager-inner-arcs"),
"Inside horizontal" => Some("style-manager-inside-horizontal"),
"Inside vertical" => Some("style-manager-inside-vertical"),
"Leader draw order" => Some("style-manager-leader-draw-order"),
"Leader note" => Some("style-manager-leader-note"),
"Left attachment" => Some("style-manager-left-attachment"),
"Line color" => Some("style-manager-line-color"),
"Line type" => Some("style-manager-line-type"),
"Line weight" => Some("style-manager-line-weight"),
"Maximum leader points" => Some("style-manager-maximum-leader-points"),
"Multileader draw order" => Some("style-manager-multileader-draw-order"),
"No preview" => Some("style-manager-no-preview"),
"Oblique angle" => Some("style-manager-oblique-angle"),
"Outer arc" => Some("style-manager-outer-arc"),
"Paper height" => Some("style-manager-paper-height"),
"Paper text height" => Some("style-manager-paper-text-height"),
"Path type" => Some("style-manager-path-type"),
"Right attachment" => Some("style-manager-right-attachment"),
"Scale factor" => Some("style-manager-scale-factor"),
"Second segment angle" => Some("style-manager-second-segment-angle"),
"Size and Effects" => Some("style-manager-size-and-effects"),
"Start caps" => Some("style-manager-start-caps"),
"Stroke fonts" => Some("style-manager-stroke-fonts"),
"Suppress header row" => Some("style-manager-suppress-header-row"),
"Suppress title row" => Some("style-manager-suppress-title-row"),
"System font" => Some("style-manager-system-font"),
"System fonts" => Some("style-manager-system-fonts"),
"Text alignment" => Some("style-manager-text-alignment"),
"Text angle" => Some("style-manager-text-angle"),
"Title Row" => Some("style-manager-title-row"),
"Top attachment" => Some("style-manager-top-attachment"),
"Unit type" => Some("style-manager-unit-type"),
"Vertical margin" => Some("style-manager-vertical-margin"),
"font family…" => Some("style-manager-font-family-placeholder"),
"Bottom to top" => Some("style-manager-bottom-to-top"),
"Arrowhead first" => Some("style-manager-arrowhead-first"),
"Content first" => Some("style-manager-content-first"),
"Leader first" => Some("style-manager-leader-first"),
"Straight" => Some("style-manager-straight"),
"Parallel to last segment" => Some("style-manager-parallel-last-segment"),
"Top of top line" => Some("style-manager-top-of-top-line"),
"Middle of top line" => Some("style-manager-middle-of-top-line"),
"Middle of text" => Some("style-manager-middle-of-text"),
"Middle of bottom line" => Some("style-manager-middle-of-bottom-line"),
"Bottom of bottom line" => Some("style-manager-bottom-of-bottom-line"),
"Bottom line" => Some("style-manager-bottom-line"),
"Underline bottom line" => Some("style-manager-underline-bottom-line"),
"Underline top line" => Some("style-manager-underline-top-line"),
"Underline all text" => Some("style-manager-underline-all-text"),
"Center of text" => Some("style-manager-center-of-text"),
"Center of text with overline" => Some("style-manager-center-of-text-overline"),
"Block extents" => Some("style-manager-block-extents"),
"Base point" => Some("style-manager-base-point"),
_ => None, _ => None,
} }
} }

View file

@ -28,7 +28,7 @@ const MAX_NESTING_DEPTH: usize = 32;
/// Skip wires whose world-AABB projects to fewer than this many pixels in /// Skip wires whose world-AABB projects to fewer than this many pixels in
/// the active view. Picks up tiny detail at zoom-out so the tessellator /// the active view. Picks up tiny detail at zoom-out so the tessellator
/// doesn't waste time on geometry that contributes a few sub-pixel marks /// doesn't waste time on geometry that contributes a few sub-pixel marks
/// to the final image. 2 px is the AutoCAD-default "small element" floor /// to the final image. Two pixels is a practical small-element floor:
/// — visibly the same image, dramatically fewer wires. /// — visibly the same image, dramatically fewer wires.
const MIN_PIXEL_SIZE: f32 = 2.0; const MIN_PIXEL_SIZE: f32 = 2.0;
@ -426,66 +426,16 @@ fn build_defn(
nested_ins, doc, bg_color, depth_map, nested_ins, doc, bg_color, depth_map,
))); )));
} }
// A dimension nested in a block bakes its geometry (extension / EntityType::Dimension(_) => {
// dim lines, arrows, text) into a per-instance `*D` block, exactly for wire in tessellate_sub_local(
// like a top-level dimension. The top-level path expands that block
// in `tessellate_entity`; the block-expand path calls the plain
// `tessellate::tessellate`, which does NOT, so nested dimensions
// drew nothing. Expand the `*D` block's entities here as block-local
// subs so they transform with the parent insert. (Empty block_name
// — a non-baked dimension — falls through to the default arm.)
EntityType::Dimension(dim)
if !dim.base().block_name.trim().is_empty()
&& !crate::entities::dimension::uses_custom_arrow_blocks(doc, dim) =>
{
let dblk = doc
.block_records
.iter()
.find(|br| br.name.eq_ignore_ascii_case(&dim.base().block_name));
if let Some(dblk) = dblk {
// The `*D` block content is baked in the coordinate space it
// occupied when the dimension was created — for a dimension
// inside a block that is often the ORIGINAL WCS, not the
// block-local space. The dimension's insertion_point (DXF
// 12) is the offset that maps the baked content into the
// dimension's own (block-local) space; it is zero for
// dimensions baked in place, so this is a no-op there.
let ins = dim.base().insertion_point;
for &deh in &dblk.entity_handles {
let Some(dsub) = doc.get_entity(deh) else {
continue;
};
// Definition points are baked as POINTs on Defpoints —
// grip markers, never drawn (matches the top-level path).
if matches!(dsub, EntityType::Point(_)) {
continue;
}
if dsub.common().invisible || layer_hidden(doc, &dsub.common().layer) {
continue;
}
let mut placed = dsub.clone();
placed.as_entity_mut().translate(ins);
// An arrowhead is baked as a nested INSERT of an arrow
// block — route it through the nested-ref machinery so
// the arrow block expands (tessellate_sub_local doesn't
// expand inserts).
if let EntityType::Insert(arrow) = &placed {
subs.push(LocalSub::Nested(build_nested_ref(
arrow, doc, bg_color, depth_map,
)));
} else {
for lw in tessellate_sub_local(
doc, doc,
&placed, entity,
anno_scale, anno_scale,
annotation_scale_handle, annotation_scale_handle,
bg_color, bg_color,
depth_map, depth_map,
) { ) {
subs.push(LocalSub::Wire(lw)); subs.push(LocalSub::Wire(wire));
}
}
}
} }
} }
// A table nested in a block bakes its geometry (gridlines, cell // A table nested in a block bakes its geometry (gridlines, cell
@ -706,7 +656,23 @@ fn tessellate_sub_local(
// Pass `local_offset` as the f64 world-offset so tessellate subtracts it // Pass `local_offset` as the f64 world-offset so tessellate subtracts it
// before casting to f32 — same precision-preservation trick used for // before casting to f32 — same precision-preservation trick used for
// top-level entities, applied per-defn. // top-level entities, applied per-defn.
let wires_out = tessellate::tessellate( let wires_out = if let EntityType::Dimension(dimension) = sub {
use crate::entities::dimension::DimensionTess;
dimension.tessellate(
doc,
h,
false,
sub_color,
lw_px,
anno_scale,
&HashSet::default(),
None,
bg_color,
None,
None,
)
} else {
tessellate::tessellate(
doc, doc,
h, h,
sub, sub,
@ -720,7 +686,8 @@ fn tessellate_sub_local(
None, None,
bg_color, bg_color,
false, false,
); )
};
if wires_out.is_empty() { if wires_out.is_empty() {
return vec![]; return vec![];
} }

View file

@ -543,8 +543,8 @@ pub(crate) fn tessellate_entity(
// An entity from an application we have no reader for (e.g. an Autodesk // An entity from an application we have no reader for (e.g. an Autodesk
// Raster Design embedded raster image) arrives as `Unknown`. Its own data is // Raster Design embedded raster image) arrives as `Unknown`. Its own data is
// a private format we cannot decode — but it usually ships a proxy-graphics // a private format we cannot decode — but it usually ships a proxy-graphics
// blob, the vector preview its author cached for exactly this case. AutoCAD // blob, the vector preview its author cached for exactly this case. Draw it
// draws that when the object enabler is missing; draw it too, so the entity // when the object enabler is missing, so the entity
// occupies its real place instead of silently disappearing. // occupies its real place instead of silently disappearing.
let proxy_blob: Option<std::borrow::Cow<'_, [u8]>> = match e { let proxy_blob: Option<std::borrow::Cow<'_, [u8]>> = match e {
EntityType::Unknown(_) => e EntityType::Unknown(_) => e
@ -665,7 +665,7 @@ pub(crate) fn tessellate_entity(
// //
// The DWG reader decodes the two cut-line endpoints, the signed end ticks // The DWG reader decodes the two cut-line endpoints, the signed end ticks
// and the identifier. Synthesize the end ticks + arrowheads + label glyphs // and the identifier. Synthesize the end ticks + arrowheads + label glyphs
// so the mark is visible on the layout, the way AutoCAD draws it. The raw // so the mark is visible on the layout. The raw
// record is still preserved for lossless write-back. // record is still preserved for lossless write-back.
if let EntityType::SectionSymbol(s) = e { if let EntityType::SectionSymbol(s) = e {
return section_symbol_wires( return section_symbol_wires(
@ -736,118 +736,6 @@ pub(crate) fn tessellate_entity(
}]; }];
} }
// ── Dimension baked-block fast path ─────────────────────────────────────
//
// A saved dimension may carry final geometry (extension lines, dim line,
// arrows and text) in a per-instance block — usually
// `*D<n>`, but custom names like `DIMBLOCK###-4NP` also occur. When the
// block exists we render its contents through `tessellate_entity` so
// sub-Text/MText get the standard baseline/greek/full LOD ladder, and
// DIMTXT × DIMSCALE isn't re-applied on already-baked geometry.
if let EntityType::Dimension(dim) = e {
let block_name = &dim.base().block_name;
if !block_name.trim().is_empty()
&& !crate::entities::dimension::uses_custom_arrow_blocks(document, dim)
{
if let Some(br) = document
.block_records
.iter()
.find(|br| br.name.eq_ignore_ascii_case(block_name))
{
if !br.entity_handles.is_empty() {
let mut wires: Vec<WireModel> = Vec::with_capacity(br.entity_handles.len());
// The Dimension's own layer style — layer-0 inheritance
// target for baked sub-entities on layer "0" (#221).
let dim_l0_color = view::render::adapt_to_bg(
view::render::layer_render_style(document, &e.common().layer).color,
bg_color,
);
let dim_l0_aci = document
.layers
.get(&e.common().layer)
.map(|l| match &l.color {
acadrust::types::Color::Index(i) => *i,
_ => 0,
})
.unwrap_or(0);
for &eh in &br.entity_handles {
let Some(sub) = document.get_entity(eh) else {
continue;
};
// A dimension's definition points are baked into the
// block as POINTs on the Defpoints layer. They are grip
// markers rather than PDMODE display geometry, so
// geometry — so rendering them adds a stray tick at each
// measured point that makes the extension lines look like
// they run past the geometry. Skip them.
if matches!(sub, EntityType::Point(_)) {
continue;
}
// Sub-entities inside *D### / DIMBLOCK## blocks
// typically use ByBlock color/linetype/lineweight —
// they should inherit from the Dimension entity.
let has_book_color =
view::render::has_resolved_book_color(document, sub);
let sub_color_is_byblock = !has_book_color
&& sub.common().color == acadrust::types::Color::ByBlock;
let sub_is_l0_bylayer =
!has_book_color
&& view::render::is_effective_layer_zero(&sub.common().layer)
&& sub.common().color == acadrust::types::Color::ByLayer;
let sub_wires = tessellate_entity(
document,
selected,
active_viewport,
bg_color,
// Block contents are baked at the final WCS size —
// don't let downstream paths re-apply anno_scale.
1.0,
None,
sub,
block_cache,
view_aabb,
world_per_pixel,
paper_space,
);
for mut w in sub_wires {
w.name = h.value().to_string();
// Override ByBlock colour with the dim's resolved
// colour so text matches `DIMCLRT`-style behaviour
// (or layer colour) instead of the raw ByBlock
// fallback that render_style_for produces. A layer-0
// sub inherits the dim's layer colour instead.
if sub_color_is_byblock {
w.color = if sel {
WireModel::SELECTED
} else {
entity_color
};
w.aci = aci;
} else if sub_is_l0_bylayer && !sel {
w.color = dim_l0_color;
w.aci = dim_l0_aci;
}
wires.push(w);
}
}
if !wires.is_empty() {
let aabb = entity_aabb(e);
for w in &mut wires {
// Empty SDF-text cells keep their tight glyph-box
// AABB; only stroke/fill wires take the whole-block
// box as a broad-phase pick hint.
if !w.points.is_empty() || !w.fill_tris.is_empty() {
set_wire_aabb(w, aabb);
}
}
return wires;
}
}
}
}
// Fall through to the synthesis path below when no block is attached.
}
if let EntityType::Dimension(dim) = e { if let EntityType::Dimension(dim) = e {
let aabb = entity_aabb(e); let aabb = entity_aabb(e);
use crate::entities::dimension::DimensionTess; use crate::entities::dimension::DimensionTess;
@ -904,7 +792,7 @@ pub(crate) fn tessellate_entity(
// ── Table baked-block fast path ───────────────────────────────────────── // ── Table baked-block fast path ─────────────────────────────────────────
// //
// AutoCAD bakes a Table's final rendered geometry (cell text, gridlines, // A table may store final rendered geometry (cell text, gridlines,
// fill) into a per-instance block (usually `*T###`) referenced through // fill) into a per-instance block (usually `*T###`) referenced through
// `table.block_record_handle`. The block's text uses the *displayed* // `table.block_record_handle`. The block's text uses the *displayed*
// height; synthesising cells from `self.rows + TableStyle` instead would // height; synthesising cells from `self.rows + TableStyle` instead would

View file

@ -1538,28 +1538,6 @@ pub(crate) fn arrow_from_block(
.unwrap_or_else(|| arrow_from_block_name(None, dimasz)) .unwrap_or_else(|| arrow_from_block_name(None, dimasz))
} }
pub(crate) fn arrow_block_is_custom(
doc: &CadDocument,
handle: acadrust::types::Handle,
) -> bool {
if handle.is_null() {
return false;
}
doc.block_records
.iter()
.find(|record| record.handle == handle)
.is_some_and(|record| {
!record.entity_handles.is_empty()
&& !record.is_layout()
&& !record.is_model_space()
&& !record.is_paper_space()
&& !record.flags.is_xref
&& !record.flags.is_xref_overlay
&& !record.flags.is_external
&& builtin_arrow_from_block_name(&record.name, 1.0).is_none()
})
}
fn arrow_from_block_name(name: Option<&str>, dimasz: f32) -> ArrowKind { fn arrow_from_block_name(name: Option<&str>, dimasz: f32) -> ArrowKind {
name.and_then(|name| builtin_arrow_from_block_name(name, dimasz)) name.and_then(|name| builtin_arrow_from_block_name(name, dimasz))
.unwrap_or(ArrowKind::Triangle { .unwrap_or(ArrowKind::Triangle {
@ -1742,6 +1720,10 @@ fn collect_custom_arrow_entity(
stack.pop(); stack.pop();
return; return;
} }
EntityType::Hatch(hatch) => {
append_custom_hatch_fill(hatch, root_base, fill);
return;
}
_ => {} _ => {}
} }
@ -1766,6 +1748,69 @@ fn collect_custom_arrow_entity(
} }
} }
fn append_custom_hatch_fill(
hatch: &acadrust::entities::Hatch,
base: acadrust::types::Vector3,
out: &mut Vec<[f32; 3]>,
) {
use lyon_tessellation::math::point;
use lyon_tessellation::path::Path;
use lyon_tessellation::{
BuffersBuilder, FillOptions, FillRule, FillTessellator, FillVertex, VertexBuffers,
};
let Some(model) = crate::scene::Scene::hatch_model_from_dxf(hatch, [1.0; 4]) else {
return;
};
let mut builder = Path::builder();
let mut ring: Vec<[f32; 2]> = Vec::new();
let mut finish_ring = |ring: &mut Vec<[f32; 2]>| {
if ring.len() >= 3 {
builder.begin(point(ring[0][0], ring[0][1]));
for p in &ring[1..] {
builder.line_to(point(p[0], p[1]));
}
builder.end(true);
}
ring.clear();
};
for &[x, y] in model.boundary.iter() {
if x.is_nan() || y.is_nan() {
finish_ring(&mut ring);
continue;
}
ring.push([
(model.world_origin[0] + x as f64 - base.x) as f32,
(model.world_origin[1] + y as f64 - base.y) as f32,
]);
}
finish_ring(&mut ring);
let mut geometry: VertexBuffers<[f32; 2], u32> = VertexBuffers::new();
let mut tessellator = FillTessellator::new();
if tessellator
.tessellate_path(
&builder.build(),
&FillOptions::default().with_fill_rule(FillRule::EvenOdd),
&mut BuffersBuilder::new(&mut geometry, |vertex: FillVertex| {
vertex.position().to_array()
}),
)
.is_err()
{
return;
}
let z = -base.z as f32;
out.extend(
geometry
.indices
.iter()
.filter_map(|&index| geometry.vertices.get(index as usize))
.map(|&[x, y]| [x, y, z]),
);
}
fn append_custom_wire_points( fn append_custom_wire_points(
points: &[[f32; 3]], points: &[[f32; 3]],
points_low: &[[f32; 3]], points_low: &[[f32; 3]],
@ -1928,7 +1973,7 @@ fn fallback_geometry(entity: &EntityType) -> Geometry {
/// Extract pre-computed edge-wire points from Solid3D / Region / Body entities. /// Extract pre-computed edge-wire points from Solid3D / Region / Body entities.
/// ///
/// AutoCAD stores explicit wire geometry (from SOLVIEW / 3DPLOT) alongside the /// Some drawings store explicit wire geometry alongside the
/// ACIS data. We use this as a visible fallback when the SAT tessellator /// ACIS data. We use this as a visible fallback when the SAT tessellator
/// produces no mesh (e.g. binary SAB data or unsupported geometry). /// produces no mesh (e.g. binary SAB data or unsupported geometry).
fn solid_wire_fallback(entity: &EntityType) -> Vec<[f64; 3]> { fn solid_wire_fallback(entity: &EntityType) -> Vec<[f64; 3]> {
@ -2117,7 +2162,7 @@ pub(crate) fn add_polyline(points: &mut Vec<[f32; 3]>, polyline: &[Vec3]) {
/// Returns the text position of a dimension in DXF world-space (f64, no offset applied). /// Returns the text position of a dimension in DXF world-space (f64, no offset applied).
/// Used when building a synthetic Text entity so tessellate() can apply world_offset itself. /// Used when building a synthetic Text entity so tessellate() can apply world_offset itself.
/// When the saved `text_middle_point` is zero (i.e. AutoCAD never wrote one), /// When the saved `text_middle_point` is zero (no explicit point was written),
/// computes a fallback from the dim geometry and applies DIMTAD/DIMGAP. /// computes a fallback from the dim geometry and applies DIMTAD/DIMGAP.
pub(crate) fn normalized_or(v: Vec3, fallback: Vec3) -> Vec3 { pub(crate) fn normalized_or(v: Vec3, fallback: Vec3) -> Vec3 {
if v.length_squared() <= 1e-12 { if v.length_squared() <= 1e-12 {

View file

@ -1791,7 +1791,7 @@ impl Scene {
} }
} }
pub(super) fn hatch_model_from_dxf( pub(crate) fn hatch_model_from_dxf(
dxf: &DxfHatch, dxf: &DxfHatch,
color: [f32; 4], color: [f32; 4],
) -> Option<HatchModel> { ) -> Option<HatchModel> {
@ -1814,9 +1814,9 @@ impl Scene {
for path in &dxf.paths { for path in &dxf.paths {
// Skip TEXTBOX boundary paths (flag bit 3). These are text // Skip TEXTBOX boundary paths (flag bit 3). These are text
// bounding-boxes AutoCAD derives for island detection; they are // derived bounding boxes used for island detection; they are
// never drawn or filled. Treating one as a fill boundary paints its // never drawn or filled. Treating one as a fill boundary paints its
// rectangle solid — a phantom bar AutoCAD never shows. // rectangle solid and creates a phantom bar.
if path.flags.bits() & 8 != 0 { if path.flags.bits() & 8 != 0 {
continue; continue;
} }

File diff suppressed because it is too large Load diff

File diff suppressed because it is too large Load diff

View file

@ -1,103 +1,383 @@
//! Multiline Style Manager window — fills the entire OS window. //! Multiline style manager.
use crate::app::Message; use crate::app::Message;
use iced::widget::{column, container, row, scrollable, text};
use iced::{Element, Theme};
use crate::t; use crate::t;
use iced::widget::{button, canvas, checkbox, column, container, row, text, text_input};
use iced::{mouse, Color, Element, Length, Point, Rectangle, Theme};
use std::borrow::Cow; use std::borrow::Cow;
pub struct MlStyleView<'a> {
pub styles: Vec<String>,
pub selected: &'a str,
pub style: Option<&'a acadrust::objects::MLineStyle>,
pub current: String,
pub tab: u8,
pub compare_name: String,
pub compare_opts: Vec<String>,
pub comparison_sections: Vec<String>,
pub in_use: bool,
pub description: &'a str,
pub start_angle: &'a str,
pub end_angle: &'a str,
pub fill_color: &'a str,
pub elements: &'a [[String; 3]],
pub rename_active: Option<&'a str>,
pub rename_buf: &'a str,
}
fn muted_style(theme: &Theme) -> iced::widget::text::Style { fn muted_style(theme: &Theme) -> iced::widget::text::Style {
iced::widget::text::Style { iced::widget::text::Style {
color: Some(theme.palette().background.base.text.scale_alpha(0.68)), color: Some(theme.palette().background.base.text.scale_alpha(0.68)),
} }
} }
pub fn view_window<'a>( fn primary_style(theme: &Theme) -> iced::widget::text::Style {
styles: Vec<String>, iced::widget::text::Style {
selected: &'a str, color: Some(theme.palette().primary.base.color),
selected_style: Option<&'a acadrust::objects::MLineStyle>, }
current_style: String, }
rename_active: Option<&'a str>,
rename_buf: &'a str, fn input_row<'a>(
sizing: crate::ui::modal::ModalSizing, label: Cow<'static, str>,
placeholder: &'static str,
value: &'a str,
field: &'static str,
) -> Element<'a, Message> { ) -> Element<'a, Message> {
// ── Right: Details panel ──────────────────────────────────────────────
let info_row = |label: Cow<'static, str>, val: String| -> Element<'_, Message> {
row![ row![
text(label).size(11).style(muted_style).width(120), text(label).size(11).style(muted_style).width(165),
text(val).size(11), text_input(placeholder, value)
.on_input(move |value| Message::MlStyleEdit { field, value })
.size(11)
.width(190),
] ]
.spacing(8) .spacing(8)
.align_y(iced::Center) .align_y(iced::Center)
.into() .into()
}; }
let details: Element<'_, Message> = if let Some(s) = selected_style { fn toggle<'a>(label: Cow<'static, str>, value: bool, field: &'static str) -> Element<'a, Message> {
let elem_rows: Vec<Element<'_, Message>> = s checkbox(value)
.label(label)
.on_toggle(move |_| Message::MlStyleToggle(field))
.size(14)
.text_size(11)
.into()
}
fn aci_color(value: &str, fallback: Color) -> Color {
crate::ui::color_select::aci_string_to_color(value)
.rgb()
.map(|(r, g, b)| Color::from_rgb8(r, g, b))
.unwrap_or(fallback)
}
struct MLinePreviewCanvas {
description: String,
start_angle: String,
end_angle: String,
fill_color: String,
fill: bool,
joints: bool,
start_square: bool,
start_inner: bool,
start_round: bool,
end_square: bool,
end_inner: bool,
end_round: bool,
elements: Vec<[String; 3]>,
}
fn element_points(offset: f32, bounds: Rectangle) -> [Point; 3] {
let y = bounds.height * 0.48 - offset * 26.0;
[
Point::new(44.0, y),
Point::new(bounds.width * 0.52, y - 16.0),
Point::new(bounds.width - 44.0, y + 5.0),
]
}
fn draw_arc(frame: &mut canvas::Frame, center: Point, radius: f32, start_side: bool, color: Color) {
let path = canvas::Path::new(|builder| {
for step in 0..=18 {
let t = step as f32 / 18.0;
let angle = if start_side {
std::f32::consts::FRAC_PI_2 + t * std::f32::consts::PI
} else {
-std::f32::consts::FRAC_PI_2 + t * std::f32::consts::PI
};
let point = Point::new(center.x + radius * angle.cos(), center.y + radius * angle.sin());
if step == 0 {
builder.move_to(point);
} else {
builder.line_to(point);
}
}
});
frame.stroke(&path, canvas::Stroke::default().with_color(color).with_width(1.1));
}
impl canvas::Program<Message> for MLinePreviewCanvas {
type State = ();
fn draw(
&self,
_state: &(),
renderer: &iced::Renderer,
theme: &Theme,
bounds: Rectangle,
_cursor: mouse::Cursor,
) -> Vec<canvas::Geometry> {
let mut frame = canvas::Frame::new(renderer, bounds.size());
let ink = theme.palette().background.base.text;
let mut parsed = self
.elements .elements
.iter() .iter()
.enumerate() .map(|element| {
.map(|(idx, e)| { (
let color_str = match &e.color { element[0].trim().parse::<f32>().unwrap_or(0.0),
acadrust::types::Color::ByLayer => "ByLayer".into(), element[1].clone(),
acadrust::types::Color::None => "None".into(), element[2].clone(),
acadrust::types::Color::ByBlock => "ByBlock".into(), )
acadrust::types::Color::Index(i) => format!("ACI {i}"),
acadrust::types::Color::Rgb { r, g, b } => format!("#{r:02X}{g:02X}{b:02X}"),
};
let lt = if e.linetype.is_empty() {
"ByLayer"
} else {
&e.linetype
};
row![
text(format!(" {idx}:")).size(10).style(muted_style).width(24),
text(format!("{:+.3}", e.offset)).size(10).width(70),
text(color_str).size(10).width(90),
text(lt).size(10),
]
.spacing(4)
.align_y(iced::Center)
.into()
}) })
.collect(); .collect::<Vec<_>>();
parsed.sort_by(|a, b| b.0.total_cmp(&a.0));
if let (Some(top), Some(bottom)) = (parsed.first(), parsed.last()) {
if self.fill {
let upper = element_points(top.0, bounds);
let lower = element_points(bottom.0, bounds);
let fill = canvas::Path::new(|builder| {
builder.move_to(upper[0]);
builder.line_to(upper[1]);
builder.line_to(upper[2]);
builder.line_to(lower[2]);
builder.line_to(lower[1]);
builder.line_to(lower[0]);
builder.close();
});
frame.fill(&fill, aci_color(&self.fill_color, theme.palette().background.weak.color));
}
}
for (offset, color, linetype) in &parsed {
let points = element_points(*offset, bounds);
let dashed = !linetype.trim().is_empty()
&& !linetype.eq_ignore_ascii_case("ByLayer")
&& !linetype.to_ascii_lowercase().contains("continuous");
let stroke = canvas::Stroke {
style: canvas::Style::Solid(aci_color(color, ink)),
width: 1.4,
line_dash: if dashed {
canvas::LineDash { segments: &[6.0, 4.0], offset: 0 }
} else {
canvas::LineDash::default()
},
..Default::default()
};
frame.stroke(
&canvas::Path::new(|builder| {
builder.move_to(points[0]);
builder.line_to(points[1]);
builder.line_to(points[2]);
}),
stroke,
);
}
if let (Some(top), Some(bottom)) = (parsed.first(), parsed.last()) {
let top_points = element_points(top.0, bounds);
let bottom_points = element_points(bottom.0, bounds);
let start_top = top_points[0];
let start_bottom = bottom_points[0];
let end_top = top_points[2];
let end_bottom = bottom_points[2];
let start_angle = self.start_angle.trim().parse::<f32>().unwrap_or(90.0).to_radians();
let end_angle = self.end_angle.trim().parse::<f32>().unwrap_or(90.0).to_radians();
let cap = |a: Point, b: Point, angle: f32| {
let middle = Point::new((a.x + b.x) * 0.5, (a.y + b.y) * 0.5);
let half = ((a.y - b.y).abs() * 0.5).max(2.0);
let direction = Point::new(angle.cos() * half, angle.sin() * half);
(Point::new(middle.x - direction.x, middle.y - direction.y), Point::new(middle.x + direction.x, middle.y + direction.y))
};
if self.start_square {
let (a, b) = cap(start_top, start_bottom, start_angle);
frame.stroke(&canvas::Path::line(a, b), canvas::Stroke::default().with_color(ink));
}
if self.end_square {
let (a, b) = cap(end_top, end_bottom, end_angle);
frame.stroke(&canvas::Path::line(a, b), canvas::Stroke::default().with_color(ink));
}
let start_center = Point::new((start_top.x + start_bottom.x) * 0.5, (start_top.y + start_bottom.y) * 0.5);
let end_center = Point::new((end_top.x + end_bottom.x) * 0.5, (end_top.y + end_bottom.y) * 0.5);
let outer_radius = ((start_top.y - start_bottom.y).abs() * 0.5).max(3.0);
if self.start_round {
draw_arc(&mut frame, start_center, outer_radius, true, ink);
}
if self.end_round {
draw_arc(&mut frame, end_center, outer_radius, false, ink);
}
if self.start_inner && parsed.len() > 2 {
draw_arc(&mut frame, start_center, outer_radius * 0.55, true, ink);
}
if self.end_inner && parsed.len() > 2 {
draw_arc(&mut frame, end_center, outer_radius * 0.55, false, ink);
}
if self.joints {
let joint_top = top_points[1];
let joint_bottom = bottom_points[1];
frame.stroke(&canvas::Path::line(joint_top, joint_bottom), canvas::Stroke::default().with_color(ink).with_width(1.0));
}
}
frame.fill_text(canvas::Text {
content: format!("{} · {} {}", self.description, t!("Elements"), self.elements.len()),
position: Point::new(10.0, bounds.height - 9.0),
color: ink.scale_alpha(0.62),
size: iced::Pixels(10.0),
align_y: iced::alignment::Vertical::Center,
..Default::default()
});
vec![frame.into_geometry()]
}
}
let mut col_items: Vec<Element<'_, Message>> = vec![ pub fn view_window<'a>(
info_row(t!("Name:"), s.name.clone()), v: MlStyleView<'a>,
info_row(t!("Elements:"), s.elements.len().to_string()), sizing: crate::ui::modal::ModalSizing,
text(t!(" Off Color Ltype")) ) -> Element<'a, Message> {
.size(10) let style = v.style;
.style(muted_style) let content: Element<'a, Message> = match (v.tab, style) {
.into(), (_, None) => text(t!("Select a style to view details.")).size(11).into(),
]; (0, Some(_)) => {
col_items.extend(elem_rows); let mut items = column![
scrollable(column(col_items).spacing(6).padding([12, 12])) row![
.height(sizing.height) text(t!("Offset")).size(10).style(muted_style).width(110),
text(t!("Color")).size(10).style(muted_style).width(100),
text(t!("Line type")).size(10).style(muted_style).width(170),
button(text(t!("Add")).size(10)).on_press(Message::MlStyleElementAdd).padding([4, 12]),
]
.spacing(6)
.align_y(iced::Center),
]
.spacing(6);
for (index, element) in v.elements.iter().enumerate() {
items = items.push(
row![
text_input("0.0", &element[0])
.on_input(move |value| Message::MlStyleElementEdit { index, field: "offset", value })
.size(11).width(110),
text_input("256", &element[1])
.on_input(move |value| Message::MlStyleElementEdit { index, field: "color", value })
.size(11).width(100),
text_input("ByLayer", &element[2])
.on_input(move |value| Message::MlStyleElementEdit { index, field: "linetype", value })
.size(11).width(170),
button(text(t!("Delete")).size(10))
.on_press(Message::MlStyleElementDelete(index))
.padding([4, 10]),
]
.spacing(6)
.align_y(iced::Center),
);
}
items.into()
}
(_, Some(style)) => column![
input_row(t!("Description"), "", v.description, "description"),
text(t!("Fill and joints")).size(11).style(primary_style),
row![
toggle(t!("Fill enabled"), style.flags.fill_on, "fill"),
toggle(t!("Display joints"), style.flags.display_joints, "joints"),
].spacing(18),
input_row(t!("Fill color"), "256", v.fill_color, "fill_color"),
text(t!("Start caps")).size(11).style(primary_style),
row![
toggle(t!("Line"), style.flags.start_square_cap, "start_square"),
toggle(t!("Inner arcs"), style.flags.start_inner_arcs_cap, "start_inner"),
toggle(t!("Outer arc"), style.flags.start_round_cap, "start_round"),
].spacing(18),
input_row(t!("Start angle"), "90", v.start_angle, "start_angle"),
text(t!("End caps")).size(11).style(primary_style),
row![
toggle(t!("Line"), style.flags.end_square_cap, "end_square"),
toggle(t!("Inner arcs"), style.flags.end_inner_arcs_cap, "end_inner"),
toggle(t!("Outer arc"), style.flags.end_round_cap, "end_round"),
].spacing(18),
input_row(t!("End angle"), "90", v.end_angle, "end_angle"),
].spacing(9).into(),
};
let preview: Element<'a, Message> = if let Some(style) = style {
canvas(MLinePreviewCanvas {
description: v.description.to_string(),
start_angle: v.start_angle.to_string(),
end_angle: v.end_angle.to_string(),
fill_color: v.fill_color.to_string(),
fill: style.flags.fill_on,
joints: style.flags.display_joints,
start_square: style.flags.start_square_cap,
start_inner: style.flags.start_inner_arcs_cap,
start_round: style.flags.start_round_cap,
end_square: style.flags.end_square_cap,
end_inner: style.flags.end_inner_arcs_cap,
end_round: style.flags.end_round_cap,
elements: v.elements.to_vec(),
})
.width(Length::Fill)
.height(150)
.into() .into()
} else { } else {
container(text(t!("Select a style to view details.")).size(11).style(muted_style)) container(text(t!("No preview"))).height(150).into()
.padding([12, 12])
.into()
}; };
let status = if v.selected.eq_ignore_ascii_case(&v.current) {
let right_panel = container(details) t!("Current style")
.width(sizing.width) } else if v.in_use {
.height(sizing.height); t!("Used in drawing")
} else {
t!("Available style")
};
let summary = if v.comparison_sections.is_empty() {
t!("No differences").into_owned()
} else {
format!("{}: {}", t!("Different"), v.comparison_sections.join(", "))
};
let editor = crate::ui::style::style_manager::editor_shell(
crate::ui::style::style_manager::EditorShell {
sizing,
selected: v.selected.to_string(),
status,
preview,
comparison: Some(crate::ui::style::style_manager::EditorComparison {
selected: v.compare_name,
options: v.compare_opts,
summary,
on_select: Message::MlStyleDialogCompare,
}),
tabs: vec![
crate::ui::style::style_manager::EditorTab {
label: t!("Elements"),
active: v.tab == 0,
on_press: Message::MlStyleDialogTab(0),
},
crate::ui::style::style_manager::EditorTab {
label: t!("Caps and Fill"),
active: v.tab == 1,
on_press: Message::MlStyleDialogTab(1),
},
],
content,
},
);
crate::ui::style::style_manager::view(crate::ui::style::style_manager::Scaffold { crate::ui::style::style_manager::view(crate::ui::style::style_manager::Scaffold {
sizing, sizing,
kind: crate::app::StyleKind::MLine, kind: crate::app::StyleKind::MLine,
styles: &styles, styles: &v.styles,
selected, selected: v.selected,
current: Some(current_style.as_str()), current: Some(v.current.as_str()),
rename_active, rename_active: v.rename_active,
rename_buf, rename_buf: v.rename_buf,
on_new: Message::MlStyleDialogNew, on_new: Message::MlStyleDialogNew,
on_copy: Message::MlStyleDialogCopy, on_copy: Message::MlStyleDialogCopy,
on_delete: Message::MlStyleDialogDelete, on_delete: Message::MlStyleDialogDelete,
on_select: Message::MlStyleDialogSelect, on_select: Message::MlStyleDialogSelect,
on_set_current: Message::MlStyleDialogSetCurrent, on_set_current: Message::MlStyleDialogSetCurrent,
on_apply: Message::MlStyleApply, on_apply: Message::MlStyleApply,
editor: right_panel.into(), read_only: false,
editor,
}) })
} }

View file

@ -29,6 +29,7 @@ pub fn item<'a>(
on_select: Message, on_select: Message,
rename_active: Option<&str>, rename_active: Option<&str>,
rename_buf: &'a str, rename_buf: &'a str,
can_rename: bool,
) -> Element<'a, Message> { ) -> Element<'a, Message> {
if rename_active == Some(name) { if rename_active == Some(name) {
text_input("", rename_buf) text_input("", rename_buf)
@ -56,9 +57,12 @@ pub fn item<'a>(
..Default::default() ..Default::default()
} }
}); });
mouse_area(cell) let area = mouse_area(cell).on_press(on_select);
.on_press(on_select) if can_rename {
.on_double_click(Message::StyleRenameStart(kind, name.to_string())) area.on_double_click(Message::StyleRenameStart(kind, name.to_string()))
.into() .into()
} else {
area.into()
}
} }
} }

View file

@ -10,7 +10,7 @@
use crate::app::{Message, StyleKind}; use crate::app::{Message, StyleKind};
use iced::widget::button::{Status, Style}; use iced::widget::button::{Status, Style};
use iced::widget::{button, column, container, row, scrollable, text, Space}; use iced::widget::{button, column, container, row, scrollable, text, Space};
use iced::{Background, Border, Element, Theme}; use iced::{Background, Border, Element, Length, Theme};
use crate::t; use crate::t;
use std::borrow::Cow; use std::borrow::Cow;
@ -45,6 +45,9 @@ pub struct Scaffold<'a, 'b> {
pub on_set_current: Message, pub on_set_current: Message,
/// "Apply" action (right side, primary). Every manager has one. /// "Apply" action (right side, primary). Every manager has one.
pub on_apply: Message, pub on_apply: Message,
/// Referenced styles may be copied but not changed, renamed, deleted, or
/// made current.
pub read_only: bool,
pub editor: Element<'a, Message>, pub editor: Element<'a, Message>,
} }
@ -55,10 +58,10 @@ pub fn view<'a, 'b>(s: Scaffold<'a, 'b>) -> Element<'a, Message> {
let bar = row![ let bar = row![
tb_button(t!("New"), s.on_new, false), tb_button(t!("New"), s.on_new, false),
tb_button(t!("Copy"), s.on_copy, false), tb_button(t!("Copy"), s.on_copy, false),
tb_button(t!("Delete"), s.on_delete, false), tb_button_enabled(t!("Delete"), s.on_delete, false, !s.read_only),
Space::new().width(width), Space::new().width(width),
tb_button(t!("Set Current"), s.on_set_current, false), tb_button_enabled(t!("Set Current"), s.on_set_current, false, !s.read_only),
tb_button(t!("Apply"), s.on_apply, true), tb_button_enabled(t!("Apply"), s.on_apply, true, !s.read_only),
] ]
.spacing(4) .spacing(4)
.align_y(iced::Center); .align_y(iced::Center);
@ -87,6 +90,7 @@ pub fn view<'a, 'b>(s: Scaffold<'a, 'b>) -> Element<'a, Message> {
(s.on_select)(name.clone()), (s.on_select)(name.clone()),
s.rename_active, s.rename_active,
s.rename_buf, s.rename_buf,
!(s.read_only && is_sel),
) )
}) })
.collect(); .collect();
@ -137,6 +141,102 @@ pub fn view<'a, 'b>(s: Scaffold<'a, 'b>) -> Element<'a, Message> {
.into() .into()
} }
pub struct EditorTab {
pub label: Cow<'static, str>,
pub active: bool,
pub on_press: Message,
}
pub struct EditorComparison {
pub selected: String,
pub options: Vec<String>,
pub summary: String,
pub on_select: fn(String) -> Message,
}
/// Shared right-hand editor composition used by every named style manager.
/// The preview stays visible while the active property page scrolls.
pub struct EditorShell<'a> {
pub sizing: crate::ui::modal::ModalSizing,
pub selected: String,
pub status: Cow<'static, str>,
pub preview: Element<'a, Message>,
pub comparison: Option<EditorComparison>,
pub tabs: Vec<EditorTab>,
pub content: Element<'a, Message>,
}
pub fn editor_shell<'a>(shell: EditorShell<'a>) -> Element<'a, Message> {
let tabs: Vec<Element<'a, Message>> = shell
.tabs
.into_iter()
.map(|tab| {
button(text(tab.label).size(11))
.on_press(tab.on_press)
.style(tab_button_style(tab.active))
.padding([4, 10])
.into()
})
.collect();
let comparison: Element<'a, Message> = match shell.comparison {
Some(comparison) if !comparison.options.is_empty() => row![
text(t!("Compare with")).size(10).style(muted_text_style),
iced::widget::pick_list(
Some(comparison.selected),
comparison.options,
|value| value.to_string(),
)
.on_select(comparison.on_select)
.text_size(11)
.width(150),
text(comparison.summary).size(10).style(muted_text_style),
]
.spacing(8)
.align_y(iced::Center)
.into(),
_ => Space::new().height(0).into(),
};
let preview = container(shell.preview)
.width(Length::Fill)
.style(|theme: &Theme| container::Style {
background: Some(Background::Color(theme.palette().background.weak.color)),
border: Border {
color: theme.palette().background.neutral.color,
width: 1.0,
radius: 3.0.into(),
},
..Default::default()
});
container(
column![
row![
text(shell.selected).size(13).style(primary_text_style),
Space::new().width(Length::Fill),
text(shell.status).size(10).style(muted_text_style),
]
.align_y(iced::Center),
preview,
comparison,
row(tabs).spacing(2),
hdivider(Length::Fill),
scrollable(container(shell.content).padding([12, 12]).width(Length::Fill))
.width(Length::Fill)
.height(Length::Fill),
]
.spacing(6)
.height(shell.sizing.height),
)
.height(shell.sizing.height)
.width(Length::Fill)
.padding(iced::Padding {
top: 12.0,
right: 0.0,
bottom: 12.0,
left: 0.0,
})
.into()
}
// ── Shared chrome ────────────────────────────────────────────────────────── // ── Shared chrome ──────────────────────────────────────────────────────────
pub(crate) fn tb_button( pub(crate) fn tb_button(
@ -152,10 +252,55 @@ pub(crate) fn tb_button(
.into() .into()
} }
pub(crate) fn tb_button_enabled(
label: impl Into<Cow<'static, str>>,
msg: Message,
accent: bool,
enabled: bool,
) -> Element<'static, Message> {
let pad = if accent { [4, 14] } else { [4, 10] };
let button = button(text(label.into()).size(11))
.style(btn_s(accent))
.padding(pad);
if enabled {
button.on_press(msg).into()
} else {
button.into()
}
}
fn tab_button_style(active: bool) -> impl Fn(&Theme, Status) -> Style {
move |theme: &Theme, status| {
let palette = theme.palette();
let pair = match (active, status) {
(true, _) => palette.primary.strong,
(false, Status::Hovered | Status::Pressed) => palette.background.strong,
_ => palette.background.weak,
};
Style {
background: Some(Background::Color(pair.color)),
text_color: pair.text,
border: Border {
color: palette.background.neutral.color,
width: 1.0,
radius: 3.0.into(),
},
..Default::default()
}
}
}
fn primary_text_style(theme: &Theme) -> iced::widget::text::Style {
iced::widget::text::Style {
color: Some(theme.palette().primary.base.color),
}
}
fn btn_s(accent: bool) -> impl Fn(&Theme, Status) -> Style { fn btn_s(accent: bool) -> impl Fn(&Theme, Status) -> Style {
move |theme: &Theme, st| { move |theme: &Theme, st| {
let palette = theme.palette(); let palette = theme.palette();
let pair = match (accent, st) { let pair = match (accent, st) {
(_, Status::Disabled) => palette.background.weak,
(true, Status::Hovered | Status::Pressed) => palette.primary.strong, (true, Status::Hovered | Status::Pressed) => palette.primary.strong,
(false, Status::Hovered | Status::Pressed) => palette.background.strong, (false, Status::Hovered | Status::Pressed) => palette.background.strong,
(true, _) => palette.primary.base, (true, _) => palette.primary.base,
@ -163,7 +308,11 @@ fn btn_s(accent: bool) -> impl Fn(&Theme, Status) -> Style {
}; };
Style { Style {
background: Some(Background::Color(pair.color)), background: Some(Background::Color(pair.color)),
text_color: pair.text, text_color: if st == Status::Disabled {
pair.text.scale_alpha(0.45)
} else {
pair.text
},
border: Border { border: Border {
color: palette.background.neutral.color, color: palette.background.neutral.color,
width: 1.0, width: 1.0,

View file

@ -1,49 +1,60 @@
//! Table Style Manager window — fills the entire OS window. //! Table style manager.
use crate::app::Message; use crate::app::Message;
use iced::widget::{
button, checkbox, column, container, row, scrollable, text, text_input, Column,
};
use iced::{Background, Border, Element, Theme};
use crate::t; use crate::t;
use iced::widget::{canvas, checkbox, column, row, text, text_input, Column};
use iced::{mouse, Color, Element, Length, Point, Rectangle, Size, Theme};
use std::borrow::Cow; use std::borrow::Cow;
use std::fmt; use std::fmt;
#[derive(Clone, Debug, Eq, PartialEq)] #[derive(Clone, Debug, Eq, PartialEq)]
struct EnumChoice(String); struct EnumChoice {
code: String,
label: Cow<'static, str>,
}
impl fmt::Display for EnumChoice { impl fmt::Display for EnumChoice {
fn fmt(&self, formatter: &mut fmt::Formatter<'_>) -> fmt::Result { fn fmt(&self, formatter: &mut fmt::Formatter<'_>) -> fmt::Result {
formatter.write_str(crate::i18n::translate(&self.0).as_ref()) formatter.write_str(self.label.as_ref())
} }
} }
fn enum_choices(values: &[&str]) -> Vec<EnumChoice> { fn choices(values: &[(&str, &str)]) -> Vec<EnumChoice> {
values.iter().map(|value| EnumChoice((*value).to_string())).collect() values
.iter()
.map(|(code, label)| EnumChoice {
code: (*code).to_string(),
label: crate::i18n::translate(label),
})
.collect()
} }
fn btn_s(accent: bool) -> impl Fn(&Theme, button::Status) -> button::Style { pub struct TableStyleView<'a> {
move |theme: &Theme, st| { pub styles: Vec<String>,
let palette = theme.palette(); pub selected: &'a str,
let pair = match (accent, st) { pub current: &'a str,
(true, button::Status::Hovered | button::Status::Pressed) => palette.primary.strong, pub style: Option<&'a acadrust::objects::TableStyle>,
(false, button::Status::Hovered | button::Status::Pressed) => { pub tab: u8,
palette.background.strong pub compare_name: String,
} pub compare_opts: Vec<String>,
(true, _) => palette.primary.base, pub comparison_sections: Vec<String>,
_ => palette.background.weak, pub in_use: bool,
}; pub hmargin: &'a str,
button::Style { pub vmargin: &'a str,
background: Some(Background::Color(pair.color)), pub description: &'a str,
text_color: pair.text, pub cell_textstyle: &'a [String; 3],
border: Border { pub cell_height: &'a [String; 3],
color: palette.background.neutral.color, pub cell_textcolor: &'a [String; 3],
width: 1.0, pub cell_fillcolor: &'a [String; 3],
radius: 4.0.into(), pub cell_datatype: &'a [String; 3],
}, pub cell_unittype: &'a [String; 3],
..Default::default() pub cell_format: &'a [String; 3],
} pub border_lw: &'a [[String; 6]; 3],
} pub border_color: &'a [[String; 6]; 3],
pub border_spacing: &'a [[String; 6]; 3],
pub rename_active: Option<&'a str>,
pub rename_buf: &'a str,
pub color_open: Option<(u8, &'static str)>,
} }
fn muted_style(theme: &Theme) -> iced::widget::text::Style { fn muted_style(theme: &Theme) -> iced::widget::text::Style {
@ -58,323 +69,492 @@ fn primary_style(theme: &Theme) -> iced::widget::text::Style {
} }
} }
pub fn view_window<'a>( #[derive(Clone)]
styles: Vec<String>, struct PreviewBorder {
selected: &'a str, color: String,
current: &'a str, weight: String,
selected_style: Option<&'a acadrust::objects::TableStyle>, spacing: String,
hmargin_buf: &'a str, double: bool,
vmargin_buf: &'a str, hidden: bool,
description_buf: &'a str, }
cell_textstyle: &'a [String; 3],
cell_height: &'a [String; 3],
cell_textcolor: &'a [String; 3],
cell_fillcolor: &'a [String; 3],
cell_datatype: &'a [String; 3],
cell_unittype: &'a [String; 3],
cell_format: &'a [String; 3],
border_lw: &'a [[String; 6]; 3],
border_color: &'a [[String; 6]; 3],
border_spacing: &'a [[String; 6]; 3],
rename_active: Option<&'a str>,
rename_buf: &'a str,
color_open: Option<(u8, &'static str)>,
sizing: crate::ui::modal::ModalSizing,
) -> Element<'a, Message> {
// ── Right: Details panel ──────────────────────────────────────────────
let info_row = |label: Cow<'static, str>, val: String| -> Element<'_, Message> {
row![
text(label).size(11).style(muted_style).width(160),
text(val).size(11),
]
.spacing(8)
.align_y(iced::Center)
.into()
};
let cell_editor = |row_label: Cow<'static, str>, #[derive(Clone)]
row: u8, struct PreviewRow {
rs: &acadrust::objects::RowCellStyle| label: String,
-> Element<'a, Message> { text_style: String,
let r = row as usize; text_height: String,
let cell_in = |label: Cow<'static, str>, text_color: String,
fill_color: String,
fill: bool,
alignment: String,
data_type: String,
unit_type: String,
format: String,
borders: [PreviewBorder; 6],
}
struct TablePreviewCanvas {
rows: [PreviewRow; 3],
active_row: usize,
hmargin: String,
vmargin: String,
description: String,
flow_up: bool,
title_suppressed: bool,
header_suppressed: bool,
annotative: bool,
}
fn aci_color(value: &str, fallback: Color) -> Color {
crate::ui::color_select::aci_string_to_color(value)
.rgb()
.map(|(r, g, b)| Color::from_rgb8(r, g, b))
.unwrap_or(fallback)
}
fn border_width(value: &str) -> f32 {
value
.trim()
.parse::<f32>()
.map(|value| (value.abs() / 35.0).clamp(0.7, 4.5))
.unwrap_or(1.0)
}
fn draw_line(
frame: &mut canvas::Frame,
a: Point,
b: Point,
border: &PreviewBorder,
fallback: Color,
) {
if border.hidden {
return;
}
let stroke = canvas::Stroke::default()
.with_color(aci_color(&border.color, fallback))
.with_width(border_width(&border.weight));
frame.stroke(&canvas::Path::line(a, b), stroke.clone());
if border.double {
let spacing = border
.spacing
.trim()
.parse::<f32>()
.unwrap_or(1.0)
.abs()
.clamp(1.0, 5.0);
let dx = b.x - a.x;
let dy = b.y - a.y;
let length = (dx * dx + dy * dy).sqrt().max(1.0);
let offset = Point::new(-dy / length * spacing, dx / length * spacing);
frame.stroke(
&canvas::Path::line(
Point::new(a.x + offset.x, a.y + offset.y),
Point::new(b.x + offset.x, b.y + offset.y),
),
stroke,
);
}
}
impl canvas::Program<Message> for TablePreviewCanvas {
type State = ();
fn draw(
&self,
_state: &(),
renderer: &iced::Renderer,
theme: &Theme,
bounds: Rectangle,
_cursor: mouse::Cursor,
) -> Vec<canvas::Geometry> {
let mut frame = canvas::Frame::new(renderer, bounds.size());
let ink = theme.palette().background.base.text;
let mut order = vec![2usize, 1, 0];
if self.title_suppressed {
order.retain(|row| *row != 2);
}
if self.header_suppressed {
order.retain(|row| *row != 1);
}
if self.flow_up {
order.reverse();
}
let hmargin = self.hmargin.trim().parse::<f32>().unwrap_or(1.5).abs();
let vmargin = self.vmargin.trim().parse::<f32>().unwrap_or(1.5).abs();
let left = 18.0 + (hmargin * 1.2).clamp(0.0, 18.0);
let right = bounds.width - left;
let top = 9.0 + (vmargin * 0.8).clamp(0.0, 12.0);
let bottom = bounds.height - 24.0;
let row_height = ((bottom - top) / order.len().max(1) as f32).max(18.0);
let col_width = (right - left) / 3.0;
for (display_row, row_index) in order.iter().copied().enumerate() {
let row = &self.rows[row_index];
let y0 = top + display_row as f32 * row_height;
let y1 = y0 + row_height;
let fallback_fill = theme.palette().background.weak.color;
if row.fill {
frame.fill(
&canvas::Path::rectangle(
Point::new(left, y0),
Size::new(right - left, row_height),
),
aci_color(&row.fill_color, fallback_fill),
);
}
let borders = &row.borders;
draw_line(&mut frame, Point::new(left, y0), Point::new(left, y1), &borders[0], ink);
draw_line(&mut frame, Point::new(right, y0), Point::new(right, y1), &borders[1], ink);
draw_line(&mut frame, Point::new(left, y0), Point::new(right, y0), &borders[2], ink);
draw_line(&mut frame, Point::new(left, y1), Point::new(right, y1), &borders[3], ink);
for column in 1..3 {
let x = left + column as f32 * col_width;
draw_line(&mut frame, Point::new(x, y0), Point::new(x, y1), &borders[5], ink);
}
if display_row + 1 < order.len() {
draw_line(&mut frame, Point::new(left, y1), Point::new(right, y1), &borders[4], ink);
}
for column in 0..3 {
let label = if row.format.trim().is_empty() {
format!("{} {}", row.label, column + 1)
} else {
row.format.replace("{}", &(column + 1).to_string())
};
let horizontal = if row.alignment.ends_with("Left") {
iced::advanced::text::Alignment::Left
} else if row.alignment.ends_with("Right") {
iced::advanced::text::Alignment::Right
} else {
iced::advanced::text::Alignment::Center
};
let x = match horizontal {
iced::advanced::text::Alignment::Left => left + column as f32 * col_width + 5.0,
iced::advanced::text::Alignment::Right => left + (column + 1) as f32 * col_width - 5.0,
iced::advanced::text::Alignment::Center => left + (column as f32 + 0.5) * col_width,
_ => left + (column as f32 + 0.5) * col_width,
};
let y = if row.alignment.starts_with("Top") {
y0 + 7.0
} else if row.alignment.starts_with("Bottom") {
y1 - 7.0
} else {
(y0 + y1) * 0.5
};
frame.fill_text(canvas::Text {
content: label,
position: Point::new(x, y),
color: aci_color(&row.text_color, ink),
size: iced::Pixels(
row.text_height
.trim()
.parse::<f32>()
.map(|height| (height * 34.0).clamp(7.0, 16.0))
.unwrap_or(10.0),
),
align_x: horizontal,
align_y: iced::alignment::Vertical::Center,
..Default::default()
});
}
}
let active = &self.rows[self.active_row.min(2)];
frame.fill_text(canvas::Text {
content: format!(
"{} · {} · type {} / unit {}{}{}",
active.text_style,
if self.annotative { t!("Annotative") } else { t!("Drawing units") },
active.data_type,
active.unit_type,
if self.description.trim().is_empty() { "" } else { " · " },
self.description,
),
position: Point::new(10.0, bounds.height - 9.0),
color: ink.scale_alpha(0.65),
size: iced::Pixels(10.0),
align_y: iced::alignment::Vertical::Center,
..Default::default()
});
vec![frame.into_geometry()]
}
}
fn input_row<'a>(
label: Cow<'static, str>,
placeholder: &'static str, placeholder: &'static str,
value: &'a str, value: &'a str,
field: &'static str| message: impl Fn(String) -> Message + 'a,
-> Element<'a, Message> { ) -> Element<'a, Message> {
row![ row![
text(label).size(11).style(muted_style).width(150), text(label).size(11).style(muted_style).width(155),
text_input(placeholder, value) text_input(placeholder, value)
.on_input(move |v| Message::TableStyleCellEdit { .on_input(message)
row,
field,
value: v
})
.size(11) .size(11)
.width(100), .width(170),
] ]
.spacing(8) .spacing(8)
.align_y(iced::Center) .align_y(iced::Center)
.into() .into()
}
fn cell_editor<'a>(v: &TableStyleView<'a>, row_index: u8) -> Element<'a, Message> {
let Some(style) = v.style else {
return text(t!("Select a style to view details.")).into();
}; };
// Shared colour selector — sends the chosen colour through the same let row_style = match row_index {
// cell edit as an ACI string. 0 => &style.data_row_style,
let cell_color = |label: Cow<'static, str>, value: &'a str, field: &'static str| -> Element<'a, Message> { 1 => &style.header_row_style,
let cur = crate::ui::color_select::aci_string_to_color(value); _ => &style.title_row_style,
let open = color_open == Some((row, field)); };
let selector = crate::ui::color_select::color_selector( let index = row_index as usize;
cur, let cell_input = |label, placeholder, value: &'a str, field| {
open, input_row(label, placeholder, value, move |value| Message::TableStyleCellEdit {
crate::ui::color_select::ColorExtras { row: row_index,
by_layer: true,
by_block: true,
},
move |c| Message::TableStyleCellEdit {
row,
field, field,
value: crate::ui::color_select::color_to_aci_string(c), value,
})
};
let cell_color = |label: Cow<'static, str>, value: &'a str, field: &'static str| {
let current = crate::ui::color_select::aci_string_to_color(value);
let selector = crate::ui::color_select::color_selector(
current,
v.color_open == Some((row_index, field)),
crate::ui::color_select::ColorExtras { by_layer: true, by_block: true },
move |color| Message::TableStyleCellEdit {
row: row_index,
field,
value: crate::ui::color_select::color_to_aci_string(color),
}, },
Message::TableColorMore(row, field), Message::TableColorMore(row_index, field),
Message::OpenColorWindow( Message::OpenColorWindow(crate::app::ColorPickTarget::Table(row_index, field), current),
crate::app::ColorPickTarget::Table(row, field),
cur,
),
); );
row![text(label).size(11).style(muted_style).width(150), selector] row![text(label).size(11).style(muted_style).width(155), selector]
.spacing(8) .spacing(8)
.align_y(iced::Center) .align_y(iced::Center)
.into()
}; };
let mut col = Column::new() let selected_alignment = format!("{:?}", row_style.alignment);
.spacing(3) let mut content = Column::new()
.push(text(row_label).size(11).style(primary_style)) .spacing(7)
.push(cell_in( .push(cell_input(t!("Text style"), "Standard", &v.cell_textstyle[index], "textstyle"))
t!(" Text style:"), .push(cell_input(t!("Text height"), "0.18", &v.cell_height[index], "height"))
"Standard", .push(cell_color(t!("Text color"), &v.cell_textcolor[index], "textcolor"))
&cell_textstyle[r], .push(cell_color(t!("Fill color"), &v.cell_fillcolor[index], "fillcolor"))
"textstyle",
))
.push(cell_in(t!(" Text height:"), "0.18", &cell_height[r], "height"))
.push(cell_color(t!(" Text color:"), &cell_textcolor[r], "textcolor"))
.push(cell_color(t!(" Fill color:"), &cell_fillcolor[r], "fillcolor"))
.push( .push(
row![ row![
text(t!(" Alignment:")).size(11).style(muted_style).width(150), text(t!("Alignment")).size(11).style(muted_style).width(155),
iced::widget::pick_list( iced::widget::pick_list(
Some(EnumChoice(format!("{:?}", rs.alignment))), Some(EnumChoice { code: selected_alignment.clone(), label: crate::i18n::translate(&selected_alignment) }),
enum_choices(&[ choices(&[
"TopLeft", ("TopLeft", "Top left"), ("TopCenter", "Top center"), ("TopRight", "Top right"),
"TopCenter", ("MiddleLeft", "Middle left"), ("MiddleCenter", "Middle center"), ("MiddleRight", "Middle right"),
"TopRight", ("BottomLeft", "Bottom left"), ("BottomCenter", "Bottom center"), ("BottomRight", "Bottom right"),
"MiddleLeft",
"MiddleCenter",
"MiddleRight",
"BottomLeft",
"BottomCenter",
"BottomRight",
]), ]),
|value| value.to_string(), |value| value.to_string(),
) )
.on_select(move |choice| Message::TableStyleCellSetAlign { .on_select(move |choice| Message::TableStyleCellSetAlign { row: row_index, value: choice.code })
row,
value: choice.0,
})
.text_size(11) .text_size(11)
.width(140), .width(170),
] ]
.spacing(8) .spacing(8)
.align_y(iced::Center), .align_y(iced::Center),
) )
.push( .push(
checkbox(rs.fill_enabled) checkbox(row_style.fill_enabled)
.label(t!("Background fill enabled")) .label(t!("Background fill enabled"))
.on_toggle(move |_| Message::TableStyleCellToggleFill(row)) .on_toggle(move |_| Message::TableStyleCellToggleFill(row_index))
.size(14) .size(14)
.text_size(11), .text_size(11),
) )
.push(cell_in(t!(" Data type:"), "0", &cell_datatype[r], "datatype")) .push(cell_input(t!("Data type"), "0", &v.cell_datatype[index], "datatype"))
.push(cell_in(t!(" Unit type:"), "0", &cell_unittype[r], "unittype")) .push(cell_input(t!("Unit type"), "0", &v.cell_unittype[index], "unittype"))
.push(cell_in(t!(" Format string:"), "", &cell_format[r], "format")) .push(cell_input(t!("Format string"), "", &v.cell_format[index], "format"))
.push( .push(text(t!("Borders")).size(11).style(primary_style));
text(t!(" Borders (type / weight / color / spacing / hidden)")) let borders = [
.size(10) (t!("Left"), &row_style.left_border),
.style(muted_style), (t!("Right"), &row_style.right_border),
); (t!("Top"), &row_style.top_border),
(t!("Bottom"), &row_style.bottom_border),
let borders: [(&'static str, &acadrust::objects::TableCellBorder); 6] = [ (t!("Inside horizontal"), &row_style.horizontal_inside_border),
("L", &rs.left_border), (t!("Inside vertical"), &row_style.vertical_inside_border),
("R", &rs.right_border),
("T", &rs.top_border),
("B", &rs.bottom_border),
("H", &rs.horizontal_inside_border),
("V", &rs.vertical_inside_border),
]; ];
for (b, (bname, bd)) in borders.into_iter().enumerate() { for (border_index, (label, border)) in borders.into_iter().enumerate() {
let bu = b as u8; let border_index = border_index as u8;
col = col.push( let border_type = format!("{:?}", border.border_type);
content = content.push(
row![ row![
text(format!(" {bname}")).size(11).style(muted_style).width(28), text(label).size(10).style(muted_style).width(96),
iced::widget::pick_list( iced::widget::pick_list(
Some(EnumChoice(format!("{:?}", bd.border_type))), Some(EnumChoice { code: border_type.clone(), label: crate::i18n::translate(&border_type) }),
enum_choices(&["Single", "Double"]), choices(&[("Single", "Single"), ("Double", "Double")]),
|value| value.to_string(), |value| value.to_string(),
) )
.on_select(move |choice| Message::TableStyleBorderSetType { .on_select(move |choice| Message::TableStyleBorderSetType { cell: row_index, border: border_index, value: choice.code })
cell: row,
border: bu,
value: choice.0,
})
.text_size(10) .text_size(10)
.width(74), .width(82),
text_input("", &border_lw[r][b]) text_input(t!("Weight").as_ref(), &v.border_lw[index][border_index as usize])
.on_input(move |v| Message::TableStyleBorderEdit { .on_input(move |value| Message::TableStyleBorderEdit { cell: row_index, border: border_index, field: "lw", value })
cell: row, .size(10).width(68),
border: bu, text_input(t!("Color").as_ref(), &v.border_color[index][border_index as usize])
field: "lw", .on_input(move |value| Message::TableStyleBorderEdit { cell: row_index, border: border_index, field: "color", value })
value: v .size(10).width(62),
}) text_input(t!("Spacing").as_ref(), &v.border_spacing[index][border_index as usize])
.size(10) .on_input(move |value| Message::TableStyleBorderEdit { cell: row_index, border: border_index, field: "spacing", value })
.width(46), .size(10).width(68),
text_input("", &border_color[r][b]) checkbox(border.is_invisible)
.on_input(move |v| Message::TableStyleBorderEdit { .label(t!("Hidden"))
cell: row, .on_toggle(move |_| Message::TableStyleBorderToggleInvisible { cell: row_index, border: border_index })
border: bu, .size(13).text_size(10),
field: "color",
value: v
})
.size(10)
.width(46),
text_input("", &border_spacing[r][b])
.on_input(move |v| Message::TableStyleBorderEdit {
cell: row,
border: bu,
field: "spacing",
value: v
})
.size(10)
.width(46),
checkbox(bd.is_invisible)
.on_toggle(move |_| Message::TableStyleBorderToggleInvisible {
cell: row,
border: bu
})
.size(13),
] ]
.spacing(5) .spacing(5)
.align_y(iced::Center), .align_y(iced::Center),
); );
} }
content.into()
}
col.push( fn preview_rows(v: &TableStyleView<'_>) -> Option<[PreviewRow; 3]> {
button(text(t!("Apply cell")).size(11)) let style = v.style?;
.on_press(Message::TableStyleCellApply(row)) let source = [&style.data_row_style, &style.header_row_style, &style.title_row_style];
.style(btn_s(true)) Some(std::array::from_fn(|row| {
.padding([4, 12]), let borders = [
) &source[row].left_border,
.into() &source[row].right_border,
}; &source[row].top_border,
&source[row].bottom_border,
&source[row].horizontal_inside_border,
&source[row].vertical_inside_border,
];
PreviewRow {
label: [t!("Data"), t!("Header"), t!("Title")][row].to_string(),
text_style: v.cell_textstyle[row].clone(),
text_height: v.cell_height[row].clone(),
text_color: v.cell_textcolor[row].clone(),
fill_color: v.cell_fillcolor[row].clone(),
fill: source[row].fill_enabled,
alignment: format!("{:?}", source[row].alignment),
data_type: v.cell_datatype[row].clone(),
unit_type: v.cell_unittype[row].clone(),
format: v.cell_format[row].clone(),
borders: std::array::from_fn(|border| PreviewBorder {
color: v.border_color[row][border].clone(),
weight: v.border_lw[row][border].clone(),
spacing: v.border_spacing[row][border].clone(),
double: format!("{:?}", borders[border].border_type) == "Double",
hidden: borders[border].is_invisible,
}),
}
}))
}
let details: Element<'_, Message> = if let Some(s) = selected_style { pub fn view_window<'a>(
scrollable( v: TableStyleView<'a>,
column![ sizing: crate::ui::modal::ModalSizing,
info_row(t!("Name:"), s.name.clone()), ) -> Element<'a, Message> {
let style = v.style;
let content: Element<'a, Message> = match (v.tab, style) {
(_, None) => text(t!("Select a style to view details.")).size(11).into(),
(0, Some(style)) => column![
input_row(t!("Description"), "", v.description, |value| Message::TableStyleEdit { field: "description", value }),
row![ row![
text(t!("Description:")).size(11).style(muted_style).width(160), text(t!("Flow direction")).size(11).style(muted_style).width(155),
text_input("", description_buf)
.on_input(|v| Message::TableStyleEdit {
field: "description",
value: v
})
.size(11)
.width(160),
]
.spacing(8)
.align_y(iced::Center),
row![
text(t!("Flow direction:")).size(11).style(muted_style).width(160),
iced::widget::pick_list( iced::widget::pick_list(
Some(EnumChoice(format!("{:?}", s.flow_direction))), Some(EnumChoice { code: format!("{:?}", style.flow_direction), label: crate::i18n::translate(&format!("{:?}", style.flow_direction)) }),
enum_choices(&["Down", "Up"]), choices(&[("Down", "Top to bottom"), ("Up", "Bottom to top")]),
|value| value.to_string(), |value| value.to_string(),
) )
.on_select(|choice| Message::TableStyleSetFlow(choice.0)) .on_select(|choice| Message::TableStyleSetFlow(choice.code))
.text_size(11) .text_size(11).width(170),
.width(100),
] ]
.spacing(8) .spacing(8).align_y(iced::Center),
.align_y(iced::Center), input_row(t!("Horizontal margin"), "1.5", v.hmargin, |value| Message::TableStyleEdit { field: "hmargin", value }),
row![ input_row(t!("Vertical margin"), "1.5", v.vmargin, |value| Message::TableStyleEdit { field: "vmargin", value }),
text(t!("H Margin:")).size(11).style(muted_style).width(160), checkbox(style.title_suppressed).label(t!("Suppress title row")).on_toggle(|_| Message::TableStyleToggle("title_sup")).size(14).text_size(11),
text_input("1.5", hmargin_buf) checkbox(style.header_suppressed).label(t!("Suppress header row")).on_toggle(|_| Message::TableStyleToggle("header_sup")).size(14).text_size(11),
.on_input(|v| Message::TableStyleEdit { checkbox(style.annotative).label(t!("Annotative")).on_toggle(|_| Message::TableStyleToggleAnnotative).size(14).text_size(11),
field: "hmargin",
value: v
})
.size(11)
.width(100),
] ]
.spacing(8) .spacing(9)
.align_y(iced::Center), .into(),
row![ (tab, Some(_)) => cell_editor(&v, tab.saturating_sub(1).min(2)),
text(t!("V Margin:")).size(11).style(muted_style).width(160),
text_input("1.5", vmargin_buf)
.on_input(|v| Message::TableStyleEdit {
field: "vmargin",
value: v
})
.size(11)
.width(100),
]
.spacing(8)
.align_y(iced::Center),
checkbox(s.title_suppressed)
.label(t!("Title row suppressed"))
.on_toggle(|_| Message::TableStyleToggle("title_sup"))
.size(14)
.text_size(11),
checkbox(s.header_suppressed)
.label(t!("Header row suppressed"))
.on_toggle(|_| Message::TableStyleToggle("header_sup"))
.size(14)
.text_size(11),
button(text(t!("Apply margins")).size(11))
.on_press(Message::TableStyleApply)
.style(btn_s(true))
.padding([4, 12]),
cell_editor(t!("Data Row:"), 0, &s.data_row_style),
cell_editor(t!("Header Row:"), 1, &s.header_row_style),
cell_editor(t!("Title Row:"), 2, &s.title_row_style),
]
.spacing(6)
.padding([12, 12]),
)
.width(sizing.width)
.height(sizing.height)
.into()
} else {
container(text(t!("Select a style to view details.")).size(11).style(muted_style))
.padding([12, 12])
.into()
}; };
let rows = preview_rows(&v).unwrap_or_else(|| {
let right_panel = container(details) std::array::from_fn(|row| PreviewRow {
.width(sizing.width) label: ["Data", "Header", "Title"][row].to_string(),
.height(sizing.height); text_style: String::new(),
text_height: String::new(),
text_color: String::new(),
fill_color: String::new(),
fill: false,
alignment: "MiddleCenter".to_string(),
data_type: String::new(),
unit_type: String::new(),
format: String::new(),
borders: std::array::from_fn(|_| PreviewBorder {
color: String::new(), weight: String::new(), spacing: String::new(), double: false, hidden: false,
}),
})
});
let preview = canvas(TablePreviewCanvas {
rows,
active_row: v.tab.saturating_sub(1).min(2) as usize,
hmargin: v.hmargin.to_string(),
vmargin: v.vmargin.to_string(),
description: v.description.to_string(),
flow_up: style.is_some_and(|style| format!("{:?}", style.flow_direction) == "Up"),
title_suppressed: style.is_some_and(|style| style.title_suppressed),
header_suppressed: style.is_some_and(|style| style.header_suppressed),
annotative: style.is_some_and(|style| style.annotative),
})
.width(Length::Fill)
.height(150);
let status = if v.selected.eq_ignore_ascii_case(v.current) {
t!("Current style")
} else if v.in_use {
t!("Used in drawing")
} else {
t!("Available style")
};
let summary = if v.comparison_sections.is_empty() {
t!("No differences").into_owned()
} else {
format!("{}: {}", t!("Different"), v.comparison_sections.join(", "))
};
let editor = crate::ui::style::style_manager::editor_shell(
crate::ui::style::style_manager::EditorShell {
sizing,
selected: v.selected.to_string(),
status,
preview: preview.into(),
comparison: Some(crate::ui::style::style_manager::EditorComparison {
selected: v.compare_name,
options: v.compare_opts,
summary,
on_select: Message::TableStyleDialogCompare,
}),
tabs: [t!("General"), t!("Data Row"), t!("Header Row"), t!("Title Row")]
.into_iter()
.enumerate()
.map(|(tab, label)| crate::ui::style::style_manager::EditorTab {
label,
active: v.tab == tab as u8,
on_press: Message::TableStyleDialogTab(tab as u8),
})
.collect(),
content,
},
);
crate::ui::style::style_manager::view(crate::ui::style::style_manager::Scaffold { crate::ui::style::style_manager::view(crate::ui::style::style_manager::Scaffold {
sizing, sizing,
kind: crate::app::StyleKind::Table, kind: crate::app::StyleKind::Table,
styles: &styles, styles: &v.styles,
selected, selected: v.selected,
current: Some(current), current: Some(v.current),
rename_active, rename_active: v.rename_active,
rename_buf, rename_buf: v.rename_buf,
on_new: Message::TableStyleDialogNew, on_new: Message::TableStyleDialogNew,
on_copy: Message::TableStyleDialogCopy, on_copy: Message::TableStyleDialogCopy,
on_delete: Message::TableStyleDialogDelete, on_delete: Message::TableStyleDialogDelete,
on_select: Message::TableStyleDialogSelect, on_select: Message::TableStyleDialogSelect,
on_set_current: Message::TableStyleDialogSetCurrent, on_set_current: Message::TableStyleDialogSetCurrent,
on_apply: Message::TableStyleApply, on_apply: Message::TableStyleApply,
editor: right_panel.into(), read_only: false,
editor,
}) })
} }

View file

@ -1,20 +1,21 @@
//! Text Style Font Browser window — fills the entire OS window. //! Text style manager.
use crate::app::Message; use crate::app::{Message, StyleKind};
use crate::app::StyleKind;
use iced::widget::{
button, canvas, checkbox, column, container, row, scrollable, text, text_input, Space,
};
use iced::{mouse, Background, Border, Element, Length, Point, Rectangle, Theme};
use crate::t; use crate::t;
use iced::widget::{button, canvas, checkbox, column, container, row, scrollable, text, text_input};
use iced::{mouse, Background, Border, Element, Length, Point, Rectangle, Theme};
use std::borrow::Cow; use std::borrow::Cow;
/// View-model for the Text Style editor window.
pub struct TextStyleView<'a> { pub struct TextStyleView<'a> {
pub styles: Vec<String>, pub styles: Vec<String>,
pub selected: &'a str, pub selected: &'a str,
/// Name of the current text style (marked with ✓ in the list).
pub current: &'a str, pub current: &'a str,
pub tab: u8,
pub compare_name: String,
pub compare_opts: Vec<String>,
pub comparison_sections: Vec<String>,
pub read_only: bool,
pub in_use: bool,
pub font_buf: &'a str, pub font_buf: &'a str,
pub width_buf: &'a str, pub width_buf: &'a str,
pub oblique_buf: &'a str, pub oblique_buf: &'a str,
@ -23,57 +24,18 @@ pub struct TextStyleView<'a> {
pub ttf_buf: &'a str, pub ttf_buf: &'a str,
pub backward: bool, pub backward: bool,
pub upside_down: bool, pub upside_down: bool,
pub vertical: bool,
pub annotative: bool, pub annotative: bool,
/// Name of the style being renamed inline (double-clicked), if any.
pub rename_active: Option<&'a str>, pub rename_active: Option<&'a str>,
/// Edit buffer for the inline rename text input.
pub rename_buf: &'a str, pub rename_buf: &'a str,
} }
const BUILTIN_FONTS: &[&str] = &[ const BUILTIN_FONTS: &[&str] = &[
"Standard", "ISO", "Simplex", "RomanS", "RomanD", "RomanC", "RomanT", "ItalicC", "ItalicT", "Standard", "ISO", "Simplex", "RomanS", "RomanD", "RomanC", "RomanT", "ItalicC",
"ScriptS", "ScriptC", "GothGBT", "GothGRT", "GothITT", "GreekC", "Symbol", "ItalicT", "ScriptS", "ScriptC", "GothGBT", "GothGRT", "GothITT", "GreekC",
"ISO3098", "Unicode", "Symbol", "ISO3098", "Unicode",
]; ];
fn list_item(active: bool) -> impl Fn(&Theme, button::Status) -> button::Style {
move |theme: &Theme, st| {
let palette = theme.palette();
let pair = match (active, st) {
(true, _) => Some(palette.primary.strong),
(false, button::Status::Hovered | button::Status::Pressed) => {
Some(palette.background.strong)
}
_ => None,
};
button::Style {
background: pair.map(|p| Background::Color(p.color)),
text_color: pair.map(|p| p.text).unwrap_or(palette.background.base.text),
..Default::default()
}
}
}
fn field_style(theme: &Theme, status: text_input::Status) -> text_input::Style {
let palette = theme.palette();
let border = match status {
text_input::Status::Focused { .. } => palette.primary.base.color,
_ => palette.background.neutral.color,
};
text_input::Style {
background: Background::Color(palette.background.base.color),
border: Border {
color: border,
width: 1.0,
radius: 3.0.into(),
},
icon: palette.background.base.text,
placeholder: palette.background.base.text.scale_alpha(0.48),
value: palette.background.base.text,
selection: palette.primary.base.color.scale_alpha(0.5),
}
}
fn muted_style(theme: &Theme) -> iced::widget::text::Style { fn muted_style(theme: &Theme) -> iced::widget::text::Style {
iced::widget::text::Style { iced::widget::text::Style {
color: Some(theme.palette().background.base.text.scale_alpha(0.68)), color: Some(theme.palette().background.base.text.scale_alpha(0.68)),
@ -86,33 +48,55 @@ fn primary_style(theme: &Theme) -> iced::widget::text::Style {
} }
} }
fn vsep<'a>(height: Length) -> Element<'a, Message> { fn field_style(theme: &Theme, status: text_input::Status) -> text_input::Style {
container(Space::new().width(1).height(height)) let palette = theme.palette();
.width(1) text_input::Style {
.height(height) background: Background::Color(palette.background.base.color),
.style(|theme: &Theme| container::Style { border: Border {
background: Some(Background::Color( color: if matches!(status, text_input::Status::Focused { .. }) {
theme.palette().background.neutral.color palette.primary.base.color
)), } else {
..Default::default() palette.background.neutral.color
}) },
.into() width: 1.0,
radius: 3.0.into(),
},
icon: palette.background.base.text,
placeholder: palette.background.base.text.scale_alpha(0.48),
value: palette.background.base.text,
selection: palette.primary.base.color.scale_alpha(0.5),
}
}
fn list_item(active: bool) -> impl Fn(&Theme, button::Status) -> button::Style {
move |theme: &Theme, status| {
let palette = theme.palette();
let pair = match (active, status) {
(true, _) => Some(palette.primary.strong),
(false, button::Status::Hovered | button::Status::Pressed) => {
Some(palette.background.strong)
}
_ => None,
};
button::Style {
background: pair.map(|value| Background::Color(value.color)),
text_color: pair
.map(|value| value.text)
.unwrap_or(palette.background.base.text),
..Default::default()
}
}
} }
/// Live sample-text preview. Tessellates a fixed sample string through the same
/// stroke path the text entities use (`lff::tessellate_text_ex`), so the chosen
/// font, width factor, oblique angle and backward/upside-down flags all show up,
/// then scales the result to fit the preview box (Y flipped: glyph space is
/// Y-up, screen is Y-down). Height is not bound — it only scales uniformly and
/// the fit cancels it.
struct TextPreviewCanvas { struct TextPreviewCanvas {
font: String, font: String,
/// Negative mirrors left-right (backward). big_font: String,
width_factor: f32, width_factor: f32,
/// Radians.
oblique: f32, oblique: f32,
/// 0 or π (upside-down rotates 180° about the origin). height: f32,
rotation: f32, upside_down: bool,
vertical: bool,
annotative: bool,
} }
impl canvas::Program<Message> for TextPreviewCanvas { impl canvas::Program<Message> for TextPreviewCanvas {
@ -127,314 +111,311 @@ impl canvas::Program<Message> for TextPreviewCanvas {
_cursor: mouse::Cursor, _cursor: mouse::Cursor,
) -> Vec<canvas::Geometry> { ) -> Vec<canvas::Geometry> {
let mut frame = canvas::Frame::new(renderer, bounds.size()); let mut frame = canvas::Frame::new(renderer, bounds.size());
let (strokes, _) = crate::scene::text::lff::tessellate_text_ex( let ink = theme.palette().background.base.text;
let glyph_height = if self.height <= 0.0 {
18.0
} else {
(self.height * 7.0).clamp(6.0, 52.0)
};
let rotation = if self.upside_down {
std::f32::consts::PI
} else {
0.0
};
let strokes = if self.vertical {
let mut result = Vec::new();
for (index, character) in "Ab3".chars().enumerate() {
let (mut glyph, _) = crate::scene::text::lff::tessellate_text_ex(
[0.0, -(index as f32) * glyph_height * 1.25],
glyph_height,
rotation,
self.width_factor,
self.oblique,
&self.font,
&character.to_string(),
);
result.append(&mut glyph);
}
result
} else {
crate::scene::text::lff::tessellate_text_ex(
[0.0, 0.0], [0.0, 0.0],
9.0, glyph_height,
self.rotation, rotation,
self.width_factor, self.width_factor,
self.oblique, self.oblique,
&self.font, &self.font,
"AaBbCc 0123", "AaBbCc 0123",
); )
.0
};
let mut min = [f32::MAX; 2]; let mut min = [f32::MAX; 2];
let mut max = [f32::MIN; 2]; let mut max = [f32::MIN; 2];
for s in &strokes { for stroke in &strokes {
for &[x, y] in s { for &[x, y] in stroke {
min[0] = min[0].min(x); min[0] = min[0].min(x);
min[1] = min[1].min(y); min[1] = min[1].min(y);
max[0] = max[0].max(x); max[0] = max[0].max(x);
max[1] = max[1].max(y); max[1] = max[1].max(y);
} }
} }
if min[0] > max[0] { if min[0] <= max[0] {
return vec![frame.into_geometry()]; let span = [(max[0] - min[0]).max(1.0), (max[1] - min[1]).max(1.0)];
} let available = [bounds.width - 24.0, bounds.height - 34.0];
let pad = 10.0; let scale = (available[0] / span[0])
let span = [(max[0] - min[0]).max(1e-3), (max[1] - min[1]).max(1e-3)]; .min(available[1] / span[1])
let scale = ((bounds.width - 2.0 * pad) / span[0]) .min(1.0)
.min((bounds.height - 2.0 * pad) / span[1])
.max(0.0); .max(0.0);
let mid = [(min[0] + max[0]) * 0.5, (min[1] + max[1]) * 0.5]; let middle = [(min[0] + max[0]) * 0.5, (min[1] + max[1]) * 0.5];
let center = [bounds.width * 0.5, bounds.height * 0.5]; let center = [bounds.width * 0.5, (bounds.height - 18.0) * 0.5];
let map = |x: f32, y: f32| { let map = |x: f32, y: f32| {
Point::new( Point::new(
center[0] + (x - mid[0]) * scale, center[0] + (x - middle[0]) * scale,
center[1] - (y - mid[1]) * scale, center[1] - (y - middle[1]) * scale,
) )
}; };
let stroke = canvas::Stroke { for stroke in &strokes {
width: 1.4, if stroke.len() < 2 {
style: canvas::Style::Solid(theme.palette().background.base.text),
..Default::default()
};
for s in &strokes {
if s.len() < 2 {
continue; continue;
} }
let path = canvas::Path::new(|p| { let path = canvas::Path::new(|builder| {
p.move_to(map(s[0][0], s[0][1])); builder.move_to(map(stroke[0][0], stroke[0][1]));
for &[x, y] in &s[1..] { for &[x, y] in &stroke[1..] {
p.line_to(map(x, y)); builder.line_to(map(x, y));
} }
}); });
frame.stroke(&path, stroke.clone()); frame.stroke(
&path,
canvas::Stroke::default().with_color(ink).with_width(1.35),
);
} }
}
let font_label = if self.big_font.trim().is_empty() {
self.font.clone()
} else {
format!("{} + {}", self.font, self.big_font)
};
let mode = if self.annotative {
t!("Paper height")
} else {
t!("Drawing height")
};
frame.fill_text(canvas::Text {
content: format!("{} · {} {:.3}", font_label, mode, self.height),
position: Point::new(10.0, bounds.height - 9.0),
color: ink.scale_alpha(0.65),
size: iced::Pixels(10.0),
align_y: iced::alignment::Vertical::Center,
..Default::default()
});
vec![frame.into_geometry()] vec![frame.into_geometry()]
} }
} }
fn input_row<'a>(
label: Cow<'static, str>,
placeholder: Cow<'static, str>,
value: &'a str,
field: &'static str,
read_only: bool,
) -> Element<'a, Message> {
let control: Element<'a, Message> = if read_only {
container(text(value).size(11)).padding([5, 7]).width(180).into()
} else {
text_input(placeholder.as_ref(), value)
.on_input(move |value| Message::TextStyleEdit { field, value })
.style(field_style)
.size(11)
.width(180)
.into()
};
row![text(label).size(11).style(muted_style).width(150), control]
.spacing(8)
.align_y(iced::Center)
.into()
}
fn toggle<'a>(
label: Cow<'static, str>,
value: bool,
field: &'static str,
read_only: bool,
) -> Element<'a, Message> {
let checkbox = checkbox(value).label(label).size(14).text_size(11);
if read_only {
checkbox.into()
} else {
checkbox
.on_toggle(move |_| Message::TextStyleToggle(field))
.into()
}
}
fn font_list<'a>(
title: Cow<'static, str>,
values: Vec<String>,
selected: &'a str,
system: bool,
read_only: bool,
) -> Element<'a, Message> {
let items: Vec<Element<'a, Message>> = values
.into_iter()
.map(|font| {
let active = selected.eq_ignore_ascii_case(&font);
let button = button(text(font.clone()).size(10))
.style(list_item(active))
.padding([3, 8])
.width(Length::Fill);
if read_only {
button.into()
} else if system {
button
.on_press(Message::TextStyleEdit {
field: "ttf",
value: font,
})
.into()
} else {
button.on_press(Message::TextStyleFontPick(font)).into()
}
})
.collect();
container(column![text(title).size(10).style(muted_style), scrollable(column(items).spacing(1)).height(250)].spacing(5))
.width(Length::FillPortion(1))
.padding(4)
.into()
}
pub fn view_window<'a>( pub fn view_window<'a>(
v: TextStyleView<'a>, v: TextStyleView<'a>,
sizing: crate::ui::modal::ModalSizing, sizing: crate::ui::modal::ModalSizing,
) -> Element<'a, Message> { ) -> Element<'a, Message> {
let TextStyleView { let effective_font = if v.ttf_buf.trim().is_empty() {
styles, v.font_buf
selected,
current,
font_buf,
width_buf,
oblique_buf,
height_buf,
bigfont_buf,
ttf_buf,
backward,
upside_down,
annotative,
rename_active,
rename_buf,
} = v;
// ── Middle: Font browser ──────────────────────────────────────────────
let font_items: Vec<Element<'_, Message>> = BUILTIN_FONTS
.iter()
.map(|&f| {
let is_sel = font_buf == f;
button(text(f).size(10))
.on_press(Message::TextStyleFontPick(f.to_string()))
.style(list_item(is_sel))
.padding([3, 8])
.width(sizing.width)
.into()
})
.collect();
let font_panel = container(
column![
text(t!("Font File")).size(10).style(muted_style),
container(scrollable(column(font_items).spacing(1)).height(sizing.height))
.style(|theme: &Theme| {
let palette = theme.palette();
container::Style {
background: Some(Background::Color(palette.background.weak.color)),
border: Border {
color: palette.background.neutral.color,
width: 1.0,
radius: 3.0.into()
},
..Default::default()
}
})
.width(sizing.width)
.height(sizing.height)
.padding(2),
text_input(t!("font file…").as_ref(), font_buf)
.on_input(|v| Message::TextStyleEdit {
field: "font",
value: v
})
.style(field_style)
.size(11)
.width(sizing.width),
]
.spacing(6)
.height(sizing.height),
)
.width(190)
.height(sizing.height)
.padding([12, 8]);
// Labeled numeric/text field row → TextStyleEdit { field, value }.
fn frow<'a>(
label: impl Into<Cow<'static, str>>,
ph: impl Into<Cow<'static, str>>,
buf: &'a str,
field: &'static str,
) -> Element<'a, Message> {
row![
text(label.into()).size(11).style(muted_style).width(120),
text_input(ph.into().as_ref(), buf)
.on_input(move |v| Message::TextStyleEdit { field, value: v })
.style(field_style)
.size(11)
.width(140),
]
.spacing(6)
.align_y(iced::Center)
.into()
}
// Live preview reflecting the in-progress edits. Effective font follows the
// entity rule: TrueType name wins when set, else the stroke font file.
let eff_font = if !ttf_buf.trim().is_empty() {
ttf_buf
} else { } else {
font_buf v.ttf_buf
}; };
let prev_base_wf = width_buf let width = v
.width_buf
.trim() .trim()
.parse::<f32>() .parse::<f32>()
.unwrap_or(1.0) .unwrap_or(1.0)
.abs() .abs()
.clamp(0.01, 100.0); .clamp(0.01, 100.0);
let preview = canvas(TextPreviewCanvas { let preview = canvas(TextPreviewCanvas {
font: eff_font.to_string(), font: effective_font.to_string(),
width_factor: if backward { -prev_base_wf } else { prev_base_wf }, big_font: v.bigfont_buf.to_string(),
oblique: oblique_buf.trim().parse::<f32>().unwrap_or(0.0).to_radians(), width_factor: if v.backward { -width } else { width },
rotation: if upside_down { std::f32::consts::PI } else { 0.0 }, oblique: v.oblique_buf.trim().parse::<f32>().unwrap_or(0.0).to_radians(),
height: v.height_buf.trim().parse::<f32>().unwrap_or(0.0).max(0.0),
upside_down: v.upside_down,
vertical: v.vertical,
annotative: v.annotative,
}) })
.width(sizing.width) .width(Length::Fill)
.height(Length::Fixed(56.0)); .height(112);
// ── Right: Properties ───────────────────────────────────────────────── let content: Element<'a, Message> = match v.tab {
let props_panel = container( 0 => {
let built_in = BUILTIN_FONTS.iter().map(|value| (*value).to_string()).collect();
let system = crate::scene::text::sysfont::families().to_vec();
column![ column![
text(t!("Properties")).size(11).style(primary_style),
frow(t!("Big Font:"), t!("big-font file…"), bigfont_buf, "bigfont"),
frow(t!("TrueType Font:"), t!("e.g. Arial"), ttf_buf, "ttf"),
frow(
if annotative {
t!("Paper Text Height:")
} else {
t!("Fixed Height:")
},
t!("0 = variable"),
height_buf,
"height",
),
frow(t!("Width Factor:"), "1.0", width_buf, "width"),
frow(t!("Oblique (°):"), "0.0", oblique_buf, "oblique"),
row![ row![
checkbox(backward) font_list(t!("Stroke fonts"), built_in, v.font_buf, false, v.read_only),
.label(t!("Backward")) font_list(t!("System fonts"), system, v.ttf_buf, true, v.read_only),
.on_toggle(|_| Message::TextStyleToggle("backward"))
.size(15)
.text_size(11),
checkbox(upside_down)
.label(t!("Upside down"))
.on_toggle(|_| Message::TextStyleToggle("upside_down"))
.size(15)
.text_size(11),
] ]
.spacing(16), .spacing(10),
checkbox(annotative) input_row(t!("Font file"), t!("font file…"), v.font_buf, "font", v.read_only),
.label(t!("Annotative")) input_row(t!("Big font"), t!("big-font file…"), v.bigfont_buf, "bigfont", v.read_only),
.on_toggle(|_| Message::TextStyleToggle("annotative")) input_row(t!("System font"), t!("font family…"), v.ttf_buf, "ttf", v.read_only),
.size(15)
.text_size(11),
Space::new().height(8),
text(t!("Preview")).size(10).style(muted_style),
container(preview)
.style(|theme: &Theme| {
let palette = theme.palette();
container::Style {
background: Some(Background::Color(palette.background.base.color)),
border: Border {
color: palette.background.neutral.color,
width: 1.0,
radius: 4.0.into()
},
..Default::default()
}
})
.padding(8)
.width(sizing.width),
] ]
.spacing(10) .spacing(8)
.height(sizing.height),
)
.width(sizing.width)
.height(sizing.height)
.padding(iced::Padding {
top: 12.0,
right: 12.0,
bottom: 12.0,
left: 8.0,
});
// ── System TrueType fonts (read from the user's machine via fontdb) ──
let ttf_items: Vec<Element<'_, Message>> = crate::scene::text::sysfont::families()
.iter()
.map(|fam| {
let is_sel = ttf_buf.eq_ignore_ascii_case(fam);
button(text(fam).size(10))
.on_press(Message::TextStyleEdit {
field: "ttf",
value: fam.clone(),
})
.style(list_item(is_sel))
.padding([3, 8])
.width(sizing.width)
.into() .into()
})
.collect();
let ttf_panel = container(
column![
text(t!("TrueType (system)")).size(10).style(muted_style),
container(scrollable(column(ttf_items).spacing(1)).height(sizing.height))
.style(|theme: &Theme| {
let palette = theme.palette();
container::Style {
background: Some(Background::Color(palette.background.weak.color)),
border: Border {
color: palette.background.neutral.color,
width: 1.0,
radius: 3.0.into()
},
..Default::default()
} }
}) _ => column![
.width(sizing.width) text(t!("Size")).size(11).style(primary_style),
.height(sizing.height) input_row(
.padding(2), if v.annotative { t!("Paper text height") } else { t!("Fixed height") },
text_input(t!("TrueType font…").as_ref(), ttf_buf) t!("0 = variable"),
.on_input(|v| Message::TextStyleEdit { v.height_buf,
field: "ttf", "height",
value: v v.read_only,
}) ),
.style(field_style) text(t!("Effects")).size(11).style(primary_style),
.size(11) input_row(t!("Width factor"), "1.0".into(), v.width_buf, "width", v.read_only),
.width(sizing.width), input_row(t!("Oblique angle"), "0.0".into(), v.oblique_buf, "oblique", v.read_only),
row![
toggle(t!("Backward"), v.backward, "backward", v.read_only),
toggle(t!("Upside down"), v.upside_down, "upside_down", v.read_only),
toggle(
t!("Vertical"),
v.vertical,
"vertical",
v.read_only || !v.ttf_buf.trim().is_empty(),
),
] ]
.spacing(6) .spacing(18),
.height(sizing.height), toggle(t!("Annotative"), v.annotative, "annotative", v.read_only),
)
.width(190)
.height(sizing.height)
.padding([12, 8]);
let editor = row![
font_panel,
vsep(sizing.height),
ttf_panel,
vsep(sizing.height),
props_panel
] ]
.height(sizing.height); .spacing(9)
.into(),
};
let status = if v.read_only {
t!("Referenced style · Read only")
} else if v.selected.eq_ignore_ascii_case(v.current) {
t!("Current style")
} else if v.in_use {
t!("Used in drawing")
} else {
t!("Available style")
};
let summary = if v.comparison_sections.is_empty() {
t!("No differences").into_owned()
} else {
format!("{}: {}", t!("Different"), v.comparison_sections.join(", "))
};
let editor = crate::ui::style::style_manager::editor_shell(
crate::ui::style::style_manager::EditorShell {
sizing,
selected: v.selected.to_string(),
status,
preview: preview.into(),
comparison: Some(crate::ui::style::style_manager::EditorComparison {
selected: v.compare_name,
options: v.compare_opts,
summary,
on_select: Message::TextStyleDialogCompare,
}),
tabs: vec![
crate::ui::style::style_manager::EditorTab {
label: t!("Fonts"),
active: v.tab == 0,
on_press: Message::TextStyleDialogTab(0),
},
crate::ui::style::style_manager::EditorTab {
label: t!("Size and Effects"),
active: v.tab == 1,
on_press: Message::TextStyleDialogTab(1),
},
],
content,
},
);
crate::ui::style::style_manager::view(crate::ui::style::style_manager::Scaffold { crate::ui::style::style_manager::view(crate::ui::style::style_manager::Scaffold {
sizing, sizing,
kind: StyleKind::Text, kind: StyleKind::Text,
styles: &styles, styles: &v.styles,
selected, selected: v.selected,
current: Some(current), current: Some(v.current),
rename_active, rename_active: v.rename_active,
rename_buf, rename_buf: v.rename_buf,
on_new: Message::TextStyleDialogNew, on_new: Message::TextStyleDialogNew,
on_copy: Message::TextStyleDialogCopy, on_copy: Message::TextStyleDialogCopy,
on_delete: Message::TextStyleDialogDelete, on_delete: Message::TextStyleDialogDelete,
on_select: Message::TextStyleDialogSelect, on_select: Message::TextStyleDialogSelect,
on_set_current: Message::TextStyleDialogSetCurrent, on_set_current: Message::TextStyleDialogSetCurrent,
on_apply: Message::TextStyleApply, on_apply: Message::TextStyleApply,
editor: editor.into(), read_only: v.read_only,
editor,
}) })
} }