diff --git a/assets/icons/layers/layvpfrz.svg b/assets/icons/layers/layvpfrz.svg new file mode 100644 index 000000000..67da9cae9 --- /dev/null +++ b/assets/icons/layers/layvpfrz.svg @@ -0,0 +1,9 @@ + + + + + + + + + diff --git a/assets/icons/layers/layvpthw.svg b/assets/icons/layers/layvpthw.svg new file mode 100644 index 000000000..525158289 --- /dev/null +++ b/assets/icons/layers/layvpthw.svg @@ -0,0 +1,10 @@ + + + + + + + + + + diff --git a/assets/icons/model/shell.svg b/assets/icons/model/shell.svg new file mode 100644 index 000000000..f3c9079b8 --- /dev/null +++ b/assets/icons/model/shell.svg @@ -0,0 +1,10 @@ + + + + + + + + + + diff --git a/assets/icons/osnap/geometric_center.svg b/assets/icons/osnap/geometric_center.svg new file mode 100644 index 000000000..8f82c5570 --- /dev/null +++ b/assets/icons/osnap/geometric_center.svg @@ -0,0 +1 @@ + diff --git a/locales/ar-SA/opencadstudio.ftl b/locales/ar-SA/opencadstudio.ftl index 260bfa07f..d7ddef4f1 100644 --- a/locales/ar-SA/opencadstudio.ftl +++ b/locales/ar-SA/opencadstudio.ftl @@ -1531,10 +1531,10 @@ common = .are-you-sure-you-want-to-reset-you-will-lose-all-your-current-shortcuts = هل تريد بالتأكيد إعادة الضبط؟ ستفقد جميع اختصاراتك الحالية! .yes-reset = { common.yes }, { common.reset } .reset-to-default = { common.reset } · { common.default-title-case } - .apply-exit = { common.apply } && { draw.sketch-exit } + .apply-exit = { common.apply } & { draw.sketch-exit } .shortcut-already-used-for-command = الاختصار مستخدم بالفعل للأمر: __ocs_fmt_0__ → __ocs_fmt_1__ .unsaved-changes-will-be-discarded = ستُتجاهل التغييرات غير المحفوظة. - .discard-close = { common.discard } && { action.close } + .discard-close = { common.discard } & { action.close } .keep-editing = { common.continue } · { layers.edit } .mcp-control-is-off = التحكم عبر MCP متوقف .mcp-is-handling-a-request = يعالج MCP طلبًا @@ -2333,6 +2333,21 @@ common = .wblock-err-no-path = يرجى تحديد اسم الملف والمسار. .wblock-err-current-drawing = لا يمكن الكتابة إلى ملف الرسم الحالي. .wblock-help-info = يقوم WBLOCK بكتابة كائنات أو كتلة أو الرسم بالكامل إلى ملف رسم جديد. + .blank-drawing = رسم فارغ + .default-template-new-drawings = القالب الافتراضي للرسومات الجديدة + .fillet-trim-mode-trim = FILLET Enter Trim mode option [Trim/No trim] : + .fillet-trim-mode-no-trim = FILLET Enter Trim mode option [Trim/No trim] : + .linetype-files = ملفات أنواع الخطوط + .load-linetypes = تحميل أنواع الخطوط + .requires-two-positive-values = يتطلب قيمتين موجبتين (x,y) أو قيمة واحدة لكليهما. + .snap-marker-color = لون علامة الالتقاط + .linetypes-loaded = تم تحميل __ocs_fmt_0__ من أنواع الخطوط. + .geometric-center-snap = المركز الهندسي + .no-trim-option = بدون تشذيب + .chspace-model-tab = CHSPACE: الأمر غير مسموح به في علامة تبويب النموذج. + .chspace-no-viewport = CHSPACE: لا توجد نافذة عرض مسقطية لتغيير الفضاء من خلالها. + .chspace-to-paper = CHSPACE: تم نقل __ocs_fmt_0__ كائن إلى فضاء الورق. + .chspace-to-model = CHSPACE: تم نقل __ocs_fmt_0__ كائن إلى فضاء النموذج. errors = .save-warning = تحذير الحفظ .unable-to-save-drawing = تعذر حفظ الرسم @@ -5502,7 +5517,7 @@ styles = .isavebak-set-to-1 = عُيّن ISAVEBAK إلى 1 .keep-with-dim-line = إبقاء مع خط البُعد .layers = الطبقات - .linetype-list-set = LINETYPE [قائمة / تعيين]: + .linetype-list-set = LINETYPE [قائمة / تعيين / تحميل]: .linetype-set-linetype-name-bylayer-byblock = LINETYPE SET اسم نوع الخط (حسب الطبقة / حسب الكتلة / …): .linetype-name-is-not-loaded-use-linetype-list = LINETYPE: لم يُحمّل "__ocs_fmt_0__". استخدم LINETYPE LIST لرؤية أنواع الخطوط المتاحة. .ltscale-new-global-line-type-scale = LTSCALE مقياس نوع الخط العام الجديد: diff --git a/locales/bg-BG/opencadstudio.ftl b/locales/bg-BG/opencadstudio.ftl index 788dad7f7..1c951e903 100644 --- a/locales/bg-BG/opencadstudio.ftl +++ b/locales/bg-BG/opencadstudio.ftl @@ -1518,10 +1518,10 @@ common = .are-you-sure-you-want-to-reset-you-will-lose-all-your-current-shortcuts = Сигурни ли сте, че искате нулиране? Ще загубите всички текущи преки пътища! .yes-reset = { common.yes }, { common.reset } .reset-to-default = { common.reset } · { common.default-title-case } - .apply-exit = { common.apply } && { draw.sketch-exit } + .apply-exit = { common.apply } & { draw.sketch-exit } .shortcut-already-used-for-command = Прекият път вече се използва за команда: __ocs_fmt_0__ → __ocs_fmt_1__ .unsaved-changes-will-be-discarded = Незаписаните промени ще бъдат отхвърлени. - .discard-close = { common.discard } && { action.close } + .discard-close = { common.discard } & { action.close } .keep-editing = { common.continue } · { layers.edit } .mcp-control-is-off = Управлението чрез MCP е изключено .mcp-is-handling-a-request = MCP обработва заявка @@ -2320,6 +2320,21 @@ common = .wblock-err-no-path = Моля, задайте име и път на файла. .wblock-err-current-drawing = Не може да се записва в текущия файл с чертеж. .wblock-help-info = WBLOCK записва обекти, блок или целия чертеж в нов чертожен файл. + .blank-drawing = Празен чертеж + .default-template-new-drawings = Шаблон по подразбиране за нови чертежи + .fillet-trim-mode-trim = FILLET Enter Trim mode option [Trim/No trim] : + .fillet-trim-mode-no-trim = FILLET Enter Trim mode option [Trim/No trim] : + .linetype-files = Файлове с типове линии + .load-linetypes = Зареждане на типове линии + .requires-two-positive-values = Изискват се две положителни стойности (x,y) или една за двете. + .snap-marker-color = Цвят на маркера за прихващане + .linetypes-loaded = Заредени типове линии: __ocs_fmt_0__. + .geometric-center-snap = Геометричен център + .no-trim-option = Без подрязване + .chspace-model-tab = CHSPACE: Командата не е разрешена в раздела Модел. + .chspace-no-viewport = CHSPACE: Няма прозорец в план, през който да се смени пространството. + .chspace-to-paper = CHSPACE: __ocs_fmt_0__ обекта преместени в пространството на листа. + .chspace-to-model = CHSPACE: __ocs_fmt_0__ обекта преместени в пространството на модела. errors = .save-warning = Предупреждение при запазване .unable-to-save-drawing = Чертежът не може да бъде запазен @@ -5490,7 +5505,7 @@ styles = .isavebak-set-to-1 = ISAVEBAK зададен на 1 .keep-with-dim-line = Пази с линията за оразм. .layers = СЛоеве - .linetype-list-set = LINETYPE [Списък / Задай]: + .linetype-list-set = LINETYPE [Списък / Задай / Зареждане]: .linetype-set-linetype-name-bylayer-byblock = LINETYPE SET име на тип линия (ByLayer / ByBlock / …): .linetype-name-is-not-loaded-use-linetype-list = LINETYPE: "__ocs_fmt_0__" не е зареден. Използвай LINETYPE LIST, за да видиш наличните типове линии. .ltscale-new-global-line-type-scale = LTSCALE нов глобален мащаб на тип линия: diff --git a/locales/cs-CZ/opencadstudio.ftl b/locales/cs-CZ/opencadstudio.ftl index fabfead9e..a1adf8088 100644 --- a/locales/cs-CZ/opencadstudio.ftl +++ b/locales/cs-CZ/opencadstudio.ftl @@ -1518,10 +1518,10 @@ common = .are-you-sure-you-want-to-reset-you-will-lose-all-your-current-shortcuts = Opravdu chcete obnovit výchozí nastavení? Ztratíte všechny aktuální zkratky! .yes-reset = { common.yes }, { common.reset } .reset-to-default = { common.reset } · { common.default-title-case } - .apply-exit = { common.apply } && { draw.sketch-exit } + .apply-exit = { common.apply } & { draw.sketch-exit } .shortcut-already-used-for-command = Zkratka je již použita pro příkaz: __ocs_fmt_0__ → __ocs_fmt_1__ .unsaved-changes-will-be-discarded = Neuložené změny budou zahozeny. - .discard-close = { common.discard } && { action.close } + .discard-close = { common.discard } & { action.close } .keep-editing = { common.continue } · { layers.edit } .mcp-control-is-off = Ovládání MCP je vypnuté .mcp-is-handling-a-request = MCP zpracovává požadavek @@ -2320,6 +2320,21 @@ common = .wblock-err-no-path = Zadejte název souboru a cestu. .wblock-err-current-drawing = Nelze zapisovat do aktuálního souboru výkresu. .wblock-help-info = WBLOCK zapíše objekty, blok nebo celý výkres do nového výkresového souboru. + .blank-drawing = Prázdný výkres + .default-template-new-drawings = Výchozí šablona pro nové výkresy + .fillet-trim-mode-trim = FILLET Enter Trim mode option [Trim/No trim] : + .fillet-trim-mode-no-trim = FILLET Enter Trim mode option [Trim/No trim] : + .linetype-files = Soubory typů čar + .load-linetypes = Načíst typy čar + .requires-two-positive-values = Vyžaduje dvě kladné hodnoty (x,y) nebo jednu pro obě. + .snap-marker-color = Barva značky uchopení + .linetypes-loaded = Načteno typů čar: __ocs_fmt_0__. + .geometric-center-snap = Geometrický střed + .no-trim-option = Neoříznout + .chspace-model-tab = CHSPACE: Příkaz není povolen na kartě Model. + .chspace-no-viewport = CHSPACE: Není výřez v půdorysném pohledu, přes který změnit prostor. + .chspace-to-paper = CHSPACE: __ocs_fmt_0__ objektů přesunuto do výkresového prostoru. + .chspace-to-model = CHSPACE: __ocs_fmt_0__ objektů přesunuto do modelového prostoru. errors = .save-warning = Uložit varování .unable-to-save-drawing = Nákres nelze uložit @@ -5487,7 +5502,7 @@ styles = .isavebak-set-to-1 = ISAVEBAK nastaven na 1 .keep-with-dim-line = Držte se tlumené linie .layers = Vrstvy - .linetype-list-set = LINETYPE [Seznam / sada]: + .linetype-list-set = LINETYPE [Seznam / sada / Načíst]: .linetype-set-linetype-name-bylayer-byblock = LINETYPE SET název typu čáry (podle hladiny / podle bloku / …): .linetype-name-is-not-loaded-use-linetype-list = LINETYPE: "__ocs_fmt_0__" není načteno. Pomocí LINETYPE LIST zobrazíte dostupné typy čar. .ltscale-new-global-line-type-scale = LTSCALE nové globální měřítko typu čáry: diff --git a/locales/de-DE/opencadstudio.ftl b/locales/de-DE/opencadstudio.ftl index 0a2bec567..113bb8981 100644 --- a/locales/de-DE/opencadstudio.ftl +++ b/locales/de-DE/opencadstudio.ftl @@ -1519,10 +1519,10 @@ common = .are-you-sure-you-want-to-reset-you-will-lose-all-your-current-shortcuts = Wirklich zurücksetzen? Alle aktuellen Tastenkürzel gehen verloren! .yes-reset = { common.yes }, { common.reset } .reset-to-default = { common.reset } · { common.default-title-case } - .apply-exit = { common.apply } && { draw.sketch-exit } + .apply-exit = { common.apply } & { draw.sketch-exit } .shortcut-already-used-for-command = Tastenkürzel bereits für Befehl verwendet: __ocs_fmt_0__ → __ocs_fmt_1__ .unsaved-changes-will-be-discarded = Nicht gespeicherte Änderungen werden verworfen. - .discard-close = { common.discard } && { action.close } + .discard-close = { common.discard } & { action.close } .keep-editing = { common.continue } · { layers.edit } .mcp-control-is-off = MCP-Steuerung ausgeschaltet .mcp-is-handling-a-request = MCP verarbeitet eine Anfrage @@ -2321,6 +2321,21 @@ common = .wblock-err-no-path = Bitte geben Sie einen Dateinamen und Pfad an. .wblock-err-current-drawing = In die aktuelle Zeichnungsdatei kann nicht geschrieben werden. .wblock-help-info = WBLOCK schreibt Objekte, einen Block oder die gesamte Zeichnung in eine neue Zeichnungsdatei. + .blank-drawing = Leere Zeichnung + .default-template-new-drawings = Standardvorlage für neue Zeichnungen + .fillet-trim-mode-trim = FILLET Enter Trim mode option [Trim/No trim] : + .fillet-trim-mode-no-trim = FILLET Enter Trim mode option [Trim/No trim] : + .linetype-files = Linientypdateien + .load-linetypes = Linientypen laden + .requires-two-positive-values = Erfordert zwei positive Werte (x,y) oder einen für beide. + .snap-marker-color = Farbe der Fangmarkierung + .linetypes-loaded = __ocs_fmt_0__ Linientyp(en) geladen. + .geometric-center-snap = Geometrischer Mittelpunkt + .no-trim-option = Nicht stutzen + .chspace-model-tab = CHSPACE: Befehl in der Registerkarte Modell nicht zulässig. + .chspace-no-viewport = CHSPACE: Kein Ansichtsfenster in Draufsicht zum Wechseln des Bereichs. + .chspace-to-paper = CHSPACE: __ocs_fmt_0__ Objekt(e) in den Papierbereich verschoben. + .chspace-to-model = CHSPACE: __ocs_fmt_0__ Objekt(e) in den Modellbereich verschoben. errors = .save-warning = Warnhinweise speichern .unable-to-save-drawing = Zeichnung konnte nicht gespeichert werden @@ -5488,7 +5503,7 @@ styles = .isavebak-set-to-1 = ISAVEBAK auf 1 gesetzt .keep-with-dim-line = Halten Sie mit Dim Line .layers = SCHIENEN - .linetype-list-set = LINETYPE [Liste / Festlegen]: + .linetype-list-set = LINETYPE [Liste / Festlegen / Laden]: .linetype-set-linetype-name-bylayer-byblock = LINETYPE SET Linientypname (ByLayer / ByBlock / …): .linetype-name-is-not-loaded-use-linetype-list = LINETYPE: "__ocs_fmt_0__" ist nicht geladen. Mit LINETYPE LIST werden die verfügbaren Linientypen angezeigt. .ltscale-new-global-line-type-scale = LTSCALE Neuer globaler Linientypfaktor: diff --git a/locales/el-GR/opencadstudio.ftl b/locales/el-GR/opencadstudio.ftl index b8be984f4..f316b9d5e 100644 --- a/locales/el-GR/opencadstudio.ftl +++ b/locales/el-GR/opencadstudio.ftl @@ -1520,10 +1520,10 @@ common = .are-you-sure-you-want-to-reset-you-will-lose-all-your-current-shortcuts = Σίγουρα θέλετε επαναφορά; Θα χαθούν όλες οι τρέχουσες συντομεύσεις σας! .yes-reset = { common.yes }, { common.reset } .reset-to-default = { common.reset } · { common.default-title-case } - .apply-exit = { common.apply } && { draw.sketch-exit } + .apply-exit = { common.apply } & { draw.sketch-exit } .shortcut-already-used-for-command = Η συντόμευση χρησιμοποιείται ήδη για την εντολή: __ocs_fmt_0__ → __ocs_fmt_1__ .unsaved-changes-will-be-discarded = Οι μη αποθηκευμένες αλλαγές θα απορριφθούν. - .discard-close = { common.discard } && { action.close } + .discard-close = { common.discard } & { action.close } .keep-editing = { common.continue } · { layers.edit } .mcp-control-is-off = Έλεγχος MCP απενεργοποιημένος .mcp-is-handling-a-request = Το MCP επεξεργάζεται ένα αίτημα @@ -2320,6 +2320,21 @@ common = .wblock-err-no-path = Καθορίστε ένα όνομα και διαδρομή αρχείου. .wblock-err-current-drawing = Δεν είναι δυνατή η εγγραφή στο τρέχον αρχείο σχεδίου. .wblock-help-info = Η εντολή WBLOCK εγγράφει αντικείμενα, ένα μπλοκ ή ολόκληρο το σχέδιο σε ένα νέο αρχείο σχεδίου. + .blank-drawing = Κενό σχέδιο + .default-template-new-drawings = Προεπιλεγμένο πρότυπο για νέα σχέδια + .fillet-trim-mode-trim = FILLET Enter Trim mode option [Trim/No trim] : + .fillet-trim-mode-no-trim = FILLET Enter Trim mode option [Trim/No trim] : + .linetype-files = Αρχεία τύπων γραμμής + .load-linetypes = Φόρτωση τύπων γραμμής + .requires-two-positive-values = Απαιτούνται δύο θετικές τιμές (x,y) ή μία και για τις δύο. + .snap-marker-color = Χρώμα δείκτη έλξης + .linetypes-loaded = Φορτώθηκαν __ocs_fmt_0__ τύποι γραμμής. + .geometric-center-snap = Γεωμετρικό κέντρο + .no-trim-option = Χωρίς περικοπή + .chspace-model-tab = CHSPACE: Η εντολή δεν επιτρέπεται στην καρτέλα Μοντέλο. + .chspace-no-viewport = CHSPACE: Δεν υπάρχει παράθυρο προβολής κάτοψης για αλλαγή χώρου. + .chspace-to-paper = CHSPACE: __ocs_fmt_0__ αντικείμενα μεταφέρθηκαν στον χώρο χαρτιού. + .chspace-to-model = CHSPACE: __ocs_fmt_0__ αντικείμενα μεταφέρθηκαν στον χώρο μοντέλου. errors = .save-warning = Προειδοποίηση αποθήκευσης .unable-to-save-drawing = Αδυναμία αποθήκευσης σχεδίου @@ -5490,7 +5505,7 @@ styles = .isavebak-set-to-1 = Το ISAVEBAK ορίστηκε σε 1 .keep-with-dim-line = Διατήρηση με τη γραμμή διάστασης .layers = ΕΠίπεδα - .linetype-list-set = LINETYPE [Λίστα / Ορισμός]: + .linetype-list-set = LINETYPE [Λίστα / Ορισμός / Φόρτωση]: .linetype-set-linetype-name-bylayer-byblock = LINETYPE SET όνομα τύπου γραμμής (ByLayer / ByBlock / …): .linetype-name-is-not-loaded-use-linetype-list = LINETYPE: το "__ocs_fmt_0__" δεν έχει φορτωθεί. Χρησιμοποιήστε LINETYPE LIST για να δείτε τους διαθέσιμους τύπους γραμμών. .ltscale-new-global-line-type-scale = LTSCALE νέα καθολική κλίμακα τύπου γραμμής: diff --git a/locales/en-US/opencadstudio.ftl b/locales/en-US/opencadstudio.ftl index 904b74d72..ca46303af 100644 --- a/locales/en-US/opencadstudio.ftl +++ b/locales/en-US/opencadstudio.ftl @@ -1520,10 +1520,10 @@ common = .are-you-sure-you-want-to-reset-you-will-lose-all-your-current-shortcuts = Are you sure you want to reset? You will lose all your current shortcuts! .yes-reset = Yes, reset .reset-to-default = Reset to default - .apply-exit = Apply && Exit + .apply-exit = Apply & Exit .shortcut-already-used-for-command = Shortcut already used for command: __ocs_fmt_0__ → __ocs_fmt_1__ .unsaved-changes-will-be-discarded = Unsaved changes will be discarded. - .discard-close = Discard && close + .discard-close = Discard & close .keep-editing = Keep editing .mcp-control-is-off = MCP control is off .mcp-is-handling-a-request = MCP is handling a request @@ -2321,6 +2321,21 @@ common = .wblock-err-no-path = Please specify a file name and path. .wblock-err-current-drawing = Cannot write to the current drawing file. .wblock-help-info = WBLOCK writes objects, a block, or the entire drawing to a new drawing file. + .blank-drawing = Blank drawing + .default-template-new-drawings = Default template for new drawings + .fillet-trim-mode-trim = FILLET Enter Trim mode option [Trim/No trim] : + .fillet-trim-mode-no-trim = FILLET Enter Trim mode option [Trim/No trim] : + .linetype-files = Linetype Files + .load-linetypes = Load Linetypes + .requires-two-positive-values = Requires two positive values (x,y) or one for both. + .snap-marker-color = Snap marker color + .linetypes-loaded = __ocs_fmt_0__ linetype(s) loaded. + .geometric-center-snap = Geometric Center + .no-trim-option = No trim + .chspace-model-tab = CHSPACE: Command not allowed in the Model tab. + .chspace-no-viewport = CHSPACE: No plan-view viewport to change space through. + .chspace-to-paper = CHSPACE: __ocs_fmt_0__ object(s) changed to paper space. + .chspace-to-model = CHSPACE: __ocs_fmt_0__ object(s) changed to model space. errors = .save-warning = Save Warning .unable-to-save-drawing = Unable to Save Drawing @@ -5491,7 +5506,7 @@ styles = .isavebak-set-to-1 = ISAVEBAK set to 1 .keep-with-dim-line = Keep with dim line .layers = LAyers - .linetype-list-set = LINETYPE [List / Set]: + .linetype-list-set = LINETYPE [List / Set / Load]: .linetype-set-linetype-name-bylayer-byblock = LINETYPE SET linetype name (ByLayer / ByBlock / …): .linetype-name-is-not-loaded-use-linetype-list = LINETYPE: "__ocs_fmt_0__" is not loaded. Use LINETYPE LIST to see available linetypes. .ltscale-new-global-line-type-scale = LTSCALE new global line-type scale: diff --git a/locales/es-ES/opencadstudio.ftl b/locales/es-ES/opencadstudio.ftl index fb3957553..2cf8237be 100644 --- a/locales/es-ES/opencadstudio.ftl +++ b/locales/es-ES/opencadstudio.ftl @@ -1519,10 +1519,10 @@ common = .are-you-sure-you-want-to-reset-you-will-lose-all-your-current-shortcuts = ¿Seguro que desea restablecer? ¡Perderá todos sus atajos actuales! .yes-reset = { common.yes }, { common.reset } .reset-to-default = { common.reset } · { common.default-title-case } - .apply-exit = { common.apply } && { draw.sketch-exit } + .apply-exit = { common.apply } & { draw.sketch-exit } .shortcut-already-used-for-command = Atajo ya usado para el comando: __ocs_fmt_0__ → __ocs_fmt_1__ .unsaved-changes-will-be-discarded = Se descartarán los cambios sin guardar. - .discard-close = { common.discard } && { action.close } + .discard-close = { common.discard } & { action.close } .keep-editing = { common.continue } · { layers.edit } .mcp-control-is-off = Control MCP desactivado .mcp-is-handling-a-request = MCP está procesando una solicitud @@ -2321,6 +2321,21 @@ common = .wblock-err-no-path = Especifique un nombre de archivo y una ruta. .wblock-err-current-drawing = No se puede escribir en el archivo de dibujo actual. .wblock-help-info = WBLOCK escribe objetos, un bloque o el dibujo completo en un nuevo archivo de dibujo. + .blank-drawing = Dibujo en blanco + .default-template-new-drawings = Plantilla predeterminada para dibujos nuevos + .fillet-trim-mode-trim = FILLET Enter Trim mode option [Trim/No trim] : + .fillet-trim-mode-no-trim = FILLET Enter Trim mode option [Trim/No trim] : + .linetype-files = Archivos de tipos de línea + .load-linetypes = Cargar tipos de línea + .requires-two-positive-values = Requiere dos valores positivos (x,y) o uno para ambos. + .snap-marker-color = Color del marcador de referencia + .linetypes-loaded = __ocs_fmt_0__ tipo(s) de línea cargado(s). + .geometric-center-snap = Centro geométrico + .no-trim-option = No recortar + .chspace-model-tab = CHSPACE: Comando no permitido en la ficha Modelo. + .chspace-no-viewport = CHSPACE: No hay ventana gráfica en vista en planta para cambiar de espacio. + .chspace-to-paper = CHSPACE: __ocs_fmt_0__ objeto(s) pasado(s) al espacio papel. + .chspace-to-model = CHSPACE: __ocs_fmt_0__ objeto(s) pasado(s) al espacio modelo. errors = .save-warning = Advertencia de guardado .unable-to-save-drawing = No se puede guardar el dibujo @@ -5490,7 +5505,7 @@ styles = .isavebak-set-to-1 = ISAVEBAK definido como 1 .keep-with-dim-line = Mantener con línea de cota .layers = Capas - .linetype-list-set = LINETYPE [Listar/Definir]: + .linetype-list-set = LINETYPE [Listar/Definir / Cargar]: .linetype-set-linetype-name-bylayer-byblock = LINETYPE SET Nombre del tipo de línea (PorCapa/PorBloque/…): .linetype-name-is-not-loaded-use-linetype-list = LINETYPE: «__ocs_fmt_0__» no está cargado. Use LINETYPE LIST para ver los tipos disponibles. .ltscale-new-global-line-type-scale = LTSCALE Nueva escala global de tipo de línea: diff --git a/locales/fi-FI/opencadstudio.ftl b/locales/fi-FI/opencadstudio.ftl index afe7bb535..c44d1f1c2 100644 --- a/locales/fi-FI/opencadstudio.ftl +++ b/locales/fi-FI/opencadstudio.ftl @@ -1518,10 +1518,10 @@ common = .are-you-sure-you-want-to-reset-you-will-lose-all-your-current-shortcuts = Haluatko varmasti palauttaa oletukset? Kaikki nykyiset pikanäppäimet menetetään! .yes-reset = { common.yes }, { common.reset } .reset-to-default = { common.reset } · { common.default-title-case } - .apply-exit = { common.apply } && { draw.sketch-exit } + .apply-exit = { common.apply } & { draw.sketch-exit } .shortcut-already-used-for-command = Pikanäppäin on jo käytössä komennolla: __ocs_fmt_0__ → __ocs_fmt_1__ .unsaved-changes-will-be-discarded = Tallentamattomat muutokset hylätään. - .discard-close = { common.discard } && { action.close } + .discard-close = { common.discard } & { action.close } .keep-editing = { common.continue } · { layers.edit } .mcp-control-is-off = MCP-ohjaus pois käytöstä .mcp-is-handling-a-request = MCP käsittelee pyyntöä @@ -2320,6 +2320,21 @@ common = .wblock-err-no-path = Määritä tiedoston nimi ja polku. .wblock-err-current-drawing = Nykyiseen piirustustiedostoon ei voi kirjoittaa. .wblock-help-info = WBLOCK kirjoittaa kohteet, lohkon tai koko piirustuksen uuteen piirustustiedostoon. + .blank-drawing = Tyhjä piirustus + .default-template-new-drawings = Uusien piirustusten oletusmalli + .fillet-trim-mode-trim = FILLET Enter Trim mode option [Trim/No trim] : + .fillet-trim-mode-no-trim = FILLET Enter Trim mode option [Trim/No trim] : + .linetype-files = Viivatyyppitiedostot + .load-linetypes = Lataa viivatyypit + .requires-two-positive-values = Vaatii kaksi positiivista arvoa (x,y) tai yhden molemmille. + .snap-marker-color = Kohdistusmerkin väri + .linetypes-loaded = Ladattu __ocs_fmt_0__ viivatyyppiä. + .geometric-center-snap = Geometrinen keskipiste + .no-trim-option = Ei trimmausta + .chspace-model-tab = CHSPACE: Komento ei ole sallittu Malli-välilehdellä. + .chspace-no-viewport = CHSPACE: Ei tasokuvan näkymäikkunaa tilan vaihtoon. + .chspace-to-paper = CHSPACE: __ocs_fmt_0__ objektia siirretty paperitilaan. + .chspace-to-model = CHSPACE: __ocs_fmt_0__ objektia siirretty mallitilaan. errors = .save-warning = Tallenna varoitus .unable-to-save-drawing = Piirustusta ei voi tallentaa @@ -5489,7 +5504,7 @@ styles = .isavebak-set-to-1 = ISAVEBAK asetettu arvoon 1 .keep-with-dim-line = Pysy himmeällä viivalla .layers = Kerrokset - .linetype-list-set = LINETYPE [Lista / sarja]: + .linetype-list-set = LINETYPE [Lista / sarja / Lataa]: .linetype-set-linetype-name-bylayer-byblock = LINETYPE SET viivatyypin nimi (ByLayer / ByBlock / …): .linetype-name-is-not-loaded-use-linetype-list = LINETYPE: "__ocs_fmt_0__" ei ole ladattu. Käytä LINETYPE LIST nähdäksesi käytettävissä olevat linjatyypit. .ltscale-new-global-line-type-scale = LTSCALE uusi globaali viivatyyppinen asteikko: diff --git a/locales/fr-FR/opencadstudio.ftl b/locales/fr-FR/opencadstudio.ftl index 68179d0f4..dfeb98f84 100644 --- a/locales/fr-FR/opencadstudio.ftl +++ b/locales/fr-FR/opencadstudio.ftl @@ -1519,10 +1519,10 @@ common = .are-you-sure-you-want-to-reset-you-will-lose-all-your-current-shortcuts = Voulez-vous vraiment réinitialiser ? Tous vos raccourcis actuels seront perdus ! .yes-reset = { common.yes }, { common.reset } .reset-to-default = { common.reset } · { common.default-title-case } - .apply-exit = { common.apply } && { draw.sketch-exit } + .apply-exit = { common.apply } & { draw.sketch-exit } .shortcut-already-used-for-command = Raccourci déjà utilisé pour la commande : __ocs_fmt_0__ → __ocs_fmt_1__ .unsaved-changes-will-be-discarded = Les modifications non enregistrées seront perdues. - .discard-close = { common.discard } && { action.close } + .discard-close = { common.discard } & { action.close } .keep-editing = { common.continue } · { layers.edit } .mcp-control-is-off = Contrôle MCP désactivé .mcp-is-handling-a-request = MCP traite une requête @@ -2321,6 +2321,21 @@ common = .wblock-err-no-path = Veuillez spécifier un nom de fichier et un chemin d'accès. .wblock-err-current-drawing = Impossible d'écrire dans le fichier de dessin actuel. .wblock-help-info = WBLOCK écrit des objets, un bloc ou l'ensemble du dessin dans un nouveau fichier de dessin. + .blank-drawing = Dessin vierge + .default-template-new-drawings = Gabarit par défaut des nouveaux dessins + .fillet-trim-mode-trim = FILLET Enter Trim mode option [Trim/No trim] : + .fillet-trim-mode-no-trim = FILLET Enter Trim mode option [Trim/No trim] : + .linetype-files = Fichiers de types de ligne + .load-linetypes = Charger des types de ligne + .requires-two-positive-values = Nécessite deux valeurs positives (x,y) ou une seule pour les deux. + .snap-marker-color = Couleur du marqueur d'accrochage + .linetypes-loaded = __ocs_fmt_0__ type(s) de ligne chargé(s). + .geometric-center-snap = Centre géométrique + .no-trim-option = Sans ajuster + .chspace-model-tab = CHSPACE : commande non autorisée dans l'onglet Objet. + .chspace-no-viewport = CHSPACE : aucune fenêtre en vue en plan pour changer d'espace. + .chspace-to-paper = CHSPACE : __ocs_fmt_0__ objet(s) déplacé(s) dans l'espace papier. + .chspace-to-model = CHSPACE : __ocs_fmt_0__ objet(s) déplacé(s) dans l'espace objet. errors = .save-warning = Enregistrer l' avertissement .unable-to-save-drawing = Impossible de sauvegarder le dessin @@ -5490,7 +5505,7 @@ styles = .isavebak-set-to-1 = ISAVEBAK réglé à 1 .keep-with-dim-line = Gardez avec la ligne dim .layers = Autres - .linetype-list-set = LINETYPE [Liste / Définir] : + .linetype-list-set = LINETYPE [Liste / Définir / Charger] : .linetype-set-linetype-name-bylayer-byblock = LINETYPE SET nom de type de ligne (ByLayer / ByBlock / ...): .linetype-name-is-not-loaded-use-linetype-list = LINETYPE: "__ocs_fmt_0__" n'est pas chargé. Utilisez LINETYPE LIST pour voir les types de lignes disponibles. .ltscale-new-global-line-type-scale = LTSCALE nouvelle échelle de type ligne mondiale: diff --git a/locales/hi-IN/opencadstudio.ftl b/locales/hi-IN/opencadstudio.ftl index 3fe0ddd95..fe5621a45 100644 --- a/locales/hi-IN/opencadstudio.ftl +++ b/locales/hi-IN/opencadstudio.ftl @@ -1507,10 +1507,10 @@ common = .are-you-sure-you-want-to-reset-you-will-lose-all-your-current-shortcuts = क्या आप वाकई रीसेट करना चाहते हैं? आपके सभी वर्तमान शॉर्टकट खो जाएँगे! .yes-reset = { common.yes }, { common.reset } .reset-to-default = { common.reset } · { common.default-title-case } - .apply-exit = { common.apply } && { draw.sketch-exit } + .apply-exit = { common.apply } & { draw.sketch-exit } .shortcut-already-used-for-command = शॉर्टकट पहले से इस कमांड के लिए उपयोग में है: __ocs_fmt_0__ → __ocs_fmt_1__ .unsaved-changes-will-be-discarded = बिना सहेजे बदलाव हटा दिए जाएँगे। - .discard-close = { common.discard } && { action.close } + .discard-close = { common.discard } & { action.close } .keep-editing = { common.continue } · { layers.edit } .mcp-control-is-off = MCP नियंत्रण बंद है .mcp-is-handling-a-request = MCP अनुरोध संसाधित कर रहा है @@ -2309,6 +2309,21 @@ common = .wblock-err-no-path = कृपया फ़ाइल का नाम और पथ निर्दिष्ट करें। .wblock-err-current-drawing = वर्तमान आरेखण फ़ाइल में नहीं लिखा जा सकता। .wblock-help-info = WBLOCK ऑब्जेक्ट, एक ब्लॉक, या संपूर्ण रेखाचित्र को एक नई रेखाचित्र फ़ाइल में लिखता है। + .blank-drawing = रिक्त ड्रॉइंग + .default-template-new-drawings = नई ड्रॉइंग के लिए डिफ़ॉल्ट टेम्पलेट + .fillet-trim-mode-trim = FILLET Enter Trim mode option [Trim/No trim] : + .fillet-trim-mode-no-trim = FILLET Enter Trim mode option [Trim/No trim] : + .linetype-files = लाइनटाइप फ़ाइलें + .load-linetypes = लाइनटाइप लोड करें + .requires-two-positive-values = दो धनात्मक मान (x,y) या दोनों के लिए एक मान आवश्यक है। + .snap-marker-color = स्नैप मार्कर का रंग + .linetypes-loaded = __ocs_fmt_0__ लाइनटाइप लोड हुए। + .geometric-center-snap = ज्यामितीय केंद्र + .no-trim-option = ट्रिम नहीं + .chspace-model-tab = CHSPACE: मॉडल टैब में कमांड की अनुमति नहीं है। + .chspace-no-viewport = CHSPACE: स्पेस बदलने के लिए कोई प्लान-व्यू व्यूपोर्ट नहीं। + .chspace-to-paper = CHSPACE: __ocs_fmt_0__ ऑब्जेक्ट पेपर स्पेस में बदले गए। + .chspace-to-model = CHSPACE: __ocs_fmt_0__ ऑब्जेक्ट मॉडल स्पेस में बदले गए। errors = .save-warning = चेतावनी सहेजें .unable-to-save-drawing = ड्रॉइंग सहेजी नहीं जा सकी @@ -5478,7 +5493,7 @@ styles = .isavebak-set-to-1 = ISAVEBAK सेट 1 .keep-with-dim-line = डिम लाइन के साथ रखें .layers = लेयर - .linetype-list-set = LINETYPE [सूची / सेट]: + .linetype-list-set = LINETYPE [सूची / सेट / लोड]: .linetype-set-linetype-name-bylayer-byblock = LINETYPE SET लाइनटाइप का नाम (ByLayer / ByBlock / …): .linetype-name-is-not-loaded-use-linetype-list = LINETYPE: "__ocs_fmt_0__" को लोड नहीं किया गया है। उपलब्ध लाइनटाइप देखने के लिए LINETYPE LIST का प्रयोग करें। .ltscale-new-global-line-type-scale = LTSCALE नई वैश्विक लाइन-प्रकार पैमाने: diff --git a/locales/hu-HU/opencadstudio.ftl b/locales/hu-HU/opencadstudio.ftl index 03ab9ef83..041e9061e 100644 --- a/locales/hu-HU/opencadstudio.ftl +++ b/locales/hu-HU/opencadstudio.ftl @@ -1518,10 +1518,10 @@ common = .are-you-sure-you-want-to-reset-you-will-lose-all-your-current-shortcuts = Biztosan visszaállítja? Az összes jelenlegi gyorsbillentyű elveszik! .yes-reset = { common.yes }, { common.reset } .reset-to-default = { common.reset } · { common.default-title-case } - .apply-exit = { common.apply } && { draw.sketch-exit } + .apply-exit = { common.apply } & { draw.sketch-exit } .shortcut-already-used-for-command = A gyorsbillentyű már használatban van: __ocs_fmt_0__ → __ocs_fmt_1__ .unsaved-changes-will-be-discarded = A nem mentett módosítások elvesznek. - .discard-close = { common.discard } && { action.close } + .discard-close = { common.discard } & { action.close } .keep-editing = { common.continue } · { layers.edit } .mcp-control-is-off = MCP-vezérlés kikapcsolva .mcp-is-handling-a-request = Az MCP kérést dolgoz fel @@ -2320,6 +2320,21 @@ common = .wblock-err-no-path = Adja meg a fájlnevet és az elérési utat. .wblock-err-current-drawing = Nem lehet írni az aktuális rajzfájlba. .wblock-help-info = A WBLOCK objektumokat, egy blokkot vagy a teljes rajzot új rajzfájlba menti. + .blank-drawing = Üres rajz + .default-template-new-drawings = Alapértelmezett sablon új rajzokhoz + .fillet-trim-mode-trim = FILLET Enter Trim mode option [Trim/No trim] : + .fillet-trim-mode-no-trim = FILLET Enter Trim mode option [Trim/No trim] : + .linetype-files = Vonaltípus-fájlok + .load-linetypes = Vonaltípusok betöltése + .requires-two-positive-values = Két pozitív érték (x,y) vagy egy közös érték szükséges. + .snap-marker-color = Illesztési jelölő színe + .linetypes-loaded = __ocs_fmt_0__ vonaltípus betöltve. + .geometric-center-snap = Geometriai középpont + .no-trim-option = Nincs vágás + .chspace-model-tab = CHSPACE: A parancs a Modell lapon nem engedélyezett. + .chspace-no-viewport = CHSPACE: Nincs felülnézeti nézetablak a tér váltásához. + .chspace-to-paper = CHSPACE: __ocs_fmt_0__ objektum papírtérbe helyezve. + .chspace-to-model = CHSPACE: __ocs_fmt_0__ objektum modelltérbe helyezve. errors = .save-warning = Figyelmeztetés mentése .unable-to-save-drawing = Nem sikerült menteni a rajzot @@ -5489,7 +5504,7 @@ styles = .isavebak-set-to-1 = ISAVEBAK 1-re állítva .keep-with-dim-line = Tartsa halvány vonallal .layers = Rétegek - .linetype-list-set = LINETYPE [Lista / Készlet]: + .linetype-list-set = LINETYPE [Lista / Készlet / Betöltés]: .linetype-set-linetype-name-bylayer-byblock = LINETYPE SET vonaltípus neve (ByLayer / ByBlock / …): .linetype-name-is-not-loaded-use-linetype-list = LINETYPE: A "__ocs_fmt_0__" nincs betöltve. A rendelkezésre álló vonaltípusok megtekintéséhez használja a LINETYPE LIST parancsot. .ltscale-new-global-line-type-scale = LTSCALE új globális vonal típusú skála: diff --git a/locales/it-IT/opencadstudio.ftl b/locales/it-IT/opencadstudio.ftl index 0e47586df..a1df56f45 100644 --- a/locales/it-IT/opencadstudio.ftl +++ b/locales/it-IT/opencadstudio.ftl @@ -1518,10 +1518,10 @@ common = .are-you-sure-you-want-to-reset-you-will-lose-all-your-current-shortcuts = Vuoi davvero reimpostare? Perderai tutte le scorciatoie attuali! .yes-reset = { common.yes }, { common.reset } .reset-to-default = { common.reset } · { common.default-title-case } - .apply-exit = { common.apply } && { draw.sketch-exit } + .apply-exit = { common.apply } & { draw.sketch-exit } .shortcut-already-used-for-command = Scorciatoia già usata per il comando: __ocs_fmt_0__ → __ocs_fmt_1__ .unsaved-changes-will-be-discarded = Le modifiche non salvate verranno scartate. - .discard-close = { common.discard } && { action.close } + .discard-close = { common.discard } & { action.close } .keep-editing = { common.continue } · { layers.edit } .mcp-control-is-off = Controllo MCP disattivato .mcp-is-handling-a-request = MCP sta elaborando una richiesta @@ -2320,6 +2320,21 @@ common = .wblock-err-no-path = Specificare un nome file e un percorso. .wblock-err-current-drawing = Impossibile scrivere nel file di disegno corrente. .wblock-help-info = WBLOCK scrive gli oggetti, un blocco o l'intero disegno in un nuovo file di disegno. + .blank-drawing = Disegno vuoto + .default-template-new-drawings = Modello predefinito per i nuovi disegni + .fillet-trim-mode-trim = FILLET Enter Trim mode option [Trim/No trim] : + .fillet-trim-mode-no-trim = FILLET Enter Trim mode option [Trim/No trim] : + .linetype-files = File dei tipi di linea + .load-linetypes = Carica tipi di linea + .requires-two-positive-values = Richiede due valori positivi (x,y) o uno per entrambi. + .snap-marker-color = Colore del marcatore di snap + .linetypes-loaded = __ocs_fmt_0__ tipo/i di linea caricato/i. + .geometric-center-snap = Centro geometrico + .no-trim-option = Non tagliare + .chspace-model-tab = CHSPACE: comando non consentito nella scheda Modello. + .chspace-no-viewport = CHSPACE: nessuna finestra in vista in pianta attraverso cui cambiare spazio. + .chspace-to-paper = CHSPACE: __ocs_fmt_0__ oggetto/i spostato/i nello spazio carta. + .chspace-to-model = CHSPACE: __ocs_fmt_0__ oggetto/i spostato/i nello spazio modello. errors = .save-warning = Salva avviso .unable-to-save-drawing = Impossibile salvare il disegno @@ -5489,7 +5504,7 @@ styles = .isavebak-set-to-1 = ISAVEBAK impostato su 1 .keep-with-dim-line = Continua con la linea debole .layers = Livelli - .linetype-list-set = LINETYPE [Elenco/Imposta]: + .linetype-list-set = LINETYPE [Elenco/Imposta / Carica]: .linetype-set-linetype-name-bylayer-byblock = LINETYPE SET nome del tipo di linea (DaLayer / DaBlocco / …): .linetype-name-is-not-loaded-use-linetype-list = LINETYPE: "__ocs_fmt_0__" non è caricato. Utilizza LINETYPE LIST per vedere i tipi di linea disponibili. .ltscale-new-global-line-type-scale = LTSCALE nuova scala globale di tipo linea: diff --git a/locales/ja-JP/opencadstudio.ftl b/locales/ja-JP/opencadstudio.ftl index c1def5ba7..ade38d6d5 100644 --- a/locales/ja-JP/opencadstudio.ftl +++ b/locales/ja-JP/opencadstudio.ftl @@ -1509,10 +1509,10 @@ common = .are-you-sure-you-want-to-reset-you-will-lose-all-your-current-shortcuts = リセットしてもよろしいですか?現在のすべてのショートカットが失われます! .yes-reset = { common.yes }, { common.reset } .reset-to-default = { common.reset } · { common.default-title-case } - .apply-exit = { common.apply } && { draw.sketch-exit } + .apply-exit = { common.apply } & { draw.sketch-exit } .shortcut-already-used-for-command = ショートカットは既に次のコマンドで使用されています: __ocs_fmt_0__ → __ocs_fmt_1__ .unsaved-changes-will-be-discarded = 未保存の変更は破棄されます。 - .discard-close = { common.discard } && { action.close } + .discard-close = { common.discard } & { action.close } .keep-editing = { common.continue } · { layers.edit } .mcp-control-is-off = MCP制御は無効です .mcp-is-handling-a-request = MCPがリクエストを処理中です @@ -2311,6 +2311,21 @@ common = .wblock-err-no-path = ファイル名とパスを指定してください。 .wblock-err-current-drawing = 現在の図面ファイルには書き込めません。 .wblock-help-info = WBLOCK はオブジェクト、ブロック、または図面全体を新しい図面ファイルに書き出します。 + .blank-drawing = 空の図面 + .default-template-new-drawings = 新規図面の既定テンプレート + .fillet-trim-mode-trim = FILLET Enter Trim mode option [Trim/No trim] : + .fillet-trim-mode-no-trim = FILLET Enter Trim mode option [Trim/No trim] : + .linetype-files = 線種ファイル + .load-linetypes = 線種をロード + .requires-two-positive-values = 2つの正の値 (x,y)、または両方に共通の1つの値が必要です。 + .snap-marker-color = スナップ マーカーの色 + .linetypes-loaded = __ocs_fmt_0__ 個の線種をロードしました。 + .geometric-center-snap = 図心 + .no-trim-option = トリムしない + .chspace-model-tab = CHSPACE: モデル タブではこのコマンドを使用できません。 + .chspace-no-viewport = CHSPACE: 空間を変更できる平面ビューのビューポートがありません。 + .chspace-to-paper = CHSPACE: __ocs_fmt_0__ 個のオブジェクトをペーパー空間に移動しました。 + .chspace-to-model = CHSPACE: __ocs_fmt_0__ 個のオブジェクトをモデル空間に移動しました。 errors = .save-warning = 保存の警告 .unable-to-save-drawing = 図面を保存できません @@ -5478,7 +5493,7 @@ styles = .isavebak-set-to-1 = ISAVEBAK を 1 に設定しました .keep-with-dim-line = 寸法線と共に保持 .layers = レイヤー - .linetype-list-set = LINETYPE [一覧 / 設定]: + .linetype-list-set = LINETYPE [一覧 / 設定 / ロード]: .linetype-set-linetype-name-bylayer-byblock = LINETYPE SET 線種名(レイヤー依存 / ブロック依存 / …): .linetype-name-is-not-loaded-use-linetype-list = LINETYPE: "__ocs_fmt_0__" は未読込です。LINETYPE LIST で利用可能な線種を確認してください。 .ltscale-new-global-line-type-scale = LTSCALE 新しいグローバル線種尺度: diff --git a/locales/ko-KR/opencadstudio.ftl b/locales/ko-KR/opencadstudio.ftl index 38deb29bd..939c2e0c2 100644 --- a/locales/ko-KR/opencadstudio.ftl +++ b/locales/ko-KR/opencadstudio.ftl @@ -1518,10 +1518,10 @@ common = .are-you-sure-you-want-to-reset-you-will-lose-all-your-current-shortcuts = 정말 초기화하시겠습니까? 현재 단축키가 모두 삭제됩니다! .yes-reset = { common.yes }, { common.reset } .reset-to-default = { common.reset } · { common.default-title-case } - .apply-exit = { common.apply } && { draw.sketch-exit } + .apply-exit = { common.apply } & { draw.sketch-exit } .shortcut-already-used-for-command = 단축키가 이미 다음 명령에 사용 중입니다: __ocs_fmt_0__ → __ocs_fmt_1__ .unsaved-changes-will-be-discarded = 저장하지 않은 변경 사항은 삭제됩니다. - .discard-close = { common.discard } && { action.close } + .discard-close = { common.discard } & { action.close } .keep-editing = { common.continue } · { layers.edit } .mcp-control-is-off = MCP 제어가 꺼져 있습니다 .mcp-is-handling-a-request = MCP가 요청을 처리 중입니다 @@ -2320,6 +2320,21 @@ common = .wblock-err-no-path = 파일 이름과 경로를 지정하십시오. .wblock-err-current-drawing = 현재 도면 파일에는 쓸 수 없습니다. .wblock-help-info = WBLOCK은 객체, 블록 또는 도면 전체를 새 도면 파일로 내보냅니다. + .blank-drawing = 빈 도면 + .default-template-new-drawings = 새 도면의 기본 템플릿 + .fillet-trim-mode-trim = FILLET Enter Trim mode option [Trim/No trim] : + .fillet-trim-mode-no-trim = FILLET Enter Trim mode option [Trim/No trim] : + .linetype-files = 선종류 파일 + .load-linetypes = 선종류 로드 + .requires-two-positive-values = 양수 값 두 개(x,y) 또는 둘 다에 쓸 값 하나가 필요합니다. + .snap-marker-color = 스냅 마커 색상 + .linetypes-loaded = 선종류 __ocs_fmt_0__개를 로드했습니다. + .geometric-center-snap = 기하학적 중심 + .no-trim-option = 자르지 않기 + .chspace-model-tab = CHSPACE: 모델 탭에서는 명령을 사용할 수 없습니다. + .chspace-no-viewport = CHSPACE: 공간을 변경할 평면 뷰포트가 없습니다. + .chspace-to-paper = CHSPACE: 객체 __ocs_fmt_0__개를 도면 공간으로 옮겼습니다. + .chspace-to-model = CHSPACE: 객체 __ocs_fmt_0__개를 모형 공간으로 옮겼습니다. errors = .save-warning = 경고 저장 .unable-to-save-drawing = 도면을 저장할 수 없습니다 @@ -5489,7 +5504,7 @@ styles = .isavebak-set-to-1 = ISAVEBAK가 1로 설정됨 .keep-with-dim-line = 희미한 선으로 유지 .layers = 레이어 - .linetype-list-set = LINETYPE [목록/설정]: + .linetype-list-set = LINETYPE [목록/설정 / 로드]: .linetype-set-linetype-name-bylayer-byblock = LINETYPE SET 선종류 이름(ByLayer / ByBlock / …): .linetype-name-is-not-loaded-use-linetype-list = LINETYPE: "__ocs_fmt_0__"가 로드되지 않았습니다. 사용 가능한 선종류를 보려면 LINETYPE LIST를 사용하세요. .ltscale-new-global-line-type-scale = LTSCALE 새로운 글로벌 라인형 스케일: diff --git a/locales/nl-NL/opencadstudio.ftl b/locales/nl-NL/opencadstudio.ftl index eebd34be8..a7e3748bc 100644 --- a/locales/nl-NL/opencadstudio.ftl +++ b/locales/nl-NL/opencadstudio.ftl @@ -1517,10 +1517,10 @@ common = .are-you-sure-you-want-to-reset-you-will-lose-all-your-current-shortcuts = Weet u zeker dat u wilt herstellen? Al uw huidige sneltoetsen gaan verloren! .yes-reset = { common.yes }, { common.reset } .reset-to-default = { common.reset } · { common.default-title-case } - .apply-exit = { common.apply } && { draw.sketch-exit } + .apply-exit = { common.apply } & { draw.sketch-exit } .shortcut-already-used-for-command = Sneltoets al gebruikt voor opdracht: __ocs_fmt_0__ → __ocs_fmt_1__ .unsaved-changes-will-be-discarded = Niet-opgeslagen wijzigingen worden verworpen. - .discard-close = { common.discard } && { action.close } + .discard-close = { common.discard } & { action.close } .keep-editing = { common.continue } · { layers.edit } .mcp-control-is-off = MCP-besturing uitgeschakeld .mcp-is-handling-a-request = MCP verwerkt een verzoek @@ -2321,6 +2321,21 @@ common = .wblock-err-no-path = Geef een bestandsnaam en pad op. .wblock-err-current-drawing = Kan niet schrijven naar het huidige tekeningbestand. .wblock-help-info = WBLOCK schrijft objecten, een blok of de volledige tekening naar een nieuw tekeningbestand. + .blank-drawing = Lege tekening + .default-template-new-drawings = Standaardsjabloon voor nieuwe tekeningen + .fillet-trim-mode-trim = FILLET Enter Trim mode option [Trim/No trim] : + .fillet-trim-mode-no-trim = FILLET Enter Trim mode option [Trim/No trim] : + .linetype-files = Lijntypebestanden + .load-linetypes = Lijntypen laden + .requires-two-positive-values = Vereist twee positieve waarden (x,y) of één voor beide. + .snap-marker-color = Kleur van snapmarkering + .linetypes-loaded = __ocs_fmt_0__ lijntype(n) geladen. + .geometric-center-snap = Geometrisch middelpunt + .no-trim-option = Niet trimmen + .chspace-model-tab = CHSPACE: opdracht niet toegestaan op het tabblad Model. + .chspace-no-viewport = CHSPACE: geen viewport in bovenaanzicht om de ruimte via te wisselen. + .chspace-to-paper = CHSPACE: __ocs_fmt_0__ object(en) naar de papierruimte verplaatst. + .chspace-to-model = CHSPACE: __ocs_fmt_0__ object(en) naar de modelruimte verplaatst. errors = .save-warning = Waarschuwing opslaan .unable-to-save-drawing = Kan tekening niet opslaan @@ -5488,7 +5503,7 @@ styles = .isavebak-set-to-1 = ISAVEBAK ingesteld op 1 .keep-with-dim-line = Houden met dim lijn .layers = Lagen - .linetype-list-set = LINETYPE [Lijst / Instellen]: + .linetype-list-set = LINETYPE [Lijst / Instellen / Laden]: .linetype-set-linetype-name-bylayer-byblock = LINETYPE SET Lijntypenaam (ByLayer / ByBlock / …): .linetype-name-is-not-loaded-use-linetype-list = LINETYPE: "__ocs_fmt_0__" is niet geladen. Gebruik LINETYPE LIST om de beschikbare lijntypen te bekijken. .ltscale-new-global-line-type-scale = LTSCALE nieuwe mondiale lijnschaal: diff --git a/locales/pl-PL/opencadstudio.ftl b/locales/pl-PL/opencadstudio.ftl index fdb7cc4c7..0331ad907 100644 --- a/locales/pl-PL/opencadstudio.ftl +++ b/locales/pl-PL/opencadstudio.ftl @@ -1518,10 +1518,10 @@ common = .are-you-sure-you-want-to-reset-you-will-lose-all-your-current-shortcuts = Na pewno zresetować? Wszystkie bieżące skróty zostaną utracone! .yes-reset = { common.yes }, { common.reset } .reset-to-default = { common.reset } · { common.default-title-case } - .apply-exit = { common.apply } && { draw.sketch-exit } + .apply-exit = { common.apply } & { draw.sketch-exit } .shortcut-already-used-for-command = Skrót jest już używany dla polecenia: __ocs_fmt_0__ → __ocs_fmt_1__ .unsaved-changes-will-be-discarded = Niezapisane zmiany zostaną odrzucone. - .discard-close = { common.discard } && { action.close } + .discard-close = { common.discard } & { action.close } .keep-editing = { common.continue } · { layers.edit } .mcp-control-is-off = Sterowanie MCP wyłączone .mcp-is-handling-a-request = MCP przetwarza żądanie @@ -2320,6 +2320,21 @@ common = .wblock-err-no-path = Podaj nazwę pliku i ścieżkę. .wblock-err-current-drawing = Nie można zapisać do bieżącego pliku rysunku. .wblock-help-info = WBLOCK zapisuje obiekty, blok lub cały rysunek do nowego pliku rysunku. + .blank-drawing = Pusty rysunek + .default-template-new-drawings = Domyślny szablon nowych rysunków + .fillet-trim-mode-trim = FILLET Enter Trim mode option [Trim/No trim] : + .fillet-trim-mode-no-trim = FILLET Enter Trim mode option [Trim/No trim] : + .linetype-files = Pliki rodzajów linii + .load-linetypes = Wczytaj rodzaje linii + .requires-two-positive-values = Wymaga dwóch wartości dodatnich (x,y) lub jednej dla obu. + .snap-marker-color = Kolor znacznika przyciągania + .linetypes-loaded = Wczytano rodzaje linii: __ocs_fmt_0__. + .geometric-center-snap = Środek geometryczny + .no-trim-option = Bez przycinania + .chspace-model-tab = CHSPACE: polecenie niedostępne na karcie Model. + .chspace-no-viewport = CHSPACE: brak rzutni w widoku z góry do zmiany obszaru. + .chspace-to-paper = CHSPACE: przeniesiono obiekty do obszaru papieru: __ocs_fmt_0__. + .chspace-to-model = CHSPACE: przeniesiono obiekty do obszaru modelu: __ocs_fmt_0__. errors = .save-warning = Zapisz ostrzeżenie .unable-to-save-drawing = Nie można zapisać rysunku @@ -5489,7 +5504,7 @@ styles = .isavebak-set-to-1 = ISAVEBAK ustawione na 1 .keep-with-dim-line = Trzymaj się ciemnej linii .layers = Warstwy - .linetype-list-set = LINETYPE [Lista / Zestaw]: + .linetype-list-set = LINETYPE [Lista / Zestaw / Wczytaj]: .linetype-set-linetype-name-bylayer-byblock = LINETYPE SET nazwa rodzaju linii (JakWarstwa / JakBlok / …): .linetype-name-is-not-loaded-use-linetype-list = LINETYPE: „__ocs_fmt_0__” nie jest załadowany. Użyj LINETYPE LIST, aby zobaczyć dostępne rodzaje linii. .ltscale-new-global-line-type-scale = LTSCALE nowa globalna skala liniowa: diff --git a/locales/pt-BR/opencadstudio.ftl b/locales/pt-BR/opencadstudio.ftl index e09630951..f2d153fad 100644 --- a/locales/pt-BR/opencadstudio.ftl +++ b/locales/pt-BR/opencadstudio.ftl @@ -1519,10 +1519,10 @@ common = .are-you-sure-you-want-to-reset-you-will-lose-all-your-current-shortcuts = Deseja mesmo redefinir? Todos os atalhos atuais serão perdidos! .yes-reset = { common.yes }, { common.reset } .reset-to-default = { common.reset } · { common.default-title-case } - .apply-exit = { common.apply } && { draw.sketch-exit } + .apply-exit = { common.apply } & { draw.sketch-exit } .shortcut-already-used-for-command = Atalho já usado para o comando: __ocs_fmt_0__ → __ocs_fmt_1__ .unsaved-changes-will-be-discarded = As alterações não salvas serão descartadas. - .discard-close = { common.discard } && { action.close } + .discard-close = { common.discard } & { action.close } .keep-editing = { common.continue } · { layers.edit } .mcp-control-is-off = Controle MCP desativado .mcp-is-handling-a-request = MCP está processando uma solicitação @@ -2321,6 +2321,21 @@ common = .wblock-err-no-path = Especifique um nome de arquivo e caminho. .wblock-err-current-drawing = Não é possível gravar no arquivo de desenho atual. .wblock-help-info = O WBLOCK grava objetos, um bloco ou o desenho inteiro em um novo arquivo de desenho. + .blank-drawing = Desenho em branco + .default-template-new-drawings = Modelo padrão para novos desenhos + .fillet-trim-mode-trim = FILLET Enter Trim mode option [Trim/No trim] : + .fillet-trim-mode-no-trim = FILLET Enter Trim mode option [Trim/No trim] : + .linetype-files = Arquivos de tipos de linha + .load-linetypes = Carregar tipos de linha + .requires-two-positive-values = Requer dois valores positivos (x,y) ou um para ambos. + .snap-marker-color = Cor do marcador de snap + .linetypes-loaded = __ocs_fmt_0__ tipo(s) de linha carregado(s). + .geometric-center-snap = Centro geométrico + .no-trim-option = Não aparar + .chspace-model-tab = CHSPACE: comando não permitido na guia Modelo. + .chspace-no-viewport = CHSPACE: nenhuma janela de vista em planta para mudar de espaço. + .chspace-to-paper = CHSPACE: __ocs_fmt_0__ objeto(s) movido(s) para o espaço do papel. + .chspace-to-model = CHSPACE: __ocs_fmt_0__ objeto(s) movido(s) para o espaço do modelo. errors = .save-warning = Aviso de salvamento .unable-to-save-drawing = Não foi possível salvar o desenho @@ -5490,7 +5505,7 @@ styles = .isavebak-set-to-1 = ISAVEBAK definido como 1 .keep-with-dim-line = Manter com a linha de cota .layers = CAmadas - .linetype-list-set = LINETYPE [Listar / Definir]: + .linetype-list-set = LINETYPE [Listar / Definir / Carregar]: .linetype-set-linetype-name-bylayer-byblock = LINETYPE SET nome do tipo de linha (PorCamada / PorBloco / …): .linetype-name-is-not-loaded-use-linetype-list = LINETYPE: “__ocs_fmt_0__” não está carregado. Use LINETYPE LIST para ver os tipos disponíveis. .ltscale-new-global-line-type-scale = LTSCALE nova escala global de tipos de linha: diff --git a/locales/ru-RU/opencadstudio.ftl b/locales/ru-RU/opencadstudio.ftl index 045c83bdb..213aa7a0a 100644 --- a/locales/ru-RU/opencadstudio.ftl +++ b/locales/ru-RU/opencadstudio.ftl @@ -1522,10 +1522,10 @@ common = .are-you-sure-you-want-to-reset-you-will-lose-all-your-current-shortcuts = Сбросить настройки? Все текущие сочетания клавиш будут потеряны! .yes-reset = { common.yes }, { common.reset } .reset-to-default = { common.reset } · { common.default-title-case } - .apply-exit = { common.apply } && { draw.sketch-exit } + .apply-exit = { common.apply } & { draw.sketch-exit } .shortcut-already-used-for-command = Сочетание уже используется для команды: __ocs_fmt_0__ → __ocs_fmt_1__ .unsaved-changes-will-be-discarded = Несохранённые изменения будут потеряны. - .discard-close = { common.discard } && { action.close } + .discard-close = { common.discard } & { action.close } .keep-editing = { common.continue } · { layers.edit } .mcp-control-is-off = Управление MCP выключено .mcp-is-handling-a-request = MCP обрабатывает запрос @@ -2324,6 +2324,21 @@ common = .wblock-err-no-path = Укажите имя файла и путь к нему. .wblock-err-current-drawing = Невозможно записать в текущий файл чертежа. .wblock-help-info = WBLOCK записывает объекты, блок или весь чертеж в новый файл чертежа. + .blank-drawing = Пустой чертёж + .default-template-new-drawings = Шаблон по умолчанию для новых чертежей + .fillet-trim-mode-trim = FILLET Enter Trim mode option [Trim/No trim] : + .fillet-trim-mode-no-trim = FILLET Enter Trim mode option [Trim/No trim] : + .linetype-files = Файлы типов линий + .load-linetypes = Загрузить типы линий + .requires-two-positive-values = Требуются два положительных значения (x,y) или одно для обоих. + .snap-marker-color = Цвет маркера привязки + .linetypes-loaded = Загружено типов линий: __ocs_fmt_0__. + .geometric-center-snap = Геометрический центр + .no-trim-option = Без обрезки + .chspace-model-tab = CHSPACE: команда недоступна на вкладке «Модель». + .chspace-no-viewport = CHSPACE: нет видового экрана в плане для смены пространства. + .chspace-to-paper = CHSPACE: перенесено в пространство листа объектов: __ocs_fmt_0__. + .chspace-to-model = CHSPACE: перенесено в пространство модели объектов: __ocs_fmt_0__. errors = .save-warning = Спасти предостережение .unable-to-save-drawing = Не удалось сохранить чертёж @@ -5491,7 +5506,7 @@ styles = .isavebak-set-to-1 = ISAVEBAK установленный на 1 .keep-with-dim-line = Оставлять рядом с размерной линией .layers = Лэйерс - .linetype-list-set = LINETYPE [Список / Задать]: + .linetype-list-set = LINETYPE [Список / Задать / Загрузить]: .linetype-set-linetype-name-bylayer-byblock = LINETYPE SET Имя типа линии (ByLayer / ByBlock / …): .linetype-name-is-not-loaded-use-linetype-list = LINETYPE: "__ocs_fmt_0__" не загружен. Используйте LINETYPE LIST для просмотра доступных типов линий. .ltscale-new-global-line-type-scale = LTSCALE Новый глобальный масштаб типов линий: diff --git a/locales/tr-TR/opencadstudio.ftl b/locales/tr-TR/opencadstudio.ftl index 1906ccc46..6974aef5c 100644 --- a/locales/tr-TR/opencadstudio.ftl +++ b/locales/tr-TR/opencadstudio.ftl @@ -1505,10 +1505,10 @@ common = .are-you-sure-you-want-to-reset-you-will-lose-all-your-current-shortcuts = Sıfırlamak istediğinizden emin misiniz? Mevcut tüm kısayollarınız silinecek! .yes-reset = { common.yes }, { common.reset } .reset-to-default = { common.reset } · { common.default-title-case } - .apply-exit = { common.apply } && { draw.sketch-exit } + .apply-exit = { common.apply } & { draw.sketch-exit } .shortcut-already-used-for-command = Kısayol zaten şu komut için kullanılıyor: __ocs_fmt_0__ → __ocs_fmt_1__ .unsaved-changes-will-be-discarded = Kaydedilmemiş değişiklikler atılacak. - .discard-close = { common.discard } && { action.close } + .discard-close = { common.discard } & { action.close } .keep-editing = { common.continue } · { layers.edit } .mcp-control-is-off = MCP denetimi kapalı .mcp-is-handling-a-request = MCP bir isteği işliyor @@ -2305,6 +2305,21 @@ common = .wblock-err-no-path = Lütfen bir dosya adı ve yolu belirtin. .wblock-err-current-drawing = Geçerli çizim dosyasına yazılamıyor. .wblock-help-info = WBLOCK nesneleri, bir bloğu veya tüm çizimi yeni bir çizim dosyasına yazar. + .blank-drawing = Boş çizim + .default-template-new-drawings = Yeni çizimler için varsayılan şablon + .fillet-trim-mode-trim = FILLET Enter Trim mode option [Trim/No trim] : + .fillet-trim-mode-no-trim = FILLET Enter Trim mode option [Trim/No trim] : + .linetype-files = Çizgi tipi dosyaları + .load-linetypes = Çizgi tiplerini yükle + .requires-two-positive-values = İki pozitif değer (x,y) ya da ikisi için tek değer gerekir. + .snap-marker-color = Yakalama işaretçisi rengi + .linetypes-loaded = __ocs_fmt_0__ çizgi tipi yüklendi. + .geometric-center-snap = Geometrik merkez + .no-trim-option = Budama yok + .chspace-model-tab = CHSPACE: Komut Model sekmesinde kullanılamaz. + .chspace-no-viewport = CHSPACE: Alan değiştirmek için plan görünümlü görünüm penceresi yok. + .chspace-to-paper = CHSPACE: __ocs_fmt_0__ nesne kağıt alanına taşındı. + .chspace-to-model = CHSPACE: __ocs_fmt_0__ nesne model alanına taşındı. errors = .save-warning = Kaydetme Uyarısı .unable-to-save-drawing = Çizim Kaydedilemiyor @@ -5430,7 +5445,7 @@ styles = .isavebak-set-to-1 = ISAVEBAK 1 olarak ayarlandı .keep-with-dim-line = Ölçü çizgisiyle birlikte tut .layers = Katmanlar - .linetype-list-set = LINETYPE [Listele / Ayarla]: + .linetype-list-set = LINETYPE [Listele / Ayarla / Yükle]: .linetype-set-linetype-name-bylayer-byblock = LINETYPE SET Çizgi tipi adı (Katmandan / Bloktan / …): .linetype-name-is-not-loaded-use-linetype-list = LINETYPE: "__ocs_fmt_0__" yüklü değil. Kullanılabilir çizgi tipleri için LINETYPE LIST komutunu kullanın. .ltscale-new-global-line-type-scale = LTSCALE Yeni genel çizgi tipi ölçeği: diff --git a/locales/zh-CN/opencadstudio.ftl b/locales/zh-CN/opencadstudio.ftl index 7f8940169..768db134b 100644 --- a/locales/zh-CN/opencadstudio.ftl +++ b/locales/zh-CN/opencadstudio.ftl @@ -1506,10 +1506,10 @@ common = .are-you-sure-you-want-to-reset-you-will-lose-all-your-current-shortcuts = 确定要重置吗?所有当前快捷键都将丢失! .yes-reset = { common.yes }, { common.reset } .reset-to-default = { common.reset } · { common.default-title-case } - .apply-exit = { common.apply } && { draw.sketch-exit } + .apply-exit = { common.apply } & { draw.sketch-exit } .shortcut-already-used-for-command = 快捷键已用于命令:__ocs_fmt_0__ → __ocs_fmt_1__ .unsaved-changes-will-be-discarded = 未保存的更改将被丢弃。 - .discard-close = { common.discard } && { action.close } + .discard-close = { common.discard } & { action.close } .keep-editing = { common.continue } · { layers.edit } .mcp-control-is-off = MCP 控制已关闭 .mcp-is-handling-a-request = MCP 正在处理请求 @@ -2308,6 +2308,21 @@ common = .wblock-err-no-path = 请指定文件名和路径。 .wblock-err-current-drawing = 无法写入当前图形文件。 .wblock-help-info = WBLOCK 将对象、块或整个图形写入新的图形文件。 + .blank-drawing = 空白图形 + .default-template-new-drawings = 新图形的默认样板 + .fillet-trim-mode-trim = FILLET Enter Trim mode option [Trim/No trim] : + .fillet-trim-mode-no-trim = FILLET Enter Trim mode option [Trim/No trim] : + .linetype-files = 线型文件 + .load-linetypes = 加载线型 + .requires-two-positive-values = 需要两个正值 (x,y),或一个同时用于两者的值。 + .snap-marker-color = 捕捉标记颜色 + .linetypes-loaded = 已加载 __ocs_fmt_0__ 个线型。 + .geometric-center-snap = 几何中心 + .no-trim-option = 不修剪 + .chspace-model-tab = CHSPACE: 在“模型”选项卡中不允许使用此命令。 + .chspace-no-viewport = CHSPACE: 没有可用于更改空间的平面视图视口。 + .chspace-to-paper = CHSPACE: 已将 __ocs_fmt_0__ 个对象移到图纸空间。 + .chspace-to-model = CHSPACE: 已将 __ocs_fmt_0__ 个对象移到模型空间。 errors = .save-warning = 保存警告 .unable-to-save-drawing = 无法保存图形 @@ -5473,7 +5488,7 @@ styles = .isavebak-set-to-1 = ISAVEBAK 设置为 1 .keep-with-dim-line = 保持暗线 .layers = 图层 - .linetype-list-set = LINETYPE [列表 / 设置]: + .linetype-list-set = LINETYPE [列表 / 设置 / 加载]: .linetype-set-linetype-name-bylayer-byblock = LINETYPE SET 线型名称(ByLayer / ByBlock / …): .linetype-name-is-not-loaded-use-linetype-list = LINETYPE:“__ocs_fmt_0__”未加载。使用 LINETYPE LIST 查看可用线型。 .ltscale-new-global-line-type-scale = LTSCALE 新的全局线型尺度 : diff --git a/locales/zh-TW/opencadstudio.ftl b/locales/zh-TW/opencadstudio.ftl index 532c0056b..57a0647ee 100644 --- a/locales/zh-TW/opencadstudio.ftl +++ b/locales/zh-TW/opencadstudio.ftl @@ -1518,10 +1518,10 @@ common = .are-you-sure-you-want-to-reset-you-will-lose-all-your-current-shortcuts = 確定要重設嗎?所有目前快捷鍵都將遺失! .yes-reset = { common.yes }, { common.reset } .reset-to-default = { common.reset } · { common.default-title-case } - .apply-exit = { common.apply } && { draw.sketch-exit } + .apply-exit = { common.apply } & { draw.sketch-exit } .shortcut-already-used-for-command = 快捷鍵已用於指令:__ocs_fmt_0__ → __ocs_fmt_1__ .unsaved-changes-will-be-discarded = 未儲存的變更將被捨棄。 - .discard-close = { common.discard } && { action.close } + .discard-close = { common.discard } & { action.close } .keep-editing = { common.continue } · { layers.edit } .mcp-control-is-off = MCP 控制已關閉 .mcp-is-handling-a-request = MCP 正在處理要求 @@ -2320,6 +2320,21 @@ common = .wblock-err-no-path = 請指定檔名和路徑。 .wblock-err-current-drawing = 無法寫入目前圖面檔案。 .wblock-help-info = WBLOCK 將物件、圖塊或整個圖面寫入新的圖面檔案。 + .blank-drawing = 空白圖面 + .default-template-new-drawings = 新圖面的預設樣板 + .fillet-trim-mode-trim = FILLET Enter Trim mode option [Trim/No trim] : + .fillet-trim-mode-no-trim = FILLET Enter Trim mode option [Trim/No trim] : + .linetype-files = 線型檔案 + .load-linetypes = 載入線型 + .requires-two-positive-values = 需要兩個正值 (x,y),或一個同時用於兩者的值。 + .snap-marker-color = 鎖點標記顏色 + .linetypes-loaded = 已載入 __ocs_fmt_0__ 個線型。 + .geometric-center-snap = 幾何中心 + .no-trim-option = 不修剪 + .chspace-model-tab = CHSPACE: 在「模型」頁籤中不允許使用此指令。 + .chspace-no-viewport = CHSPACE: 沒有可用於變更空間的平面視圖視埠。 + .chspace-to-paper = CHSPACE: 已將 __ocs_fmt_0__ 個物件移到圖紙空間。 + .chspace-to-model = CHSPACE: 已將 __ocs_fmt_0__ 個物件移到模型空間。 errors = .save-warning = 儲存警告 .unable-to-save-drawing = 無法儲存圖形 @@ -5485,7 +5500,7 @@ styles = .isavebak-set-to-1 = ISAVEBAK 設定為 1 .keep-with-dim-line = 保持暗線 .layers = 圖層 - .linetype-list-set = LINETYPE [清單 / 設定]: + .linetype-list-set = LINETYPE [清單 / 設定 / 載入]: .linetype-set-linetype-name-bylayer-byblock = LINETYPE SET 線型名稱(ByLayer / ByBlock / …): .linetype-name-is-not-loaded-use-linetype-list = LINETYPE:“__ocs_fmt_0__”未載入。使用 LINETYPE LIST 查看可用線型。 .ltscale-new-global-line-type-scale = LTSCALE 新的全局線型尺度 : diff --git a/src/app/command_driver/utilities.rs b/src/app/command_driver/utilities.rs index 3cd862ae0..05759388d 100644 --- a/src/app/command_driver/utilities.rs +++ b/src/app/command_driver/utilities.rs @@ -677,6 +677,7 @@ impl OpenCADStudio { None }; let is_escape = matches!(&input, StepInput::Escape); + let keyed = matches!(&input, StepInput::Text(_) | StepInput::Enter); let result: Option = { let Some(cmd) = self.tabs[i].active_cmd.as_mut() else { return (Task::none(), false); @@ -703,7 +704,15 @@ impl OpenCADStudio { self.tabs[i].pending_pause_tokens = None; } match result { - Some(r) => (self.apply_cmd_result(r), true), + Some(r) => { + let task = self.apply_cmd_result(r); + // A keyword can move the step without the cursor moving + // (PEDIT Next vertex): redraw its preview now (#1628). + if keyed && self.active_tab == i { + self.refresh_active_cmd_preview(i); + } + (task, true) + } None => (Task::none(), false), } } diff --git a/src/app/commands/dim.rs b/src/app/commands/dim.rs index 297e89d2a..bb0f570ff 100644 --- a/src/app/commands/dim.rs +++ b/src/app/commands/dim.rs @@ -911,13 +911,29 @@ impl OpenCADStudio { .or_else(|| cmd.strip_prefix("ZOOM S ")) .or_else(|| cmd.strip_prefix("ZS ")) .unwrap_or("1"); - if let Ok(factor) = rest.trim().parse::() { - if factor > 0.0 { - self.tabs[i].scene.remember_current_view(); - self.tabs[i].scene.zoom_camera(1.0 / factor); + // nXP scales the active viewport against paper space, nX (or + // a bare n) the current view (#1465). n may be an expression + // such as 1/50. + let rest = rest.trim().to_ascii_uppercase(); + let (number, xp) = match rest.strip_suffix("XP") { + Some(n) => (n, true), + None => (rest.strip_suffix('X').unwrap_or(&rest), false), + }; + let factor = crate::app::expr_eval::eval_number(number.trim()) + .filter(|f| f.is_finite() && *f > 0.0); + match factor { + Some(factor) if xp && self.tabs[i].scene.set_viewport_scale_factor(factor) => { + self.tabs[i].dirty = true; self.command_line .push_output(crate::tf!("Zoom Scale ×{factor:.3}").as_ref()); } + Some(factor) => { + self.tabs[i].scene.remember_current_view(); + self.tabs[i].scene.zoom_camera(1.0 / factor as f32); + self.command_line + .push_output(crate::tf!("Zoom Scale ×{factor:.3}").as_ref()); + } + None => {} } } diff --git a/src/app/commands/mod.rs b/src/app/commands/mod.rs index 67f5be6a3..0e1df27d3 100644 --- a/src/app/commands/mod.rs +++ b/src/app/commands/mod.rs @@ -634,6 +634,9 @@ inventory::submit!(crate::command::CommandRegistration { "FITSPLINE", // System variables (typeable directly). "MIRRTEXT", + "PERSPECTIVE", + "GRIDMODE", + "GRIDUNIT", "ZOOMWHEEL", "ZOOMFACTOR", "SHORTCUTMENU", @@ -844,6 +847,7 @@ inventory::submit!(crate::command::CommandRegistration { "MLSTYLE", "MS", "MSPACE", + "CHSPACE", "NAVVCUBE", "NEW", "OBJIMPORT", diff --git a/src/app/commands/styleprops.rs b/src/app/commands/styleprops.rs index c669354e8..3d8b08aeb 100644 --- a/src/app/commands/styleprops.rs +++ b/src/app/commands/styleprops.rs @@ -3,6 +3,65 @@ use super::*; impl OpenCADStudio { pub(super) fn dispatch_styleprops(&mut self, cmd: &str, i: usize) -> Option> { match cmd { + // View / grid variables held by the app rather than the header, + // typed bare or through SETVAR (#1524, #1618). + cmd if matches!( + cmd.strip_prefix("SETVAR ").unwrap_or(cmd).split_whitespace().next(), + Some("PERSPECTIVE" | "GRIDMODE" | "GRIDUNIT") + ) => + { + let mut words = cmd.strip_prefix("SETVAR ").unwrap_or(cmd).splitn(2, ' '); + let name = words.next().unwrap_or_default().to_string(); + let value = words.next().map(str::trim).filter(|v| !v.is_empty()); + let current = match name.as_str() { + "PERSPECTIVE" => { + let persp = self.tabs[i].scene.camera.borrow().projection + == crate::scene::Projection::Perspective; + (persp as i32).to_string() + } + "GRIDMODE" => (self.show_grid as i32).to_string(), + _ => format!("{},{}", self.grid_spacing_x, self.grid_spacing_y), + }; + let Some(value) = value else { + self.command_line + .push_output(&crate::tf!("Enter new value for {name} <{current}>:")); + self.pending_setvar = Some(name); + return Some(Task::none()); + }; + match (name.as_str(), value) { + ("PERSPECTIVE", "0" | "1") => { + return Some(Task::done(Message::SetProjection(value == "0"))); + } + ("GRIDMODE", "0" | "1") => { + if (value == "1") != self.show_grid { + return Some(Task::done(Message::ToggleGrid)); + } + } + ("GRIDUNIT", _) => { + let mut parts = value.split(',').map(|p| p.trim().parse::()); + let x = parts.next().and_then(Result::ok).filter(|v| *v > 0.0); + let y = match parts.next() { + Some(p) => p.ok().filter(|v| *v > 0.0), + None => x, + }; + let (Some(x), Some(y)) = (x, y) else { + self.command_line.push_error( + crate::t!("Requires two positive values (x,y) or one for both.") + .as_ref(), + ); + return Some(Task::none()); + }; + self.grid_spacing_x = x; + self.grid_spacing_y = y; + self.sync_vport_display(i); + self.persist_settings_if_changed(); + } + _ => self + .command_line + .push_error(crate::t!("Requires 0 or 1").as_ref()), + } + return Some(Task::none()); + } "CETRANSPARENCY" => return self.dispatch_styleprops("SETVAR CETRANSPARENCY", i), cmd if cmd.starts_with("CETRANSPARENCY ") => { return self.dispatch_styleprops(&format!("SETVAR {cmd}"), i); @@ -84,9 +143,10 @@ impl OpenCADStudio { use crate::command::KeywordCommand; let c = KeywordCommand::new( "LINETYPE", - "LINETYPE [List / Set]:", + "LINETYPE [List / Set / Load]:", vec![ ("List", "LIST", None), + ("Load", "LOAD", None), ("Set", "SET", Some("LINETYPE SET linetype name (ByLayer / ByBlock / …):")), ], ); @@ -98,6 +158,13 @@ impl OpenCADStudio { let parts: Vec<&str> = raw_rest.split_whitespace().collect(); let sub = parts.get(0).map(|s| s.to_uppercase()).unwrap_or_default(); match sub.as_str() { + // Load the definitions of a `.lin` file (#1588). + "LOAD" | "L" => { + return Some(Task::perform( + crate::io::pick_linetype_file(), + Message::LinetypeLoaded, + )); + } "" | "LIST" | "?" => { let ltypes: Vec = self.tabs[i] .scene @@ -3247,7 +3314,7 @@ impl OpenCADStudio { ); } } - cmd if cmd == "DDPTYPE" => { + cmd if cmd == "DDPTYPE" || cmd == "PTYPE" => { // The dialog shows the magnitude; the sign (relative/absolute) // is driven by the radio buttons. A positive PDSIZE is absolute; // zero or negative is relative. diff --git a/src/app/commands/view.rs b/src/app/commands/view.rs index 5a7e3a81d..3c6bc31dd 100644 --- a/src/app/commands/view.rs +++ b/src/app/commands/view.rs @@ -548,6 +548,102 @@ impl OpenCADStudio { return Some(Task::done(Message::PspaceCommand)); } + // ── CHSPACE — move objects between model and paper space through + // a viewport, keeping their apparent size and place (#1428). + // Inside a viewport the selection goes to the sheet; on the + // sheet it goes into the model through the viewport under it. + "CHSPACE" => { + let handles: Vec = self.tabs[i] + .scene + .selected_entities() + .into_iter() + .map(|(h, _)| h) + .collect(); + if self.tabs[i].scene.current_layout == "Model" { + self.command_line + .push_error(crate::t!("CHSPACE: Command not allowed in the Model tab.").as_ref()); + return Some(Task::none()); + } + if handles.is_empty() { + use crate::modules::draw::select::SelectObjectsCommand; + let cmd = SelectObjectsCommand::new("CHSPACE"); + self.command_line.push_info(&cmd.prompt()); + self.tabs[i].active_cmd = Some(Box::new(cmd)); + return Some(Task::none()); + } + let scene = &self.tabs[i].scene; + let to_paper = scene.active_viewport.is_some(); + let frame = match scene.active_viewport { + Some(vp) => scene.viewport_frame(vp), + None => { + // The viewport under the middle of the selection. + let (mut lo, mut hi) = (glam::DVec2::MAX, glam::DVec2::MIN); + for wire in scene.wire_models_for(&handles) { + let [x0, y0, x1, y1] = wire.aabb.map(f64::from); + if [x0, y0, x1, y1].iter().all(|v| v.is_finite()) { + lo = lo.min(glam::DVec2::new(x0, y0)); + hi = hi.max(glam::DVec2::new(x1, y1)); + } + } + let mid = (lo + hi) * 0.5; + mid.is_finite() + .then(|| scene.viewport_frames_at_paper_point(mid.extend(0.0))) + .and_then(|frames| frames.into_iter().next()) + } + }; + let Some(frame) = frame else { + self.command_line.push_error( + crate::t!("CHSPACE: No plan-view viewport to change space through.").as_ref(), + ); + return Some(Task::none()); + }; + use crate::command::EntityTransform as T; + let z = glam::DVec3::Z; + let steps = if to_paper { + [ + T::Translate((-frame.model_target).extend(0.0)), + T::Rotate { center: glam::DVec3::ZERO, axis: z, angle_rad: frame.twist }, + T::Scale { center: glam::DVec3::ZERO, factor: frame.scale }, + T::Translate(frame.paper_center.extend(0.0)), + ] + } else { + [ + T::Translate((-frame.paper_center).extend(0.0)), + T::Scale { center: glam::DVec3::ZERO, factor: 1.0 / frame.scale }, + T::Rotate { center: glam::DVec3::ZERO, axis: z, angle_rad: -frame.twist }, + T::Translate(frame.model_target.extend(0.0)), + ] + }; + self.push_undo_snapshot(i, "CHSPACE"); + let scene = &mut self.tabs[i].scene; + let entities: Vec = handles + .iter() + .filter_map(|h| scene.document.get_entity(*h).cloned()) + .collect(); + scene.deselect_all(); + scene.erase_entities(&handles); + // The clones land in the other space: the sheet with no active + // viewport, the model with one. + let active = scene.active_viewport; + scene.active_viewport = if to_paper { None } else { Some(frame.viewport) }; + let moved: Vec = + entities.into_iter().map(|e| scene.add_entity_clone(e)).collect(); + scene.active_viewport = active; + for step in &steps { + scene.transform_entities(&moved, step); + } + self.tabs[i].dirty = true; + let count = moved.len(); + self.command_line.push_output( + if to_paper { + crate::tf!("CHSPACE: {count} object(s) changed to paper space.") + } else { + crate::tf!("CHSPACE: {count} object(s) changed to model space.") + } + .as_ref(), + ); + } + // ── Viewport arrangement shortcuts ──────────────────────────── // Tile the model viewports into preset splits. Each delegates to the // matching VPORTS configuration so the Model/paper handling stays in diff --git a/src/app/layers.rs b/src/app/layers.rs index 07e7d8b66..4bedc8968 100644 --- a/src/app/layers.rs +++ b/src/app/layers.rs @@ -88,6 +88,11 @@ impl OpenCADStudio { return; } let active = self.tabs[i].active_layer.clone(); + let vp_column = self.tabs[i].scene.active_viewport.and_then(|vp| { + (self.tabs[i].scene.current_layout != "Model") + .then(|| self.tabs[i].layers.vp_cols.iter().position(|c| c.handle == vp)) + .flatten() + }); let infos: Vec = self.tabs[i] .layers .layers @@ -100,6 +105,8 @@ impl OpenCADStudio { visible: l.visible, frozen: l.frozen, locked: l.locked, + vp_frozen: vp_column + .map(|column| (column, l.vp_frozen.get(column).copied().unwrap_or(false))), }) .collect(); let names: Vec = infos.iter().map(|l| l.name.clone()).collect(); diff --git a/src/app/mod.rs b/src/app/mod.rs index 06f190af8..258eef13d 100644 --- a/src/app/mod.rs +++ b/src/app/mod.rs @@ -590,6 +590,10 @@ pub(super) struct OpenCADStudio { crosshair_color: Option<[u8; 3]>, /// Editable Options buffer for the crosshair colour. crosshair_color_input: String, + /// Explicit object-snap marker colour; `None` keeps the automatic one. + snap_marker_color: Option<[u8; 3]>, + /// Editable Options buffer for the snap marker colour. + snap_marker_color_input: String, /// Defer the ISOLINES mesh rebuild until the slider is released. isolines_awaiting_regen: bool, /// Edit buffer for the SNAPANG field on the Options Drafting page. Kept @@ -1024,6 +1028,8 @@ pub(super) struct OpenCADStudio { check_missing_fonts: bool, /// Custom font source base URL; empty selects the community repository. font_source_url: String, + /// Template a new drawing starts from (QNEW); empty = blank. + qnew_template: String, /// Editable copy of `font_source_url` shown in the missing-fonts prompt. font_source_input: String, donation_prompt_version: String, @@ -2289,6 +2295,10 @@ pub enum Message { LineweightDisplayScaleChanged(i32), /// Edit the optional crosshair RGB value; blank restores automatic contrast. CrosshairColorChanged(String), + SnapMarkerColorChanged(String), + /// Options → Files: the default template for new drawings. + QnewTemplateChanged(String), + QnewTemplateBrowse, /// Set the default type/version used when first saving a new drawing. DefaultSaveFormatChanged(String), /// Select one of Iced's built-in themes or the editable Custom theme. @@ -3678,6 +3688,8 @@ pub enum Message { // ── Plot Style Table ───────────────────────────────────────────────── /// Open file dialog to load a CTB/STB plot style table. PlotStyleLoad, + /// LINETYPE Load: the picked `.lin` file's text, `None` when cancelled. + LinetypeLoaded(Option), /// Callback when the user picks (or cancels) a CTB/STB file. /// The Load… picker finished: a table, nothing (cancelled), or why the /// file could not be read. @@ -4151,6 +4163,8 @@ impl OpenCADStudio { cursor_type: settings::CursorType::Crosshair, crosshair_color: None, crosshair_color_input: String::new(), + snap_marker_color: None, + snap_marker_color_input: String::new(), isolines_awaiting_regen: false, snap_angle_input: "0".to_string(), lineweight_display_scale: 100, @@ -4338,6 +4352,7 @@ impl OpenCADStudio { default_assoc_prompted: false, check_missing_fonts: true, font_source_url: String::new(), + qnew_template: String::new(), font_source_input: String::new(), donation_prompt_version: String::new(), read_only: false, diff --git a/src/app/settings.rs b/src/app/settings.rs index 3f597c41e..6c80f27d8 100644 --- a/src/app/settings.rs +++ b/src/app/settings.rs @@ -251,6 +251,7 @@ const SNAP_ORDER: &[SnapType] = &[ SnapType::Endpoint, SnapType::Midpoint, SnapType::Center, + SnapType::GeometricCenter, SnapType::Node, SnapType::Quadrant, SnapType::Intersection, @@ -276,6 +277,7 @@ fn snap_bit(s: SnapType) -> Option { SnapType::Midpoint => 2, SnapType::Center => 4, SnapType::Node => 8, + SnapType::GeometricCenter => 1024, SnapType::Quadrant => 16, SnapType::Intersection => 32, SnapType::Insertion => 64, @@ -430,6 +432,8 @@ pub struct UserSettings { pub cursor_type: CursorType, /// Explicit crosshair RGB. `None` keeps automatic background contrast. pub crosshair_color: Option<[u8; 3]>, + /// Explicit object-snap marker RGB. `None` keeps the automatic colour. + pub snap_marker_color: Option<[u8; 3]>, /// Model-space lineweight preview scale as a percentage. pub lineweight_display_scale: i32, /// Isometric drafting changes the grid and crosshair to the active axis @@ -459,6 +463,9 @@ pub struct UserSettings { /// so an intranet folder or a private GitHub raw folder both work. #[serde(default)] pub font_source_url: String, + /// Drawing new tabs start from (QNEW template); empty = a blank drawing. + #[serde(default)] + pub qnew_template: String, /// App version whose donation prompt has been displayed. pub donation_prompt_version: String, /// The graphics verdict (`GpuStatus::identity()`) whose warning popup the @@ -819,6 +826,7 @@ impl Default for UserSettings { ncopy_bind: false, cursor_type: CursorType::Crosshair, crosshair_color: None, + snap_marker_color: None, lineweight_display_scale: 100, isometric_drafting: false, iso_plane: IsoPlane::Left, @@ -827,6 +835,7 @@ impl Default for UserSettings { default_assoc_prompted: false, check_missing_fonts: true, font_source_url: String::new(), + qnew_template: String::new(), donation_prompt_version: String::new(), gpu_warning_silenced: String::new(), disabled_plugins: Vec::new(), diff --git a/src/app/update/command.rs b/src/app/update/command.rs index dfd004c20..ebbc09abb 100644 --- a/src/app/update/command.rs +++ b/src/app/update/command.rs @@ -1311,6 +1311,7 @@ pub(super) fn on_tab_close(&mut self, idx: usize) -> Task { self.tabs[i] .layers .sync_with_viewports(&doc_layers, vp_info); + self.sync_ribbon_layers(); // Per-viewport layer visibility changes the resident // assembly, not any entity's tessellated geometry. self.tabs[i].scene.bump_geometry_no_blocks(); diff --git a/src/app/update/file.rs b/src/app/update/file.rs index 3f1f17652..935bb7dff 100644 --- a/src/app/update/file.rs +++ b/src/app/update/file.rs @@ -681,6 +681,7 @@ impl OpenCADStudio { ncopy_bind: self.ncopy_bind, cursor_type: self.cursor_type, crosshair_color: self.crosshair_color, + snap_marker_color: self.snap_marker_color, lineweight_display_scale: self.lineweight_display_scale, isometric_drafting: self.isometric_drafting, iso_plane: self.iso_plane, @@ -689,6 +690,7 @@ impl OpenCADStudio { default_assoc_prompted: self.default_assoc_prompted, check_missing_fonts: self.check_missing_fonts, font_source_url: self.font_source_url.clone(), + qnew_template: self.qnew_template.clone(), donation_prompt_version: self.donation_prompt_version.clone(), gpu_warning_silenced: self.gpu_warning_silenced.clone(), disabled_plugins: { @@ -791,6 +793,11 @@ impl OpenCADStudio { .crosshair_color .map(crate::app::config::rgb_to_hex) .unwrap_or_default(); + self.snap_marker_color = s.snap_marker_color; + self.snap_marker_color_input = s + .snap_marker_color + .map(crate::app::config::rgb_to_hex) + .unwrap_or_default(); self.lineweight_display_scale = s.lineweight_display_scale.clamp(25, 200); self.isometric_drafting = s.isometric_drafting; self.iso_plane = s.iso_plane; @@ -808,6 +815,7 @@ impl OpenCADStudio { self.default_assoc_prompted = s.default_assoc_prompted; self.check_missing_fonts = s.check_missing_fonts; self.font_source_url = s.font_source_url.clone(); + self.qnew_template = s.qnew_template.clone(); self.font_source_input = s.font_source_url.clone(); self.donation_prompt_version = s.donation_prompt_version.clone(); self.gpu_warning_silenced = s.gpu_warning_silenced.clone(); @@ -3603,10 +3611,13 @@ impl OpenCADStudio { pub(super) fn apply_plot_page_settings(&mut self) { let i = self.active_tab; let layout_name = self.tabs[i].scene.current_layout.clone(); + let ps = self.plot_settings_from_dialog(self.dialog_base_settings()); if layout_name == "Model" { + // Model keeps its page setup too (#1620); it has no sheet limits. + self.tabs[i].scene.set_layout_plot_settings(&layout_name, &ps); + self.tabs[i].dirty = true; return; } - let ps = self.plot_settings_from_dialog(self.dialog_base_settings()); let (w, h) = plot_dialog_sheet_mm(&self.plot_dialog); let plot_area = self.plot_dialog.area.clone(); let center = ps.flags.plot_centered; @@ -5360,7 +5371,7 @@ impl OpenCADStudio { // into it when "Save changes to layout" is on, so the page setup // remembers the last plot the way other applications expect. The // runtime paper/scale choices drive this one plot operation either way. - if !preview && d.save_to_layout && d.paper_space { + if !preview && d.save_to_layout { self.apply_dialog_to_layout(); } else { self.sync_dialog_plot_runtime(); diff --git a/src/app/update/mod.rs b/src/app/update/mod.rs index 2479c1878..b598c2c87 100644 --- a/src/app/update/mod.rs +++ b/src/app/update/mod.rs @@ -1877,6 +1877,20 @@ impl OpenCADStudio { self.sync_ribbon_from_selection(); // A fresh drawing starts with grid/snap off (its tile defaults). self.adopt_view_display(self.active_tab); + // QNEW: start from the default template when one is set (#1113). + let template = self.qnew_template.trim().to_string(); + if !template.is_empty() { + match self.apply_template(&template) { + Ok(_) => { + self.tabs[idx].dirty = false; + self.refresh_layer_panel(); + self.sync_ribbon_styles(); + } + Err(error) => self.command_line.push_error( + error["error"].as_str().unwrap_or("template"), + ), + } + } Task::none() } @@ -5809,6 +5823,10 @@ impl OpenCADStudio { if id == crate::modules::insert::insert_block::GALLERY_ID { self.refresh_block_palette(); } + // The per-viewport freeze column follows the active viewport. + if id == crate::ui::ribbon::widgets::LAYER_COMBO_ID { + self.refresh_layer_panel(); + } self.ribbon.toggle_dropdown(&id); } Task::none() @@ -8005,6 +8023,39 @@ impl OpenCADStudio { Task::none() } + Message::QnewTemplateChanged(path) => { + self.qnew_template = path; + self.persist_settings_if_changed(); + Task::none() + } + Message::QnewTemplateBrowse => Task::perform( + async { + crate::sys::file_dialog() + .add_filter( + crate::t!("Drawing Template (*.dwt)").as_ref(), + &["dwt", "DWT", "dwg", "DWG", "dxf", "DXF"], + ) + .pick_file() + .await + .map(|handle| crate::sys::handle_path(&handle).display().to_string()) + }, + |path| match path { + Some(path) => Message::QnewTemplateChanged(path), + None => Message::Noop, + }, + ), + Message::SnapMarkerColorChanged(value) => { + self.snap_marker_color_input = value.clone(); + if value.trim().is_empty() { + self.snap_marker_color = None; + self.persist_settings_if_changed(); + } else if let Some(rgb) = crate::app::config::parse_hex(&value) { + self.snap_marker_color = Some(rgb); + self.persist_settings_if_changed(); + } + Task::none() + } + Message::DefaultSaveFormatChanged(format) => { self.default_save_format = crate::io::canonical_save_format(&format).to_string(); self.persist_settings_if_changed(); @@ -9835,6 +9886,22 @@ impl OpenCADStudio { } // ── Plot Style Table ────────────────────────────────────────────── + Message::LinetypeLoaded(Some(source)) => { + let i = self.active_tab; + self.push_undo_snapshot(i, "LINETYPE"); + let added = crate::io::linetypes::populate_document_from_source( + &mut self.tabs[i].scene.document, + &source, + ); + if added > 0 { + self.tabs[i].dirty = true; + self.tabs[i].scene.bump_geometry(); + } + self.command_line + .push_output(crate::tf!("{added} linetype(s) loaded.").as_ref()); + Task::none() + } + Message::LinetypeLoaded(None) => Task::none(), Message::PlotStyleLoad => { Task::perform(crate::io::pick_plot_style(), Message::PlotStyleLoaded) } diff --git a/src/app/view/mod.rs b/src/app/view/mod.rs index 6b3c81c15..5c275089d 100644 --- a/src/app/view/mod.rs +++ b/src/app/view/mod.rs @@ -925,6 +925,13 @@ bg={bg_ms:.1}ms n={view_count}" iso_plane: self.iso_plane, snap_angle_deg: self.snap_angle_deg, pick_pending: self.pending_pick_label().is_some(), + // An infinite-line preview runs along the arms; drop them + // so the preview stays visible (#1437). + hide_arms: tab + .active_cmd + .as_ref() + .is_some_and(|c| matches!(c.name(), "XLINE" | "RAY")), + snap_color: self.snap_marker_color, }, crate::ui::overlay::SelectionVisualOptions { area: self.model_space.selection_area, diff --git a/src/app/view/modal.rs b/src/app/view/modal.rs index 87b0fb129..4426936dd 100644 --- a/src/app/view/modal.rs +++ b/src/app/view/modal.rs @@ -383,11 +383,14 @@ impl OpenCADStudio { crate::ui::window::options::Folders::default() } }, + &self.qnew_template, self.double_click_block_refedit, self.double_click_block_attedit, self.cursor_type, self.crosshair_color, &self.crosshair_color_input, + self.snap_marker_color, + &self.snap_marker_color_input, self.lineweight_display_scale, &self.model_space, &self.model_bg_input, diff --git a/src/entities/common.rs b/src/entities/common.rs index 4f5e799d7..6816f66ac 100644 --- a/src/entities/common.rs +++ b/src/entities/common.rs @@ -951,25 +951,34 @@ pub(crate) fn tapered_band_points( let seg_count = if is_closed { n } else { n.saturating_sub(1) }; let mut pts: Vec<[f64; 3]> = Vec::with_capacity(seg_count + 1); let mut widths: Vec = Vec::with_capacity(seg_count + 1); - let mut push = |x: f64, y: f64, w: f32| { - let (wx, wy, wz) = to_wcs(x, y); - pts.push([wx, wy, wz]); + let mut push = |xy: Option<(f64, f64)>, w: f32| { + // `None` is the NaN separator that breaks the band. + pts.push(xy.map_or([f64::NAN; 3], |(x, y)| to_wcs(x, y).into())); widths.push(w); }; + let mut prev_end_width: Option = None; for i in 0..seg_count { let (p0, bulge, sw0, ew0) = verts[i]; let (p1, _, _, _) = verts[(i + 1) % n]; - if i == 0 { - push(p0[0], p0[1], sw0 as f32); + match prev_end_width { + None => push(Some((p0[0], p0[1])), sw0 as f32), + // A segment starting at another width than the last one ended + // steps there: break the band and restart it (#1628). + Some(prev) if (prev - sw0).abs() > 1e-9 => { + push(None, 0.0); + push(Some((p0[0], p0[1])), sw0 as f32); + } + Some(_) => {} } + prev_end_width = Some(ew0); if bulge.abs() < 1e-9 { - push(p1[0], p1[1], ew0 as f32); + push(Some((p1[0], p1[1])), ew0 as f32); } else if let Some(arc) = BulgeArc::from_bulge(p0, p1, bulge) { let samples = arc.tessellate_angle(kernel::tessellation::DEFAULT_ANGLE); let segments = samples.len().saturating_sub(1).max(1); for (index, s) in samples.into_iter().enumerate().skip(1) { let t = index as f64 / segments as f64; - push(s[0], s[1], (sw0 + (ew0 - sw0) * t) as f32); + push(Some((s[0], s[1])), (sw0 + (ew0 - sw0) * t) as f32); } } } diff --git a/src/entities/dimension.rs b/src/entities/dimension.rs index 28f1428d8..71104f254 100644 --- a/src/entities/dimension.rs +++ b/src/entities/dimension.rs @@ -842,12 +842,22 @@ fn large_radial_rotation(d: &DimensionLargeRadial) -> f64 { } fn apply_transform(dim: &mut Dimension, t: &EntityTransform) { + // A zero text point is the "placed automatically" sentinel, not a + // location: moving it to wherever the origin lands left the text there. + let auto_text = { + let base = dim.base(); + let p = base.text_middle_point; + !base.text_user_positioned && p.x == 0.0 && p.y == 0.0 && p.z == 0.0 + }; crate::scene::view::transform::apply_standard_entity_transform(dim, t, |entity, p1, p2| { codec::Entity::apply_transform( entity, &crate::scene::view::transform::reflection_about_xy_line(p1, p2), ); }); + if auto_text { + reset_automatic_text_position(dim.base_mut()); + } } impl PropertyEditable for Dimension { diff --git a/src/io/font_repo.rs b/src/io/font_repo.rs index 4b681499e..23e66cc74 100644 --- a/src/io/font_repo.rs +++ b/src/io/font_repo.rs @@ -82,7 +82,10 @@ pub fn missing_shx_fonts(doc: &CadDocument) -> Vec { // The primary font and the big font (Asian double-byte glyphs) are // both plain .shx files the style depends on. for file in [style.font_file.trim(), style.big_font_file.trim()] { - if file.is_empty() || !file.to_ascii_lowercase().ends_with(".shx") { + if file.is_empty() + || !file.to_ascii_lowercase().ends_with(".shx") + || crate::io::linetypes::is_bundled_shape_file(file) + { continue; } let resolved = crate::io::resolve_image_file(file, base).is_some() diff --git a/src/io/linetypes.rs b/src/io/linetypes.rs index 4d286baf6..b5018bce0 100644 --- a/src/io/linetypes.rs +++ b/src/io/linetypes.rs @@ -345,6 +345,13 @@ fn bundled_shape_name(file: &str, number: i16) -> Option<&'static str> { .map(|(_, name, _)| *name) } +/// Whether `file` is a shape file the bundled LFF substitutes stand in for, +/// so its absence on disk never needs reporting. +pub fn is_bundled_shape_file(file: &str) -> bool { + let stem = shape_file_stem(file); + BUNDLED_SHAPES.iter().any(|(f, _, _)| *f == stem) +} + /// The drawing's shape-file STYLE for `file`, added when missing (an empty /// name, as shape files are stored). fn shape_file_style(doc: &mut CadDocument, file: &str) -> codec::Handle { diff --git a/src/io/mod.rs b/src/io/mod.rs index ffaeba33b..6ff41327a 100644 --- a/src/io/mod.rs +++ b/src/io/mod.rs @@ -170,9 +170,10 @@ impl From<&str> for OpenLoadError { pub async fn pick_open_path() -> Option<(PathBuf, u64)> { let handle = crate::sys::file_dialog() .set_title(crate::t!("Open CAD file").as_ref()) - .add_filter(crate::t!("CAD Files").as_ref(), &["dwg", "dxf", "bak", "sv$", "DWG", "DXF", "BAK"]) + .add_filter(crate::t!("CAD Files").as_ref(), &["dwg", "dxf", "dwt", "bak", "sv$", "DWG", "DXF", "DWT", "BAK"]) .add_filter(crate::t!("DWG Files").as_ref(), &["dwg", "DWG"]) .add_filter(crate::t!("DXF Files").as_ref(), &["dxf", "DXF"]) + .add_filter(crate::t!("Drawing Template (*.dwt)").as_ref(), &["dwt", "DWT"]) .add_filter(crate::t!("Backup / Autosave").as_ref(), &["bak", "sv$", "BAK"]) .add_filter(crate::t!("All Files").as_ref(), &["*"]) .pick_file() @@ -1761,6 +1762,16 @@ mod save_failure_tests { /// picked from outside the plot styles folder is copied into it (unless a /// file of that name is already there), so the dialog lists it from now on /// and page setups naming it resolve after a restart. +/// Pick a `.lin` linetype definition file and return its text (#1588). +pub async fn pick_linetype_file() -> Option { + let handle = crate::sys::file_dialog() + .set_title(crate::t!("Load Linetypes").as_ref()) + .add_filter(crate::t!("Linetype Files").as_ref(), &["lin", "LIN"]) + .pick_file() + .await?; + Some(String::from_utf8_lossy(&handle.read().await).into_owned()) +} + pub async fn pick_plot_style() -> Result, String> { let dialog = crate::sys::file_dialog() .set_title(crate::t!("Load Plot Style Table").as_ref()) diff --git a/src/io/pdf_export.rs b/src/io/pdf_export.rs index c3ad5ddd6..89d11e2cd 100644 --- a/src/io/pdf_export.rs +++ b/src/io/pdf_export.rs @@ -771,7 +771,9 @@ fn append_pdf_page( } // Near-white and near-yellow (viewport active border) → dark grey for print // (only when no CTB override was applied). - if !color_overridden { + // `aci == 0` is an explicit true colour and plots as drawn, like the + // hatch pass (#1621); only a pure white there is an unresolved colour 7. + if !color_overridden && (wire.aci != 0 || [r, g, b] == [1.0; 3]) { // An authored white (not colour 7) plots as drawn, like on screen. let is_light = r > 0.80 && g > 0.80 diff --git a/src/locale_catalog.rs b/src/locale_catalog.rs index 8cf8d0f9d..617a655ec 100644 --- a/src/locale_catalog.rs +++ b/src/locale_catalog.rs @@ -1768,7 +1768,7 @@ pub(super) fn message_attribute(source: &str) -> Option<(&'static str, &'static "LINE Specify first point:" => Some(("draw", "line-specify-first-point")), "LINE Specify next point [Close/Undo]:" => Some(("draw", "line-specify-next-point-close-undo")), "LINE Specify next point [Undo]:" => Some(("draw", "line-specify-next-point-undo")), - "LINETYPE [List / Set]:" => Some(("styles", "linetype-list-set")), + "LINETYPE [List / Set / Load]:" => Some(("styles", "linetype-list-set")), "LINETYPE SET linetype name (ByLayer / ByBlock / …):" => Some(("styles", "linetype-set-linetype-name-bylayer-byblock")), "LINETYPE: \"{name}\" is not loaded. Use LINETYPE LIST to see available linetypes." => Some(("styles", "linetype-name-is-not-loaded-use-linetype-list")), "LIST: no entities selected. Select entities first." => Some(("inquiry", "list-no-entities-selected-select-entities-firs")), @@ -4873,11 +4873,26 @@ pub(super) fn message_attribute(source: &str) -> Option<(&'static str, &'static "Are you sure you want to reset? You will lose all your current shortcuts!" => Some(("common", "are-you-sure-you-want-to-reset-you-will-lose-all-your-current-shortcuts")), "Yes, reset" => Some(("common", "yes-reset")), "Reset to default" => Some(("common", "reset-to-default")), - "Apply && Exit" => Some(("common", "apply-exit")), + "Apply & Exit" => Some(("common", "apply-exit")), "Shortcut already used for command: {} → {}" => Some(("common", "shortcut-already-used-for-command")), "Unknown command: {}" => Some(("command", "unknown-command-cmd")), "Unsaved changes will be discarded." => Some(("common", "unsaved-changes-will-be-discarded")), - "Discard && close" => Some(("common", "discard-close")), + "Discard & close" => Some(("common", "discard-close")), + "CHSPACE: Command not allowed in the Model tab." => Some(("common", "chspace-model-tab")), + "CHSPACE: No plan-view viewport to change space through." => Some(("common", "chspace-no-viewport")), + "CHSPACE: {count} object(s) changed to paper space." => Some(("common", "chspace-to-paper")), + "CHSPACE: {count} object(s) changed to model space." => Some(("common", "chspace-to-model")), + "Blank drawing" => Some(("common", "blank-drawing")), + "Default template for new drawings" => Some(("common", "default-template-new-drawings")), + "FILLET Enter Trim mode option [Trim/No trim] :" => Some(("common", "fillet-trim-mode-trim")), + "FILLET Enter Trim mode option [Trim/No trim] :" => Some(("common", "fillet-trim-mode-no-trim")), + "Linetype Files" => Some(("common", "linetype-files")), + "Load Linetypes" => Some(("common", "load-linetypes")), + "Requires two positive values (x,y) or one for both." => Some(("common", "requires-two-positive-values")), + "Snap marker color" => Some(("common", "snap-marker-color")), + "{added} linetype(s) loaded." => Some(("common", "linetypes-loaded")), + "Geometric Center" => Some(("common", "geometric-center-snap")), + "No trim" => Some(("common", "no-trim-option")), "Keep editing" => Some(("common", "keep-editing")), "MCP control is off" => Some(("common", "mcp-control-is-off")), "MCP is handling a request" => Some(("common", "mcp-is-handling-a-request")), diff --git a/src/modules/draw/defaults.rs b/src/modules/draw/defaults.rs index 5666c0013..cdcc2e759 100644 --- a/src/modules/draw/defaults.rs +++ b/src/modules/draw/defaults.rs @@ -32,6 +32,7 @@ thread_local! { static HELIX_HEIGHT: Cell = Cell::new(1.0); static HELIX_TURNS: Cell = Cell::new(3.0); static HELIX_COUNTER_CLOCKWISE: Cell = Cell::new(true); + static FILLET_TRIM: Cell = Cell::new(true); // TRIMMODE } macro_rules! accessors { @@ -85,6 +86,13 @@ pub fn get_helix_counter_clockwise() -> bool { pub fn set_helix_counter_clockwise(value: bool) { HELIX_COUNTER_CLOCKWISE.with(|current| current.set(value)); } +pub fn get_fillet_trim() -> bool { + FILLET_TRIM.with(|value| value.get()) +} + +pub fn set_fillet_trim(value: bool) { + FILLET_TRIM.with(|current| current.set(value)); +} accessors!( get_donut_inner_diameter, set_donut_inner_diameter, diff --git a/src/modules/draw/draw/point.rs b/src/modules/draw/draw/point.rs index 24d4fc5e6..7ae8b1bb1 100644 --- a/src/modules/draw/draw/point.rs +++ b/src/modules/draw/draw/point.rs @@ -81,5 +81,5 @@ impl CadCommand for PointCommand { inventory::submit!(crate::command::CommandRegistration { names: &["POINT", "MULTIPOINT"] }); // PointCommand // Point display style system variables + dialog. inventory::submit!(crate::command::CommandRegistration { - names: &["PDMODE", "PDSIZE", "DDPTYPE"] + names: &["PDMODE", "PDSIZE", "DDPTYPE", "PTYPE"] }); diff --git a/src/modules/draw/modify/fillet.rs b/src/modules/draw/modify/fillet.rs index 6c301383a..c36606f7e 100644 --- a/src/modules/draw/modify/fillet.rs +++ b/src/modules/draw/modify/fillet.rs @@ -9,7 +9,7 @@ // Pick two lines (line-only; arcs are not chamferable). // Finds intersection, backs off dist1 along line 1 and dist2 along line 2. -use codec::entities::{Arc as ArcEnt, Line as LineEnt, LwPolyline}; +use codec::entities::{Arc as ArcEnt, Line as LineEnt, LwPolyline, LwVertex}; // Shared plane geometry, from opencadkernel via the local adapters. use super::geom; @@ -1354,6 +1354,9 @@ enum FilletStep { // Radius may be typed directly or measured by picking two points. WaitingForRadius, + /// Trim / No trim: whether the picked objects are cut back to the arc. + TrimMode, + RadiusSecondPoint { first: DVec3, }, @@ -1398,12 +1401,16 @@ impl FilletCommand { /// Switch to the radius sub-step, remembering the first pick (if any) so /// it can be restored afterwards. fn enter_radius_substep(&mut self) { + self.enter_substep(FilletStep::WaitingForRadius); + } + + fn enter_substep(&mut self, step: FilletStep) { self.resume_second = if let FilletStep::Second { h1, e1, click1 } = &self.step { Some((*h1, e1.clone(), *click1)) } else { None }; - self.step = FilletStep::WaitingForRadius; + self.step = step; } /// Leave the radius sub-step, resuming the second pick when a first object @@ -1415,7 +1422,12 @@ impl FilletCommand { }; } - fn continue_after_fillet(&mut self, replacements: Vec<(Handle, Vec)>) -> CmdResult { + fn continue_after_fillet(&mut self, mut replacements: Vec<(Handle, Vec)>) -> CmdResult { + for entity in replacements.iter_mut().flat_map(|(_, entities)| entities.iter_mut()) { + if let EntityType::LwPolyline(poly) = entity { + drop_zero_length_segments(poly); + } + } self.all_entities.retain(|entity| { !replacements .iter() @@ -1488,6 +1500,12 @@ impl CadCommand for FilletCommand { FilletStep::RadiusSecondPoint { .. } => { crate::t!("FILLET Specify second point for radius:").into_owned() } + FilletStep::TrimMode => if defaults::get_fillet_trim() { + crate::t!("FILLET Enter Trim mode option [Trim/No trim] :") + } else { + crate::t!("FILLET Enter Trim mode option [Trim/No trim] :") + } + .into_owned(), FilletStep::Second { .. } => { crate::tf!( "FILLET Select second object (Line/Arc/LwPolyline) [R={:.4}]:", @@ -1509,10 +1527,14 @@ impl CadCommand for FilletCommand { } opts.push(CmdOption::new("Polyline", "P")); opts.push(CmdOption::new("Radius", "R")); + opts.push(CmdOption::new("Trim", "T")); opts } FilletStep::Second { .. } => { - vec![CmdOption::new("Radius", "R")] + vec![CmdOption::new("Radius", "R"), CmdOption::new("Trim", "T")] + } + FilletStep::TrimMode => { + vec![CmdOption::new("Trim", "T"), CmdOption::new("No trim", "N")] } FilletStep::Polyline | FilletStep::WaitingForRadius @@ -1521,7 +1543,7 @@ impl CadCommand for FilletCommand { } fn wants_text_input(&self) -> bool { - matches!(self.step, FilletStep::WaitingForRadius) + matches!(self.step, FilletStep::WaitingForRadius | FilletStep::TrimMode) } fn dyn_field(&self) -> crate::command::DynField { @@ -1552,6 +1574,16 @@ impl CadCommand for FilletCommand { // Invalid — stay and re-prompt Some(CmdResult::NeedPoint) } + FilletStep::TrimMode => { + match text.trim().to_uppercase().as_str() { + "" => {} + "T" | "TRIM" => defaults::set_fillet_trim(true), + "N" | "NO TRIM" | "NOTRIM" => defaults::set_fillet_trim(false), + _ => return Some(CmdResult::NeedPoint), + } + self.resume_after_radius(); + Some(CmdResult::NeedPoint) + } FilletStep::First | FilletStep::Second { .. } => { let t = text.trim(); let upper = t.to_uppercase(); @@ -1562,6 +1594,10 @@ impl CadCommand for FilletCommand { if upper == "U" || upper == "UNDO" { return self.undo_last(); } + if upper == "T" || upper == "TRIM" { + self.enter_substep(FilletStep::TrimMode); + return Some(CmdResult::NeedPoint); + } // "R" alone → enter sub-step to collect radius if upper == "R" { self.enter_radius_substep(); @@ -1594,6 +1630,7 @@ impl CadCommand for FilletCommand { self.step, FilletStep::WaitingForRadius | FilletStep::RadiusSecondPoint { .. } + | FilletStep::TrimMode ) } @@ -1619,7 +1656,8 @@ impl CadCommand for FilletCommand { match &self.step { FilletStep::WaitingForRadius - | FilletStep::RadiusSecondPoint { .. } => { + | FilletStep::RadiusSecondPoint { .. } + | FilletStep::TrimMode => { return CmdResult::NeedPoint; } @@ -1689,6 +1727,17 @@ impl CadCommand for FilletCommand { if let Some(e2) = e2 { match compute_fillet_entities(&e1, click1, &e2, click, self.radius) { + // No trim: the objects stay as they are; only the arc + // is added (#1558). + Some((_, _, maybe_arc)) if !defaults::get_fillet_trim() => { + let Some(arc) = maybe_arc else { + return CmdResult::NeedPoint; + }; + self.step = FilletStep::First; + self.resume_second = None; + self.made += 1; + CmdResult::CommitEntity(arc) + } Some((new_e1, new_e2, maybe_arc)) => { let mut first_replacements = vec![new_e1]; if let Some(arc) = maybe_arc { @@ -1719,7 +1768,8 @@ impl CadCommand for FilletCommand { match &self.step { FilletStep::WaitingForRadius - | FilletStep::RadiusSecondPoint { .. } => vec![], + | FilletStep::RadiusSecondPoint { .. } + | FilletStep::TrimMode => vec![], FilletStep::Polyline => { let preview = self .entity_index @@ -1780,20 +1830,21 @@ impl CadCommand for FilletCommand { if let Some((new_e1, new_e2, maybe_arc)) = compute_fillet_entities(&e1, click1, &e2, click, self.radius) { - let mut out = vec![ - WireModel::solid( + let mut out = Vec::new(); + if defaults::get_fillet_trim() { + out.push(WireModel::solid( "fillet_e1".into(), entity_pts(&new_e1), WireModel::CYAN, false, - ), - WireModel::solid( + )); + out.push(WireModel::solid( "fillet_e2".into(), entity_pts(&new_e2), WireModel::CYAN, false, - ), - ]; + )); + } if let Some(arc) = maybe_arc { out.push(WireModel::solid( "fillet_arc".into(), @@ -1856,6 +1907,29 @@ impl CadCommand for FilletCommand { } } +/// A radius-0 fillet over an existing arc leaves its two ends on one point; +/// the zero-length segment between them blocked every later fillet of that +/// corner (#1422). The later vertex carries the next segment, so it survives. +fn drop_zero_length_segments(poly: &mut LwPolyline) { + let same = |a: &LwVertex, b: &LwVertex| { + (a.location.x - b.location.x).abs() < 1e-9 && (a.location.y - b.location.y).abs() < 1e-9 + }; + let mut i = 0; + while i + 1 < poly.vertices.len() && poly.vertices.len() > 2 { + if same(&poly.vertices[i], &poly.vertices[i + 1]) { + poly.vertices.remove(i); + } else { + i += 1; + } + } + if poly.is_closed && poly.vertices.len() > 2 { + let n = poly.vertices.len(); + if same(&poly.vertices[n - 1], &poly.vertices[0]) { + poly.vertices.pop(); + } + } +} + // ══════════════════════════════════════════════════════════════════════════ // ChamferCommand // ══════════════════════════════════════════════════════════════════════════ @@ -2448,16 +2522,16 @@ mod tests { 1.0, vec![line(0.0, 0.0, 10.0, 0.0, 1), line(0.0, 0.0, 0.0, 10.0, 2)], ); - assert_eq!(keywords(&command), ["P", "R"]); + assert_eq!(keywords(&command), ["P", "R", "T"]); assert!(command.on_text_input("U").is_none(), "nothing to undo yet"); command.on_entity_pick(Handle::new(1), DVec3::new(5.0, 0.0, 0.0)); assert!(matches!( command.on_entity_pick(Handle::new(2), DVec3::new(0.0, 5.0, 0.0)), CmdResult::ReplaceManyContinue(_) )); - assert_eq!(keywords(&command), ["U", "P", "R"]); + assert_eq!(keywords(&command), ["U", "P", "R", "T"]); assert!(matches!(command.on_text_input("U"), Some(CmdResult::UndoDocument))); - assert_eq!(keywords(&command), ["P", "R"]); + assert_eq!(keywords(&command), ["P", "R", "T"]); // The host hands the restored document back; the cache follows it. let mut doc = codec::CadDocument::new(); let _ = doc.add_entity(line(0.0, 0.0, 20.0, 0.0, 7)); diff --git a/src/modules/draw/modify/pedit.rs b/src/modules/draw/modify/pedit.rs index f99ddb147..c20435e37 100644 --- a/src/modules/draw/modify/pedit.rs +++ b/src/modules/draw/modify/pedit.rs @@ -9,6 +9,7 @@ use glam::DVec3; use rustc_hash::FxHashMap as HashMap; use crate::command::{CadCommand, CmdResult}; +use crate::scene::model::wire_model::WireModel; use crate::t; const TAU: f64 = std::f64::consts::TAU; @@ -188,6 +189,24 @@ impl PeditCommand { } } + /// WCS location of a vertex of the polyline being edited. + fn vertex_world(&self, index: usize) -> Option { + let entity = self.target.and_then(|handle| self.entities.get(&handle.value()))?; + let (ocs, n) = match entity { + EntityType::LwPolyline(p) => { + let v = p.vertices.get(index)?; + ((v.location.x, v.location.y, p.elevation), &p.normal) + } + EntityType::Polyline2D(p) => { + let v = p.vertices.get(index)?; + ((v.location.x, v.location.y, p.elevation), &p.normal) + } + _ => return None, + }; + let (x, y, z) = crate::scene::view::transform::ocs_point_to_wcs(ocs, (n.x, n.y, n.z)); + Some(DVec3::new(x, y, z)) + } + fn vertex_count(&self) -> usize { self.target.and_then(|handle| self.entities.get(&handle.value())).map_or(0, |entity| match entity { EntityType::LwPolyline(polyline) => polyline.vertices.len(), @@ -237,6 +256,40 @@ impl CadCommand for PeditCommand { "PEDIT" } + /// Edit-vertex steps mark the current vertex with a screen-sized X (#1628). + fn on_mouse_move(&mut self, _pt: DVec3) -> Option { + let index = match self.mode { + Mode::PolyVertex(i) + | Mode::PolyTangent(i) + | Mode::PolyMove(i) + | Mode::PolyInsert(i) + | Mode::PolyWidthStart(i, _) + | Mode::PolyWidthEnd(i, _) => i, + Mode::PolyRange { end, .. } => end, + _ => return None, + }; + let origin = self.vertex_world(index)?; + let at = |dx: f64, dy: f64| { + let p = origin + DVec3::new(dx, dy, 0.0); + [p.x, p.y, p.z] + }; + let h = 0.5; + let mut wire = WireModel::solid_f64( + "pedit_vertex_marker".into(), + vec![at(-h, -h), at(h, h), [f64::NAN; 3], at(-h, h), at(h, -h)], + WireModel::CYAN, + false, + ); + wire.point_marker = Some(crate::scene::model::wire_model::PointMarker { + origin, + normal: DVec3::Z, + axis_x: DVec3::X, + axis_y: DVec3::Y, + viewport_percent: 3.0, + }); + Some(wire) + } + fn prompt(&self) -> String { match &self.mode { Mode::PickTarget => { diff --git a/src/modules/model/mod.rs b/src/modules/model/mod.rs index f28ad352b..32095b62a 100644 --- a/src/modules/model/mod.rs +++ b/src/modules/model/mod.rs @@ -31,6 +31,7 @@ const SUBTRACT_ICON: &[u8] = include_bytes!("../../../assets/icons/model/subtrac const INTERSECT_ICON: &[u8] = include_bytes!("../../../assets/icons/model/intersect.svg"); const FILLET_ICON: &[u8] = include_bytes!("../../../assets/icons/model/fillet.svg"); const CHAMFER_ICON: &[u8] = include_bytes!("../../../assets/icons/model/chamfer.svg"); +const SHELL_ICON: &[u8] = include_bytes!("../../../assets/icons/model/shell.svg"); /// Helper to declare a ribbon tool that fires a named command. fn tool(id: &'static str, label: &'static str, icon: &'static [u8]) -> ToolDef { @@ -93,7 +94,7 @@ impl CadModule for ModelModule { tools: vec![ RibbonItem::LargeTool(tool("FILLETEDGE", "Fillet Edge", FILLET_ICON)), RibbonItem::LargeTool(tool("CHAMFEREDGE", "Chamfer", CHAMFER_ICON)), - RibbonItem::LargeTool(tool("SHELL", "Shell", PRESSPULL_ICON)), + RibbonItem::LargeTool(tool("SHELL", "Shell", SHELL_ICON)), ], }, ] diff --git a/src/modules/view/zoom_window.rs b/src/modules/view/zoom_window.rs index a7f6be8c9..490a35a96 100644 --- a/src/modules/view/zoom_window.rs +++ b/src/modules/view/zoom_window.rs @@ -97,6 +97,13 @@ impl CadCommand for ZoomWindowCommand { self.scale_prompt = true; return Some(CmdResult::NeedPoint); } + // A scale factor straight at the first prompt: n, nX or nXP. + upper if upper.ends_with('X') + || upper.ends_with("XP") + || crate::app::expr_eval::eval_number(&upper).is_some() => + { + return Some(CmdResult::Dispatch(format!("ZOOM SCALE {text}"))); + } _ => return None, }; Some(CmdResult::Dispatch(command.to_string())) diff --git a/src/scene/convert/tessellate.rs b/src/scene/convert/tessellate.rs index 917e71a3d..3d2bd2953 100644 --- a/src/scene/convert/tessellate.rs +++ b/src/scene/convert/tessellate.rs @@ -180,6 +180,21 @@ pub(crate) fn text_contrast_background( /// Split each absolute f64 source point into double-single (high, low) f32 /// buffers in one pass — the relative-to-eye residual the GPU/CPU reconstruct /// to f64 precision at UTM-scale coordinates. +/// Area centroid of a closed polyline, for the Geometric Center snap. +fn geometric_center(entity: &EntityType) -> Option { + let closed = match entity { + EntityType::LwPolyline(p) => p.is_closed && p.vertices.len() >= 3, + EntityType::Polyline2D(p) => p.is_closed() && p.vertices.len() >= 3, + _ => false, + }; + if !closed { + return None; + } + let planar = crate::entities::curve::entity_curve(entity)?; + let center = planar.curve.enclosed_centroid()?; + Some(glam::DVec3::from_array(planar.plane.point_at(center))) +} + pub(crate) fn points_to_ds( src: impl IntoIterator, ) -> (Vec<[f32; 3]>, Vec<[f32; 3]>) { @@ -1102,7 +1117,10 @@ pub fn tessellate( )), _ => convert(entity, document), }); - if let Some(te) = te { + if let Some(mut te) = te { + if let Some(center) = geometric_center(entity) { + te.snap_pts.push((center, SnapHint::GeometricCenter)); + } match te.object { // ── Text / MText: pre-tessellated glyph strokes ─────────────── // diff --git a/src/scene/dimension_assoc_chain.rs b/src/scene/dimension_assoc_chain.rs index 487f4b0eb..f81794c08 100644 --- a/src/scene/dimension_assoc_chain.rs +++ b/src/scene/dimension_assoc_chain.rs @@ -54,7 +54,7 @@ pub(crate) fn osnap_type_for(snap: crate::snap::SnapType) -> u8 { S::Nearest | S::Extension => osnap::NEAR, S::ApparentIntersection => osnap::APPARENT_INT, S::Parallel => osnap::PARA, - S::Grid | S::ObjectPick | S::Vertex | S::EdgeMidpoint | S::FaceCenter | S::Knot | S::FacePerpendicular | S::NearestFace => { + S::GeometricCenter | S::Grid | S::ObjectPick | S::Vertex | S::EdgeMidpoint | S::FaceCenter | S::Knot | S::FacePerpendicular | S::NearestFace => { osnap::NONE } } diff --git a/src/scene/mod.rs b/src/scene/mod.rs index 27806038d..7e64c2440 100644 --- a/src/scene/mod.rs +++ b/src/scene/mod.rs @@ -6300,6 +6300,39 @@ impl Scene { Some(scale_handle) } + /// Scale the active viewport to `factor` paper units per model unit + /// (ZOOM nXP). A named scale of that factor is linked as its annotation + /// scale; otherwise the viewport keeps a custom scale. + pub fn set_viewport_scale_factor(&mut self, factor: f64) -> bool { + let Some(viewport) = self.explicit_viewport_handle() else { + return false; + }; + let unit_factor = self.annotation_scale_unit_factor(); + let named = self.document.objects.values().find_map(|object| match object { + ObjectType::Scale(scale) + if !scale.is_temporary + && (scale.factor() * unit_factor - factor).abs() <= 1.0e-9 * factor.max(1.0) => + { + Some(scale.name.clone()) + } + _ => None, + }); + if let Some(name) = named { + return self.set_viewport_scale_named_for(viewport, &name).is_some(); + } + match self.document.get_entity_mut(viewport) { + Some(EntityType::Viewport(vp)) if !vp.status.locked && factor > 1.0e-9 => { + vp.custom_scale = factor; + vp.view_height = vp.height / factor; + } + _ => return false, + } + self.notify_viewport_changed(viewport); + self.resident_wire_sets.borrow_mut().clear(); + self.bump_geometry(); + true + } + pub fn viewport_annotation_scale_synced(&self) -> Option { let viewport = self.explicit_viewport_handle()?; let EntityType::Viewport(vp) = self.document.get_entity(viewport)? else { diff --git a/src/scene/model/wire_model.rs b/src/scene/model/wire_model.rs index 233ee854d..4f6c50028 100644 --- a/src/scene/model/wire_model.rs +++ b/src/scene/model/wire_model.rs @@ -4,6 +4,8 @@ pub enum SnapHint { /// Geometric center of a circle, arc, or ellipse. Center, + /// Area centroid of a closed polyline. + GeometricCenter, /// Point entity location. Node, /// 0 / 90 / 180 / 270 ° point on a circle/arc (within arc span). diff --git a/src/snap.rs b/src/snap.rs index 94e10234a..551960bfc 100644 --- a/src/snap.rs +++ b/src/snap.rs @@ -30,6 +30,8 @@ pub enum SnapType { Endpoint, Midpoint, Center, + /// Area centroid of a closed polyline (#1369). + GeometricCenter, Node, Quadrant, Intersection, @@ -83,6 +85,7 @@ pub const ALL_SNAP_MODES: &[(SnapType, &str, &str)] = &[ (SnapType::Endpoint, "◻", "Endpoint"), (SnapType::Midpoint, "△", "Midpoint"), (SnapType::Center, "◯", "Center"), + (SnapType::GeometricCenter, "⊙", "Geometric Center"), (SnapType::Node, "◆", "Node"), (SnapType::Quadrant, "◇", "Quadrant"), (SnapType::Intersection, "✕", "Intersection"), @@ -1440,6 +1443,10 @@ impl Snapper { // else stays on the 2D master + mode set. let (snap_type, on) = match hint { SnapHint::Center => (SnapType::Center, self.is_on(SnapType::Center)), + SnapHint::GeometricCenter => ( + SnapType::GeometricCenter, + self.is_on(SnapType::GeometricCenter), + ), SnapHint::Node => (SnapType::Node, self.is_on(SnapType::Node)), SnapHint::Quadrant => (SnapType::Quadrant, self.is_on(SnapType::Quadrant)), SnapHint::Insertion => (SnapType::Insertion, self.is_on(SnapType::Insertion)), @@ -2602,6 +2609,7 @@ pub(crate) fn snap_tier(t: SnapType) -> u8 { | SnapType::ApparentIntersection | SnapType::Midpoint | SnapType::Center + | SnapType::GeometricCenter | SnapType::Node | SnapType::Quadrant | SnapType::Insertion @@ -2663,7 +2671,7 @@ pub(crate) fn snap_priority(t: SnapType) -> u8 { SnapType::Intersection => 1, SnapType::ApparentIntersection => 2, SnapType::Midpoint => 3, - SnapType::Center => 4, + SnapType::Center | SnapType::GeometricCenter => 4, SnapType::Node => 5, SnapType::Quadrant => 6, SnapType::Insertion => 7, diff --git a/src/ui/color_select.rs b/src/ui/color_select.rs index ea37b7ccb..2bcc13d60 100644 --- a/src/ui/color_select.rs +++ b/src/ui/color_select.rs @@ -37,8 +37,11 @@ pub fn color_to_aci_string(c: AcadColor) -> String { /// Convert an Iced colour chosen by `iced_aw::ColorPicker` into a DWG true /// colour. ACI-only destinations map it to their closest indexed colour later. pub fn iced_to_acad_color(color: Color) -> AcadColor { - let [r, g, b, _] = color.into_rgba8(); - AcadColor::Rgb { r, g, b } + // Truncate the way the picker labels its channels, so the stored colour + // is the one the dialog showed; rounding drifted dragged values by one + // (#1621). + let byte = |c: f32| (c * 255.0) as u8; + AcadColor::Rgb { r: byte(color.r), g: byte(color.g), b: byte(color.b) } } /// Return the closest CAD Color Index for an RGB colour. diff --git a/src/ui/command_line.rs b/src/ui/command_line.rs index 20634352f..8edf25dd5 100644 --- a/src/ui/command_line.rs +++ b/src/ui/command_line.rs @@ -985,8 +985,13 @@ impl CommandLine { .on_press(Message::CommandHistoryClear) .style(header_btn_style) .padding([2, 6]); + // Collapses the console back to the single input line (#1094). + let collapse_btn = button(crate::ui::icons::themed_arrow_down(11.0)) + .on_press(Message::CommandHistoryToggle) + .style(header_btn_style) + .padding([2, 6]); let header = container( - row![Space::new().width(Length::Fill), copy_btn, clear_btn] + row![Space::new().width(Length::Fill), copy_btn, clear_btn, collapse_btn] .spacing(6) .align_y(iced::Center), ) diff --git a/src/ui/icons.rs b/src/ui/icons.rs index 0e0e2ae80..694b98a07 100644 --- a/src/ui/icons.rs +++ b/src/ui/icons.rs @@ -32,6 +32,8 @@ static REDO: &[u8] = include_bytes!("../../assets/icons/ui/redo.svg"); static OSNAP_ENDPOINT: &[u8] = include_bytes!("../../assets/icons/osnap/endpoint.svg"); static OSNAP_MIDPOINT: &[u8] = include_bytes!("../../assets/icons/osnap/midpoint.svg"); static OSNAP_CENTER: &[u8] = include_bytes!("../../assets/icons/osnap/center.svg"); +static OSNAP_GEOMETRIC_CENTER: &[u8] = + include_bytes!("../../assets/icons/osnap/geometric_center.svg"); static OSNAP_NODE: &[u8] = include_bytes!("../../assets/icons/osnap/node.svg"); static OSNAP_QUADRANT: &[u8] = include_bytes!("../../assets/icons/osnap/quadrant.svg"); static OSNAP_INTERSECTION: &[u8] = include_bytes!("../../assets/icons/osnap/intersection.svg"); @@ -51,6 +53,8 @@ static LAY_ON: &[u8] = include_bytes!("../../assets/icons/layers/layon.svg"); static LAY_OFF: &[u8] = include_bytes!("../../assets/icons/layers/layoff.svg"); static LAY_FRZ: &[u8] = include_bytes!("../../assets/icons/layers/layfrz.svg"); static LAY_THW: &[u8] = include_bytes!("../../assets/icons/layers/laythw.svg"); +static LAY_VP_FRZ: &[u8] = include_bytes!("../../assets/icons/layers/layvpfrz.svg"); +static LAY_VP_THW: &[u8] = include_bytes!("../../assets/icons/layers/layvpthw.svg"); static LAY_LCK: &[u8] = include_bytes!("../../assets/icons/layers/laylck.svg"); static LAY_ULK: &[u8] = include_bytes!("../../assets/icons/layers/layulk.svg"); @@ -630,6 +634,7 @@ pub fn osnap(snap: crate::snap::SnapType) -> &'static [u8] { S::Endpoint => OSNAP_ENDPOINT, S::Midpoint => OSNAP_MIDPOINT, S::Center => OSNAP_CENTER, + S::GeometricCenter => OSNAP_GEOMETRIC_CENTER, S::Node => OSNAP_NODE, S::Quadrant => OSNAP_QUADRANT, S::Intersection => OSNAP_INTERSECTION, @@ -694,6 +699,15 @@ pub fn layer_freeze(frozen: bool) -> &'static [u8] { } } +/// Freeze-in-current-viewport icon bytes (frozen / thawed). +pub fn layer_vp_freeze(frozen: bool) -> &'static [u8] { + if frozen { + LAY_VP_FRZ + } else { + LAY_VP_THW + } +} + /// Layer lock icon bytes (locked / unlocked). pub fn layer_lock(locked: bool) -> &'static [u8] { if locked { diff --git a/src/ui/modal.rs b/src/ui/modal.rs index 3ab2d1c9d..de01a7311 100644 --- a/src/ui/modal.rs +++ b/src/ui/modal.rs @@ -275,7 +275,7 @@ pub fn discard_guard<'a>( iced::widget::text(crate::t!("Unsaved changes will be discarded.")).size(13.5), Space::new().height(12), row![ - button(iced::widget::text(crate::t!("Discard && close")).size(12)) + button(iced::widget::text(crate::t!("Discard & close")).size(12)) .on_press(on_discard) .padding([5, 14]) .style(button::danger), diff --git a/src/ui/node_graph.rs b/src/ui/node_graph.rs index 2b58d739f..7b01a9750 100644 --- a/src/ui/node_graph.rs +++ b/src/ui/node_graph.rs @@ -518,12 +518,20 @@ impl Graph { _ => None, }; let zoom = self.zoom(); + // Lay the nodes out relative to the visible area's top-left rather + // than the graph origin: a `pin` clips to its own bounds, so nodes + // left of / above the origin vanished once panned or zoomed out (#1592). + let shift = Vector::new(self.pan.x / zoom, self.pan.y / zoom); let mut nodes = stack![]; for (node, node_sections) in self.engine.nodes.iter().zip(sections) { let Some(ui) = self.ui.get(&node.id) else { continue; }; - nodes = nodes.push(pin(self.node_view(node, node_sections)).x(ui.pos.x).y(ui.pos.y)); + nodes = nodes.push( + pin(self.node_view(node, node_sections)) + .x(ui.pos.x + shift.x) + .y(ui.pos.y + shift.y), + ); } let mut layers = stack![ canvas(Wires { wires, pending, width: 2.0 * zoom.max(0.5) }) @@ -531,7 +539,6 @@ impl Graph { .height(Length::Fill), Zoomed { content: nodes.width(Length::Fill).height(Length::Fill).into(), - pan: self.pan, zoom, }, ]; @@ -955,17 +962,16 @@ impl canvas::Program for Wires { } /// Lays its content out in graph space and draws and hit-tests it through -/// `pan` and `zoom`, so every widget inside scales as one picture. +/// `zoom`, so every widget inside scales as one picture. struct Zoomed<'a> { content: Element<'a, Message>, - pan: Vector, zoom: f32, } impl Zoomed<'_> { /// Content coordinates → screen, for a layer whose top-left is `origin`. fn transformation(&self, origin: Point) -> Transformation { - Transformation::translate(origin.x + self.pan.x, origin.y + self.pan.y) + Transformation::translate(origin.x, origin.y) * Transformation::scale(self.zoom) * Transformation::translate(-origin.x, -origin.y) } diff --git a/src/ui/overlay.rs b/src/ui/overlay.rs index adde3f65d..4e1450962 100644 --- a/src/ui/overlay.rs +++ b/src/ui/overlay.rs @@ -239,6 +239,12 @@ pub struct CrosshairOptions { /// the crosshair arms disappear and a blue pickbox remains — the /// screen-level "the client wants YOU to pick" signal. pub pick_pending: bool, + /// Draw only the pickbox: the active command previews a line that the + /// full-length arms would cover. + pub hide_arms: bool, + /// Explicit object-snap marker colour; `None` picks one that reads on + /// the canvas. + pub snap_color: Option<[u8; 3]>, } /// Rendering style for the viewport grid. @@ -1517,6 +1523,10 @@ impl canvas::Program for SelectionCanvas { if let Some((sp, snap_type)) = self.snap { let (r, g, b) = if snap_type == SnapType::ObjectPick { (0.95_f32, 0.50, 0.08) // orange object-snap marker + } else if let Some([r, g, b]) = self.crosshair.snap_color { + (r as f32 / 255.0, g as f32 / 255.0, b as f32 / 255.0) + } else if crate::ui::style::common::canvas_is_light(self.crosshair_bg) { + (0.80, 0.45, 0.0) // dark amber: yellow vanishes on a light canvas } else { (1.0, 0.9, 0.1) // classic yellow OSNAP }; @@ -1569,6 +1579,25 @@ impl canvas::Program for SelectionCanvas { let path = canvas::Path::circle(sp, r); frame.stroke(&path, stroke); } + SnapType::GeometricCenter => { + // Pentagon around a centre dot: the area centroid. + let r = 6.5_f32; + let path = canvas::Path::new(|b| { + for k in 0..5 { + let a = -std::f32::consts::FRAC_PI_2 + + k as f32 * std::f32::consts::TAU / 5.0; + let p = Point::new(sp.x + r * a.cos(), sp.y + r * a.sin()); + if k == 0 { + b.move_to(p); + } else { + b.line_to(p); + } + } + b.close(); + }); + frame.stroke(&path, stroke.clone()); + frame.fill(&canvas::Path::circle(sp, 1.6), marker); + } SnapType::Node => { // Circle with an inscribed X. let r = 5.5_f32; @@ -1915,7 +1944,7 @@ impl canvas::Program for SelectionCanvas { // The arms stay only for the normal (non-pending) cursor: a // waiting pick shows the box alone, regardless of the UCS // rotation, so the square is unmistakable. - if !pick_pending { + if !pick_pending && !self.crosshair.hide_arms { for angle in base_angles { let rad = (angle + self.crosshair.snap_angle_deg as f64).to_radians(); let dir = Point::new(rad.cos() as f32, -rad.sin() as f32); @@ -3363,6 +3392,37 @@ impl DynInputCanvas { (Self::box_content(b).len() as f32 * DYN_CHAR_W) + DYN_PAD * 2.0 } + /// Slide a box centred at `center` straight away from the crosshair until + /// it neither covers the aim point nor a box already placed (#1546). + fn clear_of(center: Point, w: f32, cursor: Point, placed: &[iced::Rectangle]) -> Point { + const KEEP: f32 = 18.0; + let hits = |c: Point| { + let r = iced::Rectangle { + x: c.x - w * 0.5, + y: c.y - DYN_BOX_H * 0.5, + width: w, + height: DYN_BOX_H, + }; + r.expand(KEEP).contains(cursor) || placed.iter().any(|p| p.intersects(&r)) + }; + let (mut dx, mut dy) = (center.x - cursor.x, center.y - cursor.y); + let len = (dx * dx + dy * dy).sqrt(); + if len < 1.0 { + (dx, dy) = (0.0, 1.0); + } else { + (dx, dy) = (dx / len, dy / len); + } + let mut c = center; + for _ in 0..60 { + if !hits(c) { + break; + } + c.x += dx * 4.0; + c.y += dy * 4.0; + } + c + } + /// Draw a value box centred at `center`, clamped inside `bounds`. fn draw_box( frame: &mut canvas::Frame, @@ -3647,6 +3707,7 @@ impl DynInputCanvas { } // ── Box placement by role ── + let mut placed: Vec = Vec::with_capacity(self.boxes.len()); for b in &self.boxes { let center = b.center.unwrap_or_else(|| match b.role { DynRole::Angle => self.label_screen.unwrap_or_else(|| { @@ -3690,6 +3751,14 @@ impl DynInputCanvas { y: base.y + dy * len * 0.5 + ny * 16.0, }, }); + let w = Self::box_width(b); + let center = Self::clear_of(center, w, cursor, &placed); + placed.push(iced::Rectangle { + x: center.x - w * 0.5, + y: center.y - DYN_BOX_H * 0.5, + width: w, + height: DYN_BOX_H, + }); Self::draw_box(frame, b, center, bounds, theme); } // Keep the tracking hint near the crosshair in guided layouts. diff --git a/src/ui/popup/context_menu.rs b/src/ui/popup/context_menu.rs index 8cb05f85a..9a0192cce 100644 --- a/src/ui/popup/context_menu.rs +++ b/src/ui/popup/context_menu.rs @@ -521,6 +521,7 @@ fn snap_override_items() -> Vec { snap("Apparent Intersection", SnapType::ApparentIntersection), snap("Extension", SnapType::Extension), snap("Center", SnapType::Center), + snap("Geometric Center", SnapType::GeometricCenter), snap("Quadrant", SnapType::Quadrant), snap("Tangent", SnapType::Tangent), snap("Perpendicular", SnapType::Perpendicular), diff --git a/src/ui/ribbon/mod.rs b/src/ui/ribbon/mod.rs index 3f7bcbcb4..e7c69f818 100644 --- a/src/ui/ribbon/mod.rs +++ b/src/ui/ribbon/mod.rs @@ -128,6 +128,8 @@ pub struct LayerInfo { pub visible: bool, pub frozen: bool, pub locked: bool, + /// Layout with an active viewport: (its Layer Manager column, frozen there). + pub vp_frozen: Option<(usize, bool)>, } /// Full-screen backdrop for an open ribbon dropdown: closes the dropdown when @@ -1042,6 +1044,13 @@ impl Ribbon { crate::ui::icons::layer_lock(ll), Message::LayerToggleLock(info.name.clone()), ); + // Freeze in the current viewport only (#1466). + let vp_freeze = info.vp_frozen.map(|(column, frozen)| { + icon_btn( + crate::ui::icons::layer_vp_freeze(frozen), + Message::LayerToggleVpFreeze(info.name.clone(), column), + ) + }); let checkmark: Element<'_, Message> = crate::ui::icons::themed_check_cell(is_active); let label = @@ -1067,7 +1076,10 @@ impl Ribbon { .padding([4, 4]); container( - row![checkmark, vis, freeze, lock, select] + row![checkmark, vis, freeze] + .push(vp_freeze) + .push(lock) + .push(select) .spacing(5) .align_y(iced::Center), ) diff --git a/src/ui/ribbon/widgets.rs b/src/ui/ribbon/widgets.rs index 5db4b88b6..1ecc75f13 100644 --- a/src/ui/ribbon/widgets.rs +++ b/src/ui/ribbon/widgets.rs @@ -310,7 +310,7 @@ pub(super) const TOP_HIST_GAP: f32 = 4.0; pub(crate) const UNDO_HISTORY_ID: &str = "UNDO_HISTORY"; pub(crate) const REDO_HISTORY_ID: &str = "REDO_HISTORY"; -pub(super) const LAYER_COMBO_ID: &str = "LAYER_COMBO"; +pub(crate) const LAYER_COMBO_ID: &str = "LAYER_COMBO"; /// Dropdown id for the tab-bar panel-density selector. pub(super) const COLLAPSE_MODE_ID: &str = "COLLAPSE_MODE"; pub(super) const PROP_COLOR_ID: &str = "PROP_COLOR"; diff --git a/src/ui/style/dimstyle.rs b/src/ui/style/dimstyle.rs index c1d01ccab..93aef476c 100644 --- a/src/ui/style/dimstyle.rs +++ b/src/ui/style/dimstyle.rs @@ -384,6 +384,29 @@ pub fn view_window<'a>( }; // Linear unit formats shared by DIMLUNIT / DIMALTU. + // Precision reads as the number pattern it produces (#1426). + const OPT_PREC: &[(&str, &str)] = &[ + ("0", "0"), + ("1", "0.0"), + ("2", "0.00"), + ("3", "0.000"), + ("4", "0.0000"), + ("5", "0.00000"), + ("6", "0.000000"), + ("7", "0.0000000"), + ("8", "0.00000000"), + ]; + // A precision dropdown, or the read-only value while the option is off. + let prec_field = move |label: Cow<'static, str>, fld: DsField, val: &'a str, enabled: bool| -> Element<'a, Message> { + if enabled { + enum_field(label, fld, val, OPT_PREC) + } else { + row![lbl(label), mk_field_enabled(fld, val, false)] + .spacing(8) + .align_y(iced::Center) + .into() + } + }; const OPT_LUNIT: &[(&str, &str)] = &[ ("1", "Scientific"), ("2", "Decimal"), @@ -900,8 +923,7 @@ pub fn view_window<'a>( 4 => column![ text(t!("Linear dimensions")).size(11).style(primary_style), enum_field(t!("Unit format"), DsField::Dimlunit, vals.dimlunit, OPT_LUNIT), - row![lbl(t!("Precision")), mk_field(DsField::Dimdec, vals.dimdec)] - .spacing(8).align_y(iced::Center), + prec_field(t!("Precision"), DsField::Dimdec, vals.dimdec, true), if matches!(vals.dimlunit.trim(), "4" | "5") { enum_field( t!("Fraction format"), @@ -946,8 +968,7 @@ pub fn view_window<'a>( vals.dimaunit, &[("0", "Decimal degrees"), ("1", "Degrees, minutes, seconds"), ("2", "Gradians"), ("3", "Radians"), ("4", "Surveyor's units")], ), - row![lbl(t!("Precision")), mk_field(DsField::Dimadec, vals.dimadec)] - .spacing(8).align_y(iced::Center), + prec_field(t!("Precision"), DsField::Dimadec, vals.dimadec, true), enum_field( t!("Zero suppression"), DsField::Dimazin, @@ -970,24 +991,14 @@ pub fn view_window<'a>( ] .spacing(8) .align_y(iced::Center), - row![ - lbl(t!("Precision")), - mk_field_enabled(DsField::Dimaltd, vals.dimaltd, vals.dimalt) - ] - .spacing(8) - .align_y(iced::Center), + prec_field(t!("Precision"), DsField::Dimaltd, vals.dimaltd, vals.dimalt), if vals.dimalt { enum_field(t!("Unit format"), DsField::Dimaltu, vals.dimaltu, OPT_LUNIT) } else { row![lbl(t!("Unit format")), container(text(alternate_unit_name).size(11).style(muted_style)).padding([4, 7]).width(150)] .spacing(8).align_y(iced::Center).into() }, - row![ - lbl(t!("Tolerance precision")), - mk_field_enabled(DsField::Dimalttd, vals.dimalttd, vals.dimalt) - ] - .spacing(8) - .align_y(iced::Center), + prec_field(t!("Tolerance precision"), DsField::Dimalttd, vals.dimalttd, vals.dimalt), row![ lbl(t!("Round off")), mk_field_enabled(DsField::Dimaltrnd, vals.dimaltrnd, vals.dimalt) @@ -1042,12 +1053,7 @@ pub fn view_window<'a>( ] .spacing(8) .align_y(iced::Center), - row![ - lbl(t!("Precision")), - mk_field_enabled(DsField::Dimtdec, vals.dimtdec, vals.dimtol || vals.dimlim) - ] - .spacing(8) - .align_y(iced::Center), + prec_field(t!("Precision"), DsField::Dimtdec, vals.dimtdec, vals.dimtol || vals.dimlim), row![ lbl(t!("Height scale")), mk_field_enabled(DsField::Dimtfac, vals.dimtfac, vals.dimtol || vals.dimlim) diff --git a/src/ui/window/alias_editor.rs b/src/ui/window/alias_editor.rs index 66835b7a7..0c7d48cec 100644 --- a/src/ui/window/alias_editor.rs +++ b/src/ui/window/alias_editor.rs @@ -218,7 +218,7 @@ pub fn view_window<'a>( .on_press(Message::AliasEditorApply) .padding([4, 16]) .style(button::primary); - let apply_exit = button(text(t!("Apply && Exit")).size(12)) + let apply_exit = button(text(t!("Apply & Exit")).size(12)) .on_press(Message::AliasEditorApplyExit) .padding([4, 16]) .style(button::primary); @@ -307,7 +307,7 @@ pub fn view_window<'a>( text(t!("Unsaved changes will be discarded.")).size(14), Space::new().height(10), row![ - button(text(t!("Discard && close")).size(12)) + button(text(t!("Discard & close")).size(12)) .on_press(Message::AliasEditorCloseDiscard) .padding([4, 12]) .style(button::danger), diff --git a/src/ui/window/options.rs b/src/ui/window/options.rs index 69ecf636e..777c255a8 100644 --- a/src/ui/window/options.rs +++ b/src/ui/window/options.rs @@ -209,11 +209,14 @@ pub fn view_window<'a>( zoom_factor_input: &'a str, drawing_prefs: DrawingPrefs, folders: Folders, + qnew_template: &'a str, double_click_block_refedit: bool, double_click_block_attedit: bool, cursor_type: CursorType, crosshair_color: Option<[u8; 3]>, crosshair_color_input: &'a str, + snap_marker_color: Option<[u8; 3]>, + snap_marker_color_input: &'a str, lineweight_display_scale: i32, model_space: &'a crate::app::config::ModelSpaceThemeConfig, model_bg_input: &'a str, @@ -522,6 +525,24 @@ pub fn view_window<'a>( ..Default::default() }); + let snap_rgb = snap_marker_color.unwrap_or([255, 230, 26]); + let snap_swatch = container(Space::new()) + .width(28) + .height(22) + .style(move |theme: &Theme| container::Style { + background: Some(Background::Color(iced::Color::from_rgb8( + snap_rgb[0], + snap_rgb[1], + snap_rgb[2], + ))), + border: Border { + color: theme.palette().background.strong.color, + width: 1.0, + radius: 3.0.into(), + }, + ..Default::default() + }); + let model_bg_rgb = model_space.custom_bg.unwrap_or(crate::app::config::CLASSIC_CAD_DARK_BG); let paper_bg_rgb = model_space.custom_paper_bg.unwrap_or(crate::app::config::DEFAULT_PAPER_BG); @@ -769,6 +790,18 @@ pub fn view_window<'a>( .spacing(10) .align_y(iced::Center), ) + .push(Space::new().height(10)) + .push( + row![ + text(crate::t!("Snap marker color")).size(12).width(140), + snap_swatch, + text_input(crate::t!("#RRGGBB or blank").as_ref(), snap_marker_color_input) + .on_input(Message::SnapMarkerColorChanged) + .width(150), + ] + .spacing(10) + .align_y(iced::Center), + ) .push(Space::new().height(6)) .push( text(crate::t!("Leave the color blank to keep automatic viewport contrast.")) @@ -1666,6 +1699,21 @@ pub fn view_window<'a>( folder_row(crate::t!("Plugins"), folders.plugins.clone()), Space::new().height(12), folder_row(crate::t!("Autosave files"), folders.autosave.clone()), + Space::new().height(16), + text(crate::t!("Default template for new drawings")).size(12), + Space::new().height(4), + row![ + text_input(crate::t!("Blank drawing").as_ref(), qnew_template) + .on_input(Message::QnewTemplateChanged) + .size(11) + .width(Fill), + button(text(crate::t!("Browse...")).size(11)) + .padding([4, 10]) + .style(button::secondary) + .on_press(Message::QnewTemplateBrowse), + ] + .spacing(10) + .align_y(iced::Center), ] .spacing(0) .width(sizing.width); diff --git a/src/ui/window/plot.rs b/src/ui/window/plot.rs index c71dc0efe..f04e41b76 100644 --- a/src/ui/window/plot.rs +++ b/src/ui/window/plot.rs @@ -1553,7 +1553,7 @@ pub fn view_window( paper_space_option(s.paperspace_last, t!("Paper space last"), PlotFlag::PaperspaceLast), check(t!("Merge overlapping lines"), s.merge_lines, PlotFlag::MergeLines), check(t!("Plot stamp"), s.stamp, PlotFlag::Stamp), - paper_space_option(s.save_to_layout, t!("Save changes to layout"), PlotFlag::SaveToLayout), + check(t!("Save changes to layout"), s.save_to_layout, PlotFlag::SaveToLayout), ] .spacing(6) .width(width), diff --git a/src/ui/window/shortcuts.rs b/src/ui/window/shortcuts.rs index 50e3b098d..56666a1a0 100644 --- a/src/ui/window/shortcuts.rs +++ b/src/ui/window/shortcuts.rs @@ -241,7 +241,7 @@ pub fn view_window<'a>( .on_press(Message::ShortcutEditorApply) .padding([4, 16]) .style(button::primary); - let apply_exit = button(text(t!("Apply && Exit")).size(12)) + let apply_exit = button(text(t!("Apply & Exit")).size(12)) .on_press(Message::ShortcutEditorApplyExit) .padding([4, 16]) .style(button::primary); @@ -330,7 +330,7 @@ pub fn view_window<'a>( text(t!("Unsaved changes will be discarded.")).size(14), Space::new().height(10), row![ - button(text(t!("Discard && close")).size(12)) + button(text(t!("Discard & close")).size(12)) .on_press(Message::ShortcutEditorCloseDiscard) .padding([4, 12]) .style(button::danger),