From c8167c54eef8be243f0228f815089e9fee7411da Mon Sep 17 00:00:00 2001 From: ramox81 <184937705+ramox81@users.noreply.github.com> Date: Mon, 12 Oct 2026 10:50:00 +0300 Subject: [PATCH] -VIEW sEttings Background dialog and view backgrounds Color, Gradient and Image at the background prompt open a Background dialog (None / Solid / Gradient / Image, preview, solid colour, two- or three-colour gradient with rotation, image file with position and scale). Apply links the view to a new background object in ACAD_BACKGROUND; restoring a view gives the model viewport a copy of it (or none), and saving a view copies the viewport's background. Co-Authored-By: Claude Opus 5.5 --- locales/ar-SA/opencadstudio.ftl | 32 ++++ locales/bg-BG/opencadstudio.ftl | 32 ++++ locales/cs-CZ/opencadstudio.ftl | 32 ++++ locales/de-DE/opencadstudio.ftl | 32 ++++ locales/el-GR/opencadstudio.ftl | 32 ++++ locales/en-US/opencadstudio.ftl | 32 ++++ locales/es-ES/opencadstudio.ftl | 32 ++++ locales/fi-FI/opencadstudio.ftl | 32 ++++ locales/fr-FR/opencadstudio.ftl | 32 ++++ locales/hi-IN/opencadstudio.ftl | 32 ++++ locales/hu-HU/opencadstudio.ftl | 32 ++++ locales/it-IT/opencadstudio.ftl | 32 ++++ locales/ja-JP/opencadstudio.ftl | 32 ++++ locales/ko-KR/opencadstudio.ftl | 32 ++++ locales/lo-LA/opencadstudio.ftl | 32 ++++ locales/nl-NL/opencadstudio.ftl | 32 ++++ locales/pl-PL/opencadstudio.ftl | 32 ++++ locales/pt-BR/opencadstudio.ftl | 32 ++++ locales/ru-RU/opencadstudio.ftl | 32 ++++ locales/tr-TR/opencadstudio.ftl | 32 ++++ locales/zh-CN/opencadstudio.ftl | 32 ++++ locales/zh-TW/opencadstudio.ftl | 32 ++++ src/app/commands/pdf_dialogs.rs | 11 ++ src/app/commands/view_presets.rs | 208 +++++++++++++++++++++- src/app/control/actions.rs | 48 +++++ src/app/mod.rs | 6 + src/app/update/mod.rs | 7 + src/app/update/style.rs | 12 ++ src/app/view/modal.rs | 11 ++ src/modules/view/view_cmd.rs | 11 +- src/scene/camera_ops.rs | 86 +++++++++ src/scene/mod.rs | 1 + src/scene/view/render.rs | 5 + src/ui/window/pdf_dialogs.rs | 294 +++++++++++++++++++++++++++++++ 34 files changed, 1400 insertions(+), 4 deletions(-) diff --git a/locales/ar-SA/opencadstudio.ftl b/locales/ar-SA/opencadstudio.ftl index a13a13223..2b7667ca3 100644 --- a/locales/ar-SA/opencadstudio.ftl +++ b/locales/ar-SA/opencadstudio.ftl @@ -6220,3 +6220,35 @@ graphic-attributes = .reset-angle = إعادة تعيين الزاوية .one-color = لون واحد .two-colors = لونان + +view-background = + .title = Background + .none = None + .solid = Solid + .gradient = Gradient + .image = Image + .preview = Preview + .solid-color = Solid color + .color = Color + .select = Select… + .three-colors = Three colors + .rotation = Rotation + .top = Top color + .middle = Middle color + .bottom = Bottom color + .browse = Browse… + .position = Position + .center = Center + .stretch = Stretch + .tile = Tile + .scale = Scale + .fit = Fit to screen + .aspect = Keep aspect ratio + .no-image = No image selected + .canvas = Drawing area color + .info = Info + .none-info = The background does not change when the view is restored; the drawing area color is used. + .apply = Apply + .help = Sets the background a named view shows when it is restored. + .image-files = Image files + .select-image = Select Background Image diff --git a/locales/bg-BG/opencadstudio.ftl b/locales/bg-BG/opencadstudio.ftl index d6d751979..6301e8d1f 100644 --- a/locales/bg-BG/opencadstudio.ftl +++ b/locales/bg-BG/opencadstudio.ftl @@ -6208,3 +6208,35 @@ graphic-attributes = .reset-angle = Нулиране на ъгъла .one-color = Един цвят .two-colors = Два цвята + +view-background = + .title = Background + .none = None + .solid = Solid + .gradient = Gradient + .image = Image + .preview = Preview + .solid-color = Solid color + .color = Color + .select = Select… + .three-colors = Three colors + .rotation = Rotation + .top = Top color + .middle = Middle color + .bottom = Bottom color + .browse = Browse… + .position = Position + .center = Center + .stretch = Stretch + .tile = Tile + .scale = Scale + .fit = Fit to screen + .aspect = Keep aspect ratio + .no-image = No image selected + .canvas = Drawing area color + .info = Info + .none-info = The background does not change when the view is restored; the drawing area color is used. + .apply = Apply + .help = Sets the background a named view shows when it is restored. + .image-files = Image files + .select-image = Select Background Image diff --git a/locales/cs-CZ/opencadstudio.ftl b/locales/cs-CZ/opencadstudio.ftl index 779f63fb7..7863c71aa 100644 --- a/locales/cs-CZ/opencadstudio.ftl +++ b/locales/cs-CZ/opencadstudio.ftl @@ -6205,3 +6205,35 @@ graphic-attributes = .reset-angle = Obnovit úhel .one-color = Jedna barva .two-colors = Dvě barvy + +view-background = + .title = Background + .none = None + .solid = Solid + .gradient = Gradient + .image = Image + .preview = Preview + .solid-color = Solid color + .color = Color + .select = Select… + .three-colors = Three colors + .rotation = Rotation + .top = Top color + .middle = Middle color + .bottom = Bottom color + .browse = Browse… + .position = Position + .center = Center + .stretch = Stretch + .tile = Tile + .scale = Scale + .fit = Fit to screen + .aspect = Keep aspect ratio + .no-image = No image selected + .canvas = Drawing area color + .info = Info + .none-info = The background does not change when the view is restored; the drawing area color is used. + .apply = Apply + .help = Sets the background a named view shows when it is restored. + .image-files = Image files + .select-image = Select Background Image diff --git a/locales/de-DE/opencadstudio.ftl b/locales/de-DE/opencadstudio.ftl index f0e33e76b..a56f9511b 100644 --- a/locales/de-DE/opencadstudio.ftl +++ b/locales/de-DE/opencadstudio.ftl @@ -6206,3 +6206,35 @@ graphic-attributes = .reset-angle = Winkel zurücksetzen .one-color = Einfarbig .two-colors = Zweifarbig + +view-background = + .title = Background + .none = None + .solid = Solid + .gradient = Gradient + .image = Image + .preview = Preview + .solid-color = Solid color + .color = Color + .select = Select… + .three-colors = Three colors + .rotation = Rotation + .top = Top color + .middle = Middle color + .bottom = Bottom color + .browse = Browse… + .position = Position + .center = Center + .stretch = Stretch + .tile = Tile + .scale = Scale + .fit = Fit to screen + .aspect = Keep aspect ratio + .no-image = No image selected + .canvas = Drawing area color + .info = Info + .none-info = The background does not change when the view is restored; the drawing area color is used. + .apply = Apply + .help = Sets the background a named view shows when it is restored. + .image-files = Image files + .select-image = Select Background Image diff --git a/locales/el-GR/opencadstudio.ftl b/locales/el-GR/opencadstudio.ftl index d3906e20d..a75508004 100644 --- a/locales/el-GR/opencadstudio.ftl +++ b/locales/el-GR/opencadstudio.ftl @@ -6208,3 +6208,35 @@ graphic-attributes = .reset-angle = Επαναφορά γωνίας .one-color = Ένα χρώμα .two-colors = Δύο χρώματα + +view-background = + .title = Background + .none = None + .solid = Solid + .gradient = Gradient + .image = Image + .preview = Preview + .solid-color = Solid color + .color = Color + .select = Select… + .three-colors = Three colors + .rotation = Rotation + .top = Top color + .middle = Middle color + .bottom = Bottom color + .browse = Browse… + .position = Position + .center = Center + .stretch = Stretch + .tile = Tile + .scale = Scale + .fit = Fit to screen + .aspect = Keep aspect ratio + .no-image = No image selected + .canvas = Drawing area color + .info = Info + .none-info = The background does not change when the view is restored; the drawing area color is used. + .apply = Apply + .help = Sets the background a named view shows when it is restored. + .image-files = Image files + .select-image = Select Background Image diff --git a/locales/en-US/opencadstudio.ftl b/locales/en-US/opencadstudio.ftl index 8ae79dd11..a3389377c 100644 --- a/locales/en-US/opencadstudio.ftl +++ b/locales/en-US/opencadstudio.ftl @@ -6209,3 +6209,35 @@ graphic-attributes = .reset-angle = Reset angle .one-color = One Color .two-colors = Two Colors + +view-background = + .title = Background + .none = None + .solid = Solid + .gradient = Gradient + .image = Image + .preview = Preview + .solid-color = Solid color + .color = Color + .select = Select… + .three-colors = Three colors + .rotation = Rotation + .top = Top color + .middle = Middle color + .bottom = Bottom color + .browse = Browse… + .position = Position + .center = Center + .stretch = Stretch + .tile = Tile + .scale = Scale + .fit = Fit to screen + .aspect = Keep aspect ratio + .no-image = No image selected + .canvas = Drawing area color + .info = Info + .none-info = The background does not change when the view is restored; the drawing area color is used. + .apply = Apply + .help = Sets the background a named view shows when it is restored. + .image-files = Image files + .select-image = Select Background Image diff --git a/locales/es-ES/opencadstudio.ftl b/locales/es-ES/opencadstudio.ftl index 282915ac7..d332786fa 100644 --- a/locales/es-ES/opencadstudio.ftl +++ b/locales/es-ES/opencadstudio.ftl @@ -6208,3 +6208,35 @@ graphic-attributes = .reset-angle = Restablecer ángulo .one-color = Un color .two-colors = Dos colores + +view-background = + .title = Background + .none = None + .solid = Solid + .gradient = Gradient + .image = Image + .preview = Preview + .solid-color = Solid color + .color = Color + .select = Select… + .three-colors = Three colors + .rotation = Rotation + .top = Top color + .middle = Middle color + .bottom = Bottom color + .browse = Browse… + .position = Position + .center = Center + .stretch = Stretch + .tile = Tile + .scale = Scale + .fit = Fit to screen + .aspect = Keep aspect ratio + .no-image = No image selected + .canvas = Drawing area color + .info = Info + .none-info = The background does not change when the view is restored; the drawing area color is used. + .apply = Apply + .help = Sets the background a named view shows when it is restored. + .image-files = Image files + .select-image = Select Background Image diff --git a/locales/fi-FI/opencadstudio.ftl b/locales/fi-FI/opencadstudio.ftl index 71e626c9e..5e77c4ebc 100644 --- a/locales/fi-FI/opencadstudio.ftl +++ b/locales/fi-FI/opencadstudio.ftl @@ -6205,3 +6205,35 @@ graphic-attributes = .reset-angle = Palauta kulma .one-color = Yksi väri .two-colors = Kaksi väriä + +view-background = + .title = Background + .none = None + .solid = Solid + .gradient = Gradient + .image = Image + .preview = Preview + .solid-color = Solid color + .color = Color + .select = Select… + .three-colors = Three colors + .rotation = Rotation + .top = Top color + .middle = Middle color + .bottom = Bottom color + .browse = Browse… + .position = Position + .center = Center + .stretch = Stretch + .tile = Tile + .scale = Scale + .fit = Fit to screen + .aspect = Keep aspect ratio + .no-image = No image selected + .canvas = Drawing area color + .info = Info + .none-info = The background does not change when the view is restored; the drawing area color is used. + .apply = Apply + .help = Sets the background a named view shows when it is restored. + .image-files = Image files + .select-image = Select Background Image diff --git a/locales/fr-FR/opencadstudio.ftl b/locales/fr-FR/opencadstudio.ftl index 47ab852c5..8ba0b5ee8 100644 --- a/locales/fr-FR/opencadstudio.ftl +++ b/locales/fr-FR/opencadstudio.ftl @@ -6207,3 +6207,35 @@ graphic-attributes = .reset-angle = Réinitialiser l'angle .one-color = Une couleur .two-colors = Deux couleurs + +view-background = + .title = Background + .none = None + .solid = Solid + .gradient = Gradient + .image = Image + .preview = Preview + .solid-color = Solid color + .color = Color + .select = Select… + .three-colors = Three colors + .rotation = Rotation + .top = Top color + .middle = Middle color + .bottom = Bottom color + .browse = Browse… + .position = Position + .center = Center + .stretch = Stretch + .tile = Tile + .scale = Scale + .fit = Fit to screen + .aspect = Keep aspect ratio + .no-image = No image selected + .canvas = Drawing area color + .info = Info + .none-info = The background does not change when the view is restored; the drawing area color is used. + .apply = Apply + .help = Sets the background a named view shows when it is restored. + .image-files = Image files + .select-image = Select Background Image diff --git a/locales/hi-IN/opencadstudio.ftl b/locales/hi-IN/opencadstudio.ftl index b6e4ed947..5aaffbdc6 100644 --- a/locales/hi-IN/opencadstudio.ftl +++ b/locales/hi-IN/opencadstudio.ftl @@ -6196,3 +6196,35 @@ graphic-attributes = .reset-angle = कोण रीसेट करें .one-color = एक रंग .two-colors = दो रंग + +view-background = + .title = Background + .none = None + .solid = Solid + .gradient = Gradient + .image = Image + .preview = Preview + .solid-color = Solid color + .color = Color + .select = Select… + .three-colors = Three colors + .rotation = Rotation + .top = Top color + .middle = Middle color + .bottom = Bottom color + .browse = Browse… + .position = Position + .center = Center + .stretch = Stretch + .tile = Tile + .scale = Scale + .fit = Fit to screen + .aspect = Keep aspect ratio + .no-image = No image selected + .canvas = Drawing area color + .info = Info + .none-info = The background does not change when the view is restored; the drawing area color is used. + .apply = Apply + .help = Sets the background a named view shows when it is restored. + .image-files = Image files + .select-image = Select Background Image diff --git a/locales/hu-HU/opencadstudio.ftl b/locales/hu-HU/opencadstudio.ftl index 00674b1f2..8082c7a92 100644 --- a/locales/hu-HU/opencadstudio.ftl +++ b/locales/hu-HU/opencadstudio.ftl @@ -6194,3 +6194,35 @@ graphic-attributes = .reset-angle = Szög visszaállítása .one-color = Egy szín .two-colors = Két szín + +view-background = + .title = Background + .none = None + .solid = Solid + .gradient = Gradient + .image = Image + .preview = Preview + .solid-color = Solid color + .color = Color + .select = Select… + .three-colors = Three colors + .rotation = Rotation + .top = Top color + .middle = Middle color + .bottom = Bottom color + .browse = Browse… + .position = Position + .center = Center + .stretch = Stretch + .tile = Tile + .scale = Scale + .fit = Fit to screen + .aspect = Keep aspect ratio + .no-image = No image selected + .canvas = Drawing area color + .info = Info + .none-info = The background does not change when the view is restored; the drawing area color is used. + .apply = Apply + .help = Sets the background a named view shows when it is restored. + .image-files = Image files + .select-image = Select Background Image diff --git a/locales/it-IT/opencadstudio.ftl b/locales/it-IT/opencadstudio.ftl index d84e500ad..e04a56056 100644 --- a/locales/it-IT/opencadstudio.ftl +++ b/locales/it-IT/opencadstudio.ftl @@ -6207,3 +6207,35 @@ graphic-attributes = .reset-angle = Reimposta angolo .one-color = Un colore .two-colors = Due colori + +view-background = + .title = Background + .none = None + .solid = Solid + .gradient = Gradient + .image = Image + .preview = Preview + .solid-color = Solid color + .color = Color + .select = Select… + .three-colors = Three colors + .rotation = Rotation + .top = Top color + .middle = Middle color + .bottom = Bottom color + .browse = Browse… + .position = Position + .center = Center + .stretch = Stretch + .tile = Tile + .scale = Scale + .fit = Fit to screen + .aspect = Keep aspect ratio + .no-image = No image selected + .canvas = Drawing area color + .info = Info + .none-info = The background does not change when the view is restored; the drawing area color is used. + .apply = Apply + .help = Sets the background a named view shows when it is restored. + .image-files = Image files + .select-image = Select Background Image diff --git a/locales/ja-JP/opencadstudio.ftl b/locales/ja-JP/opencadstudio.ftl index ed37d6a97..70e632c1e 100644 --- a/locales/ja-JP/opencadstudio.ftl +++ b/locales/ja-JP/opencadstudio.ftl @@ -6196,3 +6196,35 @@ graphic-attributes = .reset-angle = 角度をリセット .one-color = 1 色 .two-colors = 2 色 + +view-background = + .title = Background + .none = None + .solid = Solid + .gradient = Gradient + .image = Image + .preview = Preview + .solid-color = Solid color + .color = Color + .select = Select… + .three-colors = Three colors + .rotation = Rotation + .top = Top color + .middle = Middle color + .bottom = Bottom color + .browse = Browse… + .position = Position + .center = Center + .stretch = Stretch + .tile = Tile + .scale = Scale + .fit = Fit to screen + .aspect = Keep aspect ratio + .no-image = No image selected + .canvas = Drawing area color + .info = Info + .none-info = The background does not change when the view is restored; the drawing area color is used. + .apply = Apply + .help = Sets the background a named view shows when it is restored. + .image-files = Image files + .select-image = Select Background Image diff --git a/locales/ko-KR/opencadstudio.ftl b/locales/ko-KR/opencadstudio.ftl index a5ed580ee..8b3a260d0 100644 --- a/locales/ko-KR/opencadstudio.ftl +++ b/locales/ko-KR/opencadstudio.ftl @@ -6207,3 +6207,35 @@ graphic-attributes = .reset-angle = 각도 재설정 .one-color = 단색 .two-colors = 두 색 + +view-background = + .title = Background + .none = None + .solid = Solid + .gradient = Gradient + .image = Image + .preview = Preview + .solid-color = Solid color + .color = Color + .select = Select… + .three-colors = Three colors + .rotation = Rotation + .top = Top color + .middle = Middle color + .bottom = Bottom color + .browse = Browse… + .position = Position + .center = Center + .stretch = Stretch + .tile = Tile + .scale = Scale + .fit = Fit to screen + .aspect = Keep aspect ratio + .no-image = No image selected + .canvas = Drawing area color + .info = Info + .none-info = The background does not change when the view is restored; the drawing area color is used. + .apply = Apply + .help = Sets the background a named view shows when it is restored. + .image-files = Image files + .select-image = Select Background Image diff --git a/locales/lo-LA/opencadstudio.ftl b/locales/lo-LA/opencadstudio.ftl index 7304c8344..c26755152 100644 --- a/locales/lo-LA/opencadstudio.ftl +++ b/locales/lo-LA/opencadstudio.ftl @@ -6209,3 +6209,35 @@ graphic-attributes = .reset-angle = ຣີເຊັດມຸມ .one-color = ໜຶ່ງສີ .two-colors = ສອງສີ + +view-background = + .title = Background + .none = None + .solid = Solid + .gradient = Gradient + .image = Image + .preview = Preview + .solid-color = Solid color + .color = Color + .select = Select… + .three-colors = Three colors + .rotation = Rotation + .top = Top color + .middle = Middle color + .bottom = Bottom color + .browse = Browse… + .position = Position + .center = Center + .stretch = Stretch + .tile = Tile + .scale = Scale + .fit = Fit to screen + .aspect = Keep aspect ratio + .no-image = No image selected + .canvas = Drawing area color + .info = Info + .none-info = The background does not change when the view is restored; the drawing area color is used. + .apply = Apply + .help = Sets the background a named view shows when it is restored. + .image-files = Image files + .select-image = Select Background Image diff --git a/locales/nl-NL/opencadstudio.ftl b/locales/nl-NL/opencadstudio.ftl index 4ad871607..195ff3b44 100644 --- a/locales/nl-NL/opencadstudio.ftl +++ b/locales/nl-NL/opencadstudio.ftl @@ -6206,3 +6206,35 @@ graphic-attributes = .reset-angle = Hoek herstellen .one-color = Eén kleur .two-colors = Twee kleuren + +view-background = + .title = Background + .none = None + .solid = Solid + .gradient = Gradient + .image = Image + .preview = Preview + .solid-color = Solid color + .color = Color + .select = Select… + .three-colors = Three colors + .rotation = Rotation + .top = Top color + .middle = Middle color + .bottom = Bottom color + .browse = Browse… + .position = Position + .center = Center + .stretch = Stretch + .tile = Tile + .scale = Scale + .fit = Fit to screen + .aspect = Keep aspect ratio + .no-image = No image selected + .canvas = Drawing area color + .info = Info + .none-info = The background does not change when the view is restored; the drawing area color is used. + .apply = Apply + .help = Sets the background a named view shows when it is restored. + .image-files = Image files + .select-image = Select Background Image diff --git a/locales/pl-PL/opencadstudio.ftl b/locales/pl-PL/opencadstudio.ftl index 7ba65e368..0a33d5796 100644 --- a/locales/pl-PL/opencadstudio.ftl +++ b/locales/pl-PL/opencadstudio.ftl @@ -6207,3 +6207,35 @@ graphic-attributes = .reset-angle = Resetuj kąt .one-color = Jeden kolor .two-colors = Dwa kolory + +view-background = + .title = Background + .none = None + .solid = Solid + .gradient = Gradient + .image = Image + .preview = Preview + .solid-color = Solid color + .color = Color + .select = Select… + .three-colors = Three colors + .rotation = Rotation + .top = Top color + .middle = Middle color + .bottom = Bottom color + .browse = Browse… + .position = Position + .center = Center + .stretch = Stretch + .tile = Tile + .scale = Scale + .fit = Fit to screen + .aspect = Keep aspect ratio + .no-image = No image selected + .canvas = Drawing area color + .info = Info + .none-info = The background does not change when the view is restored; the drawing area color is used. + .apply = Apply + .help = Sets the background a named view shows when it is restored. + .image-files = Image files + .select-image = Select Background Image diff --git a/locales/pt-BR/opencadstudio.ftl b/locales/pt-BR/opencadstudio.ftl index 2e21eeeef..8601be839 100644 --- a/locales/pt-BR/opencadstudio.ftl +++ b/locales/pt-BR/opencadstudio.ftl @@ -6208,3 +6208,35 @@ graphic-attributes = .reset-angle = Redefinir ângulo .one-color = Uma cor .two-colors = Duas cores + +view-background = + .title = Background + .none = None + .solid = Solid + .gradient = Gradient + .image = Image + .preview = Preview + .solid-color = Solid color + .color = Color + .select = Select… + .three-colors = Three colors + .rotation = Rotation + .top = Top color + .middle = Middle color + .bottom = Bottom color + .browse = Browse… + .position = Position + .center = Center + .stretch = Stretch + .tile = Tile + .scale = Scale + .fit = Fit to screen + .aspect = Keep aspect ratio + .no-image = No image selected + .canvas = Drawing area color + .info = Info + .none-info = The background does not change when the view is restored; the drawing area color is used. + .apply = Apply + .help = Sets the background a named view shows when it is restored. + .image-files = Image files + .select-image = Select Background Image diff --git a/locales/ru-RU/opencadstudio.ftl b/locales/ru-RU/opencadstudio.ftl index 9672928b6..e03bdb029 100644 --- a/locales/ru-RU/opencadstudio.ftl +++ b/locales/ru-RU/opencadstudio.ftl @@ -6209,3 +6209,35 @@ graphic-attributes = .reset-angle = Сбросить угол .one-color = Один цвет .two-colors = Два цвета + +view-background = + .title = Background + .none = None + .solid = Solid + .gradient = Gradient + .image = Image + .preview = Preview + .solid-color = Solid color + .color = Color + .select = Select… + .three-colors = Three colors + .rotation = Rotation + .top = Top color + .middle = Middle color + .bottom = Bottom color + .browse = Browse… + .position = Position + .center = Center + .stretch = Stretch + .tile = Tile + .scale = Scale + .fit = Fit to screen + .aspect = Keep aspect ratio + .no-image = No image selected + .canvas = Drawing area color + .info = Info + .none-info = The background does not change when the view is restored; the drawing area color is used. + .apply = Apply + .help = Sets the background a named view shows when it is restored. + .image-files = Image files + .select-image = Select Background Image diff --git a/locales/tr-TR/opencadstudio.ftl b/locales/tr-TR/opencadstudio.ftl index 31ed14f3f..34ca01758 100644 --- a/locales/tr-TR/opencadstudio.ftl +++ b/locales/tr-TR/opencadstudio.ftl @@ -6131,3 +6131,35 @@ graphic-attributes = .reset-angle = Açıyı sıfırla .one-color = Tek renk .two-colors = İki renk + +view-background = + .title = Arka Plan + .none = Yok + .solid = Düz + .gradient = Gradyan + .image = Görüntü + .preview = Önizleme + .solid-color = Düz renk + .color = Renk + .select = Seç… + .three-colors = Üç renk + .rotation = Döndürme + .top = Üst renk + .middle = Orta renk + .bottom = Alt renk + .browse = Gözat… + .position = Yerleşim + .center = Ortala + .stretch = Uzat + .tile = Döşe + .scale = Ölçek + .fit = Ekrana sığdır + .aspect = Oranı koru + .no-image = görüntü seçilmedi + .canvas = çizim alanı rengi + .info = Bilgi + .none-info = Görünüm geri yüklendiğinde arka plan değişmez; çizim alanı rengi kullanılır. + .apply = Uygula + .help = Adlandırılmış görünüm geri yüklendiğinde gösterilecek arka planı ayarlar. + .image-files = Görüntü dosyaları + .select-image = Arka Plan Görüntüsü Seç diff --git a/locales/zh-CN/opencadstudio.ftl b/locales/zh-CN/opencadstudio.ftl index 521e92320..0948800ad 100644 --- a/locales/zh-CN/opencadstudio.ftl +++ b/locales/zh-CN/opencadstudio.ftl @@ -6191,3 +6191,35 @@ graphic-attributes = .reset-angle = 重置角度 .one-color = 单色 .two-colors = 双色 + +view-background = + .title = Background + .none = None + .solid = Solid + .gradient = Gradient + .image = Image + .preview = Preview + .solid-color = Solid color + .color = Color + .select = Select… + .three-colors = Three colors + .rotation = Rotation + .top = Top color + .middle = Middle color + .bottom = Bottom color + .browse = Browse… + .position = Position + .center = Center + .stretch = Stretch + .tile = Tile + .scale = Scale + .fit = Fit to screen + .aspect = Keep aspect ratio + .no-image = No image selected + .canvas = Drawing area color + .info = Info + .none-info = The background does not change when the view is restored; the drawing area color is used. + .apply = Apply + .help = Sets the background a named view shows when it is restored. + .image-files = Image files + .select-image = Select Background Image diff --git a/locales/zh-TW/opencadstudio.ftl b/locales/zh-TW/opencadstudio.ftl index 3346a8f09..139e45847 100644 --- a/locales/zh-TW/opencadstudio.ftl +++ b/locales/zh-TW/opencadstudio.ftl @@ -6203,3 +6203,35 @@ graphic-attributes = .reset-angle = 重設角度 .one-color = 單色 .two-colors = 雙色 + +view-background = + .title = Background + .none = None + .solid = Solid + .gradient = Gradient + .image = Image + .preview = Preview + .solid-color = Solid color + .color = Color + .select = Select… + .three-colors = Three colors + .rotation = Rotation + .top = Top color + .middle = Middle color + .bottom = Bottom color + .browse = Browse… + .position = Position + .center = Center + .stretch = Stretch + .tile = Tile + .scale = Scale + .fit = Fit to screen + .aspect = Keep aspect ratio + .no-image = No image selected + .canvas = Drawing area color + .info = Info + .none-info = The background does not change when the view is restored; the drawing area color is used. + .apply = Apply + .help = Sets the background a named view shows when it is restored. + .image-files = Image files + .select-image = Select Background Image diff --git a/src/app/commands/pdf_dialogs.rs b/src/app/commands/pdf_dialogs.rs index ec69e6e12..2c14389c8 100644 --- a/src/app/commands/pdf_dialogs.rs +++ b/src/app/commands/pdf_dialogs.rs @@ -439,6 +439,7 @@ impl OpenCADStudio { "attach" => crate::t!("Attaches a PDF file as an underlay."), "pointcloud" => crate::t!("Attaches a point cloud scan or project."), "pcsection" => crate::t!("Traces the points on the kept side of a live section as lines."), + "background" => std::borrow::Cow::Owned(crate::tr!("view-background", "help")), "pointcloudcolormap" => { crate::t!("Sets the color schemes and ranges a point cloud is colored by intensity or elevation.") } @@ -572,6 +573,16 @@ impl OpenCADStudio { | PdfDialogMsg::MapCurrent(_) | PdfDialogMsg::MapApply | PdfDialogMsg::MapOk) => return self.update_point_cloud_color_map(message), + message @ (PdfDialogMsg::BgKind(_) + | PdfDialogMsg::BgPickColor(_) + | PdfDialogMsg::BgColor(..) + | PdfDialogMsg::BgThree(_) + | PdfDialogMsg::BgRotation(_) + | PdfDialogMsg::BgImage(_) + | PdfDialogMsg::BgBrowse + | PdfDialogMsg::BgPosition(_) + | PdfDialogMsg::BgAspect(_) + | PdfDialogMsg::BgApply) => return self.update_view_background(message), message @ (PdfDialogMsg::SecPerimeter(_) | PdfDialogMsg::SecMaxPoints(_) | PdfDialogMsg::SecLayer(_) diff --git a/src/app/commands/view_presets.rs b/src/app/commands/view_presets.rs index 66dc10bb9..901deddd1 100644 --- a/src/app/commands/view_presets.rs +++ b/src/app/commands/view_presets.rs @@ -111,7 +111,7 @@ impl OpenCADStudio { } /// Carry out what -VIEW gathered (`-VIEW >VERB args`). - pub(super) fn run_dash_view(&mut self, rest: &str, i: usize) -> Task { + pub(crate) fn run_dash_view(&mut self, rest: &str, i: usize) -> Task { let (verb, arg) = rest.split_once(' ').unwrap_or((rest, "")); let mut settings_again = false; match verb { @@ -197,6 +197,14 @@ impl OpenCADStudio { } } "SETTINGS" => settings_again = true, + // Background type chosen: its dialog takes over until Apply or Cancel. + "BACKGROUND" => { + if let Some((view, kind)) = arg.split_once('\u{1f}') { + self.open_view_background(i, view, kind); + return self.finish_dispatch("-VIEW"); + } + settings_again = true; + } "UCSVIEW" => { view_cmd::set_ucs_view(arg == "1"); self.save_config(); @@ -245,14 +253,20 @@ impl OpenCADStudio { /// of a view saved again under the same name. fn save_named_view(&mut self, i: usize, mut view: codec::tables::View) { self.push_undo_snapshot(i, "-VIEW"); + let background = self.tabs[i].scene.active_background(); let document = &mut self.tabs[i].scene.document; + view.background_handle = crate::scene::copy_background(document, background); match document.views.get(&view.name).cloned() { Some(old) => { view.name = old.name; view.handle = old.handle; view.visual_style_handle = old.visual_style_handle; view.live_section_handle = old.live_section_handle; - view.background_handle = old.background_handle; + // The view keeps its background unless the viewport has + // one, which the view then takes a copy of. + if view.background_handle.is_null() { + view.background_handle = old.background_handle; + } } None => { view.handle = document.allocate_handle(); @@ -339,3 +353,193 @@ impl OpenCADStudio { self.tabs[i].dirty = true; } } + +impl OpenCADStudio { + /// The Background dialog for a named view, on the type typed at the + /// prompt and filled from the view's background where it has one. + fn open_view_background(&mut self, i: usize, view: &str, kind: &str) { + use crate::ui::window::pdf_dialogs::{BgKind, BgPosition, ViewBackgroundState}; + use codec::objects::ClassObjectData as D; + let unpack = |c: u32| [(c >> 16) as u8, (c >> 8) as u8, c as u8]; + let canvas = self.tabs[i].scene.bg_color; + let mut state = ViewBackgroundState { + view: view.to_string(), + kind: match kind { + "SOLID" => BgKind::Solid, + "GRADIENT" => BgKind::Gradient, + "IMAGE" => BgKind::Image, + _ => BgKind::None, + }, + // The reference's defaults. + solid: [128, 128, 255], + top: [204, 204, 204], + middle: [128, 128, 128], + bottom: [51, 51, 51], + three: false, + rotation: "0".into(), + image: String::new(), + position: BgPosition::Center, + aspect: false, + canvas: [0, 1, 2].map(|k| (canvas[k].clamp(0.0, 1.0) * 255.0).round() as u8), + }; + let document = &self.tabs[i].scene.document; + let current = document.views.get(view).map_or(Handle::NULL, |v| v.background_handle); + if let Some(ObjectType::ClassObject(object)) = document.objects.get(¤t) { + match &object.data { + D::SolidBackground(value) => state.solid = unpack(value.color), + D::GradientBackground(value) => { + (state.top, state.middle, state.bottom) = ( + unpack(value.color_top), + unpack(value.color_middle), + unpack(value.color_bottom), + ); + state.three = value.height > 0.0; + state.rotation = crate::ui::window::pdf_dialogs::short(value.rotation); + } + D::ImageBackground(value) => { + state.image = value.filename.clone(); + state.position = if value.use_tiling { + BgPosition::Tile + } else if value.fit_to_screen { + BgPosition::Stretch + } else { + BgPosition::Center + }; + state.aspect = value.maintain_aspect_ratio; + } + _ => {} + } + } + self.view_background = Some(state); + self.active_modal = Some(crate::app::ModalKind::ViewBackground); + } + + pub(in crate::app) fn update_view_background( + &mut self, + message: crate::ui::window::pdf_dialogs::PdfDialogMsg, + ) -> Task { + use crate::ui::window::pdf_dialogs::{BgKind, BgPosition, PdfDialogMsg as M}; + use codec::objects::{ + ClassObjectData as D, GradientBackground, ImageBackground, SolidBackground, + }; + let i = self.active_tab; + let Some(state) = self.view_background.as_mut() else { + return Task::none(); + }; + match message { + M::BgKind(kind) => state.kind = kind, + M::BgThree(on) => state.three = on, + M::BgRotation(value) => state.rotation = value, + M::BgImage(value) => state.image = value, + M::BgPosition(value) => state.position = value, + M::BgAspect(on) => state.aspect = on, + M::BgColor(slot, rgb) => match slot { + 0 => state.solid = rgb, + 1 => state.top = rgb, + 2 => state.middle = rgb, + _ => state.bottom = rgb, + }, + M::BgPickColor(slot) => { + let [r, g, b] = match slot { + 0 => state.solid, + 1 => state.top, + 2 => state.middle, + _ => state.bottom, + }; + return Task::done(Message::OpenColorWindow( + crate::app::ColorPickTarget::ViewBackground(slot), + codec::types::Color::Rgb { r, g, b }, + )); + } + M::BgBrowse => { + return Task::perform( + async { + crate::sys::file_dialog() + .set_title(crate::tr!("view-background", "select-image")) + .add_filter( + crate::tr!("view-background", "image-files"), + &["png", "jpg", "jpeg", "bmp", "tif", "tiff", "gif", "tga"], + ) + .pick_file() + .await + .map(|handle| crate::sys::handle_path(&handle)) + }, + |path| match path { + Some(path) => Message::PdfDialog(M::BgImage(path.display().to_string())), + None => Message::Noop, + }, + ); + } + M::BgApply => { + // Colours are stored as true colours (flag byte 0xC2), as the + // reference stores them. + let pack = |c: [u8; 3]| { + 0xC200_0000 | (c[0] as u32) << 16 | (c[1] as u32) << 8 | c[2] as u32 + }; + let data = match state.kind { + BgKind::None => None, + BgKind::Solid => Some(D::SolidBackground(SolidBackground { + class_version: 1, + color: pack(state.solid), + })), + BgKind::Gradient => { + let Some(rotation) = state + .rotation + .trim() + .parse::() + .ok() + .filter(|v| v.is_finite()) + else { + self.command_line + .push_error(crate::t!("Requires a number.").as_ref()); + return Task::none(); + }; + Some(D::GradientBackground(GradientBackground { + class_version: 1, + color_top: pack(state.top), + color_middle: pack(state.middle), + color_bottom: pack(state.bottom), + horizon: 0.5, + // The reference marks a three-colour gradient by its + // middle band's height. + height: if state.three { 0.33 } else { 0.0 }, + rotation, + })) + } + BgKind::Image => { + if !std::path::Path::new(state.image.trim()).is_file() { + self.command_line.push_error("Invalid Background Image File!"); + return Task::none(); + } + Some(D::ImageBackground(ImageBackground { + class_version: 1, + filename: state.image.trim().to_string(), + fit_to_screen: state.position == BgPosition::Stretch, + maintain_aspect_ratio: state.aspect, + use_tiling: state.position == BgPosition::Tile, + offset: codec::types::Vector2::new(0.0, 0.0), + scale: codec::types::Vector2::new(1.0, 1.0), + })) + } + }; + let name = state.view.clone(); + self.view_background = None; + self.close_active_modal(); + if self.tabs[i].scene.document.views.get(&name).is_some() { + self.push_undo_snapshot(i, "-VIEW"); + let document = &mut self.tabs[i].scene.document; + let handle = data.map_or(Handle::NULL, |data| { + crate::scene::add_background(document, data) + }); + if let Some(view) = document.views.get_mut(&name) { + view.background_handle = handle; + } + self.tabs[i].dirty = true; + } + return self.run_dash_view("SETTINGS", i); + } + _ => {} + } + Task::none() + } +} diff --git a/src/app/control/actions.rs b/src/app/control/actions.rs index cbe3fdcb0..b2e0acb7f 100644 --- a/src/app/control/actions.rs +++ b/src/app/control/actions.rs @@ -103,6 +103,7 @@ pub(super) const NAMES: &[&str] = &[ #[cfg(not(target_arch = "wasm32"))] "external_change_cancel", "pdf_dialog_ok", + "view_background", "pdf_layer_toggle", "pc_manager_toggle", "pc_manager", @@ -1786,6 +1787,53 @@ impl OpenCADStudio { }) } "ribbon_dropdown" => Message::ToggleRibbonDropdown(string(req, "value")?.into()), + // The -VIEW Background dialog, one field per call: "type=gradient", + // "color|top|middle|bottom=r,g,b", "three=1", "rotation=30", + // "image=", "position=center|stretch|tile", "aspect=1", + // "apply" or "cancel". + "view_background" => { + use crate::ui::window::pdf_dialogs::{BgKind, BgPosition, PdfDialogMsg as P}; + if self.active_modal != Some(crate::app::ModalKind::ViewBackground) { + return Err(failure("no_dialog", "The Background dialog is not open")); + } + let spec = string(req, "value")?; + let (key, value) = spec.split_once('=').unwrap_or((spec, "")); + let bad = || failure("invalid_value", spec); + let rgb = || -> Result<[u8; 3], Value> { + let parts: Vec = value + .split(',') + .map(|part| part.trim().parse::()) + .collect::>() + .map_err(|_| bad())?; + <[u8; 3]>::try_from(parts).map_err(|_| bad()) + }; + match key { + "cancel" => Message::CloseModal, + "apply" => Message::PdfDialog(P::BgApply), + "type" => Message::PdfDialog(P::BgKind(match value { + "none" => BgKind::None, + "solid" => BgKind::Solid, + "gradient" => BgKind::Gradient, + "image" => BgKind::Image, + _ => return Err(bad()), + })), + "color" => Message::PdfDialog(P::BgColor(0, rgb()?)), + "top" => Message::PdfDialog(P::BgColor(1, rgb()?)), + "middle" => Message::PdfDialog(P::BgColor(2, rgb()?)), + "bottom" => Message::PdfDialog(P::BgColor(3, rgb()?)), + "three" => Message::PdfDialog(P::BgThree(value == "1")), + "rotation" => Message::PdfDialog(P::BgRotation(value.to_string())), + "image" => Message::PdfDialog(P::BgImage(value.to_string())), + "position" => Message::PdfDialog(P::BgPosition(match value { + "center" => BgPosition::Center, + "stretch" => BgPosition::Stretch, + "tile" => BgPosition::Tile, + _ => return Err(bad()), + })), + "aspect" => Message::PdfDialog(P::BgAspect(value == "1")), + _ => return Err(bad()), + } + } "pdf_dialog_ok" => { use crate::ui::window::pdf_dialogs::PdfDialogMsg; Message::PdfDialog(match self.active_modal { diff --git a/src/app/mod.rs b/src/app/mod.rs index 4462dd48a..99e714609 100644 --- a/src/app/mod.rs +++ b/src/app/mod.rs @@ -502,6 +502,8 @@ pub(super) struct OpenCADStudio { field_dialog: Option, point_cloud_color_map: Option, pc_section: Option, + /// -VIEW sEttings Background dialog. + view_background: Option, underlay_layers: Option, pdf_import_settings: Option, pdf_import_file: Option, @@ -1641,6 +1643,8 @@ pub enum ColorPickTarget { LayerState(usize), /// The MText editor's selection (or global) colour. MText, + /// A colour of the view Background dialog: 0 solid, 1 top, 2 middle, 3 bottom. + ViewBackground(u8), } /// Table records the clipboard entities depend on, snapshotted from the source @@ -1981,6 +1985,7 @@ pub enum ModalKind { PointCloudAttach, PointCloudColorMap, PcSection, + ViewBackground, UnderlayLayers, PdfImportSettings, PdfImportFile, @@ -4186,6 +4191,7 @@ impl OpenCADStudio { field_dialog: None, point_cloud_color_map: None, pc_section: None, + view_background: None, underlay_layers: None, pdf_import_settings: None, pdf_import_file: None, diff --git a/src/app/update/mod.rs b/src/app/update/mod.rs index 0ab8654a2..636841cac 100644 --- a/src/app/update/mod.rs +++ b/src/app/update/mod.rs @@ -8799,6 +8799,13 @@ impl OpenCADStudio { self.close_sheet_set_dialog(); return Task::none(); } + // The Background dialog returns to -VIEW's settings prompt. + if self.active_modal == Some(super::ModalKind::ViewBackground) { + self.view_background = None; + self.close_active_modal(); + let i = self.active_tab; + return self.run_dash_view("SETTINGS", i); + } if self.active_modal == Some(super::ModalKind::Field) { self.close_field_dialog(); return Task::none(); diff --git a/src/app/update/style.rs b/src/app/update/style.rs index 2deac74ec..8ffb8f908 100644 --- a/src/app/update/style.rs +++ b/src/app/update/style.rs @@ -1162,6 +1162,18 @@ impl OpenCADStudio { crate::ui::window::gradient_editor::GradientMsg::Color(index, color), ))) } + Some(crate::app::ColorPickTarget::ViewBackground(slot)) => { + let rgb = match color { + codec::types::Color::Rgb { r, g, b } => Some([r, g, b]), + codec::types::Color::Index(index) => { + codec::types::aci_table::aci_to_rgb(index).map(|(r, g, b)| [r, g, b]) + } + _ => None, + }; + rgb.map(|rgb| { + Message::PdfDialog(crate::ui::window::pdf_dialogs::PdfDialogMsg::BgColor(slot, rgb)) + }) + } Some(crate::app::ColorPickTarget::GraphicAttributesLine) => Some( Message::GraphicAttributes(GraphicAttributesMsg::LineColor(color)), ), diff --git a/src/app/view/modal.rs b/src/app/view/modal.rs index c6d45b677..7d153b356 100644 --- a/src/app/view/modal.rs +++ b/src/app/view/modal.rs @@ -35,6 +35,11 @@ impl OpenCADStudio { Some(K::PointCloudAttach) => crate::t!("Attach Point Cloud").into_owned(), Some(K::PointCloudColorMap) => crate::t!("Point Cloud Color Map").into_owned(), Some(K::PcSection) => crate::t!("Extract Section Lines from Point Cloud").into_owned(), + Some(K::ViewBackground) => format!( + "{} — {}", + crate::tr!("view-background", "title"), + self.view_background.as_ref().map_or("", |s| s.view.as_str()) + ), Some(K::UnderlayLayers) => crate::t!("Underlay Layers").into_owned(), Some(K::PdfImportSettings) => crate::t!("PDF Import Settings").into_owned(), Some(K::PdfImportFile) => crate::t!("Import PDF").into_owned(), @@ -510,6 +515,12 @@ impl OpenCADStudio { crate::ui::window::pdf_dialogs::view_pc_section(state, flow) }) } + super::super::ModalKind::ViewBackground => { + let state = self.view_background.as_ref()?; + sized_flow(ex, 420, 560, |flow| { + crate::ui::window::pdf_dialogs::view_background(state, flow) + }) + } super::super::ModalKind::UnderlayLayers => { let state = self.underlay_layers.as_ref()?; sized_flow(ex, 460, 520, |flow| { diff --git a/src/modules/view/view_cmd.rs b/src/modules/view/view_cmd.rs index c29d9164b..4d3099f4f 100644 --- a/src/modules/view/view_cmd.rs +++ b/src/modules/view/view_cmd.rs @@ -226,9 +226,16 @@ impl DashViewCommand { let t = text.trim(); match setting { Setting::Background => match keyword(t, &["Color", "Gradient", "Image", "None"]) { - // The other background types are chosen in their own dialogs. Some("None") => Self::apply(setting, &view, "NONE"), - Some(_) => CmdResult::Dispatch("-VIEW >SETTINGS".into()), + // The other types are set in the Background dialog. + Some(kind) => CmdResult::Dispatch(format!( + "-VIEW >BACKGROUND {view}\u{1f}{}", + match kind { + "Color" => "SOLID", + "Gradient" => "GRADIENT", + _ => "IMAGE", + } + )), None => { self.step = Step::SettingValue(setting, view); CmdResult::ReportError("Invalid option keyword.".into()) diff --git a/src/scene/camera_ops.rs b/src/scene/camera_ops.rs index d437bfac7..a26d85274 100644 --- a/src/scene/camera_ops.rs +++ b/src/scene/camera_ops.rs @@ -215,6 +215,11 @@ impl Scene { /// Restore camera to a named view from the document view table. pub fn restore_named_view(&mut self, view: &codec::tables::View) { use glam::Vec3; + // The model viewport takes a copy of the view's background, or none. + if self.current_layout == "Model" { + let background = copy_background(&mut self.document, view.background_handle); + self.set_active_background(background); + } let cam = &mut *self.camera.borrow_mut(); // The stored direction points from the target toward the eye. let eye_dir = Vec3::new( @@ -245,6 +250,22 @@ impl Scene { self.camera_generation += 1; } + /// The background the active model-space viewport shows (null for the + /// drawing area colour). + pub fn active_background(&self) -> Handle { + self.active_tile_vport_handle() + .and_then(|handle| self.document.vports.iter().find(|value| value.handle == handle)) + .map_or(Handle::NULL, |value| value.background_handle) + } + + fn set_active_background(&mut self, background: Handle) { + let Some(handle) = self.active_tile_vport_handle() else { return }; + if let Some(value) = self.document.vports.iter_mut().find(|value| value.handle == handle) { + value.background_handle = background; + self.camera_generation += 1; + } + } + /// Save the current camera state into a new named view entry. /// Returns the view; caller must push it into document.views. pub fn current_as_named_view(&self, name: &str) -> codec::tables::View { @@ -1638,3 +1659,68 @@ impl Scene { } } } + +/// The drawing's ACAD_BACKGROUND dictionary, made when it has none. +fn background_dictionary(document: &mut codec::CadDocument) -> Option { + use codec::objects::{Dictionary, ObjectType}; + let root = document.header.named_objects_dict_handle; + let Some(ObjectType::Dictionary(root_dictionary)) = document.objects.get(&root) else { + return None; + }; + if let Some(existing) = root_dictionary.get("ACAD_BACKGROUND") { + if matches!(document.objects.get(&existing), Some(ObjectType::Dictionary(_))) { + return Some(existing); + } + } + let handle = document.allocate_handle(); + let mut dictionary = Dictionary::new(); + dictionary.handle = handle; + dictionary.owner = root; + dictionary.reactors.push(root); + document.objects.insert(handle, ObjectType::Dictionary(dictionary)); + if let Some(ObjectType::Dictionary(root_dictionary)) = document.objects.get_mut(&root) { + root_dictionary.add_entry("ACAD_BACKGROUND", handle); + } + Some(handle) +} + +/// A new background object in ACAD_BACKGROUND, keyed by its own handle as +/// the reference keys it. Null when the drawing has no named-objects root. +pub fn add_background( + document: &mut codec::CadDocument, + data: codec::objects::ClassObjectData, +) -> Handle { + use codec::objects::{ClassObject, ObjectType}; + let Some(dictionary) = background_dictionary(document) else { + return Handle::NULL; + }; + let handle = document.allocate_handle(); + let mut object = ClassObject::new(data); + object.handle = handle; + object.owner = dictionary; + object.reactors = vec![dictionary]; + document.objects.insert(handle, ObjectType::ClassObject(object)); + if let Some(ObjectType::Dictionary(entries)) = document.objects.get_mut(&dictionary) { + entries.add_entry(format!("{:X}", handle.value()), handle); + } + handle +} + +/// A copy of a background object (the reference gives each view and +/// viewport its own); null for none. +pub fn copy_background(document: &mut codec::CadDocument, handle: Handle) -> Handle { + use codec::objects::{ClassObjectData as D, ObjectType}; + match document.objects.get(&handle) { + Some(ObjectType::ClassObject(object)) + if matches!( + object.data, + D::SolidBackground(_) | D::GradientBackground(_) | D::ImageBackground(_) + | D::GroundPlaneBackground(_) | D::SkyLightBackground(_) | D::IblBackground(_) + ) => + { + let data = object.data.clone(); + add_background(document, data) + } + _ => Handle::NULL, + } +} diff --git a/src/scene/mod.rs b/src/scene/mod.rs index a895f6dd8..0ebed3852 100644 --- a/src/scene/mod.rs +++ b/src/scene/mod.rs @@ -28,6 +28,7 @@ pub enum BlockScalePolicy { // blocks and/or free functions). Pure text-move from the original mod.rs. mod boundary; mod camera_ops; +pub use camera_ops::{add_background, copy_background}; pub(crate) mod centerline; pub(crate) mod centermark; pub(crate) mod dimension_assoc; diff --git a/src/scene/view/render.rs b/src/scene/view/render.rs index c91d30ae2..d1bdc2ae9 100644 --- a/src/scene/view/render.rs +++ b/src/scene/view/render.rs @@ -2034,6 +2034,11 @@ text_atlas={:.1} wire_arena={:.1} block_geometry={:.1}", } impl Scene { + /// The model-space viewport record of the active tile. + pub(crate) fn active_tile_vport_handle(&self) -> Option { + self.model_tile_vport(self.active_model_tile.get()).map(|value| value.handle) + } + fn model_tile_vport(&self, index: usize) -> Option<&codec::tables::VPort> { let rect = self.model_tiles.borrow().get(index)?.rect; let lower_left = [rect.x as f64, (1.0 - rect.y - rect.height) as f64]; diff --git a/src/ui/window/pdf_dialogs.rs b/src/ui/window/pdf_dialogs.rs index 3c1e49d26..a971b4081 100644 --- a/src/ui/window/pdf_dialogs.rs +++ b/src/ui/window/pdf_dialogs.rs @@ -111,6 +111,18 @@ pub enum PdfDialogMsg { ImportOk, Options, Help(&'static str), + // View background + BgKind(BgKind), + /// Open the colour window for a slot: 0 solid, 1 top, 2 middle, 3 bottom. + BgPickColor(u8), + BgColor(u8, [u8; 3]), + BgThree(bool), + BgRotation(String), + BgImage(String), + BgBrowse, + BgPosition(BgPosition), + BgAspect(bool), + BgApply, } fn msg(m: PdfDialogMsg) -> Message { @@ -2131,3 +2143,285 @@ pub fn view_pc_section<'a>( .width(sizing.width) .into() } + +// ── View background (-VIEW sEttings Background) ───────────────────────── + +/// What a named view shows behind the drawing. +#[derive(Debug, Clone, Copy, PartialEq, Eq)] +pub enum BgKind { + None, + Solid, + Gradient, + Image, +} + +/// Where an image background sits: centred, stretched over the view, or +/// repeated. +#[derive(Debug, Clone, Copy, PartialEq, Eq)] +pub enum BgPosition { + Center, + Stretch, + Tile, +} + +impl BgPosition { + pub const ALL: [BgPosition; 3] = [BgPosition::Center, BgPosition::Stretch, BgPosition::Tile]; +} + +impl fmt::Display for BgPosition { + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { + f.write_str(&match self { + BgPosition::Center => crate::tr!("view-background", "center"), + BgPosition::Stretch => crate::tr!("view-background", "stretch"), + BgPosition::Tile => crate::tr!("view-background", "tile"), + }) + } +} + +/// How an image background is scaled: to the screen, or keeping its +/// aspect ratio. +#[derive(Debug, Clone, Copy, PartialEq, Eq)] +pub struct BgAspect(pub bool); + +impl fmt::Display for BgAspect { + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { + f.write_str(&if self.0 { + crate::tr!("view-background", "aspect") + } else { + crate::tr!("view-background", "fit") + }) + } +} + +/// The Background dialog's fields for one named view. +pub struct ViewBackgroundState { + pub view: String, + pub kind: BgKind, + pub solid: [u8; 3], + pub top: [u8; 3], + pub middle: [u8; 3], + pub bottom: [u8; 3], + pub three: bool, + pub rotation: String, + pub image: String, + pub position: BgPosition, + pub aspect: bool, + /// The drawing area colour, shown for no background. + pub canvas: [u8; 3], +} + +pub fn view_background<'a>( + state: &'a ViewBackgroundState, + sizing: crate::ui::modal::ModalSizing, +) -> Element<'a, Message> { + let kind = segmented( + vec![ + (BgKind::None, crate::tr!("view-background", "none")), + (BgKind::Solid, crate::tr!("view-background", "solid")), + (BgKind::Gradient, crate::tr!("view-background", "gradient")), + (BgKind::Image, crate::tr!("view-background", "image")), + ], + state.kind, + PdfDialogMsg::BgKind, + ); + let rgb = |c: [u8; 3]| iced::Color::from_rgb8(c[0], c[1], c[2]); + let fill = |background: Background| { + move |_: &Theme| container::Style { + background: Some(background), + border: Border { + width: 1.0, + radius: 4.0.into(), + color: iced::Color::from_rgb8(0x4a, 0x4a, 0x4a), + }, + ..Default::default() + } + }; + let note = |label: String| text(label).size(11).style(muted_style); + let preview: Element<'a, Message> = match state.kind { + BgKind::None => container(note(crate::tr!("view-background", "canvas"))) + .center(Fill) + .style(fill(Background::Color(rgb(state.canvas)))) + .into(), + BgKind::Solid => container(Space::new()) + .width(Fill) + .height(Fill) + .style(fill(Background::Color(rgb(state.solid)))) + .into(), + BgKind::Gradient => { + // Bottom colour at the start, top at the end, turned + // counter-clockwise by the rotation. + let angle = state.rotation.trim().parse::().unwrap_or(0.0); + let mut linear = iced::gradient::Linear::new(iced::Radians(-angle.to_radians())) + .add_stop(0.0, rgb(state.bottom)); + if state.three { + linear = linear.add_stop(0.5, rgb(state.middle)); + } + linear = linear.add_stop(1.0, rgb(state.top)); + container(Space::new()) + .width(Fill) + .height(Fill) + .style(fill(Background::Gradient(iced::Gradient::Linear(linear)))) + .into() + } + BgKind::Image => { + if std::path::Path::new(state.image.trim()).is_file() { + let fit = match (state.position, state.aspect) { + (BgPosition::Stretch, false) => iced::ContentFit::Fill, + (BgPosition::Stretch, true) => iced::ContentFit::Contain, + _ => iced::ContentFit::None, + }; + container( + image(image::Handle::from_path(state.image.trim())) + .content_fit(fit) + .width(Fill) + .height(Fill), + ) + .center(Fill) + .style(fill(Background::Color(rgb(state.canvas)))) + .into() + } else { + container(note(crate::tr!("view-background", "no-image"))) + .center(Fill) + .style(fill(Background::Color(iced::Color::from_rgb8(0x1a, 0x1a, 0x1a)))) + .into() + } + } + }; + let preview = card( + crate::tr!("view-background", "preview"), + container(preview).width(Fill).height(Length::Fixed(150.0)), + ); + let label = |value: String, enabled: bool| { + text(value) + .size(11) + .style(move |theme: &Theme| { + if enabled { + muted_style(theme) + } else { + text::Style { + color: Some(theme.palette().background.strong.color), + } + } + }) + .width(Length::Fixed(92.0)) + }; + let color_row = |name: String, slot: u8, color: [u8; 3], enabled: bool| -> Element<'a, Message> { + let mut pick = button(text(crate::tr!("view-background", "select")).size(11)) + .style(button_style(false)) + .padding([4, 10]); + if enabled { + pick = pick.on_press(msg(PdfDialogMsg::BgPickColor(slot))); + } + row![ + label(name, enabled), + swatch(color, 64.0, 22.0), + text(format!("{},{},{}", color[0], color[1], color[2])) + .size(11) + .style(muted_style), + Space::new().width(Fill), + pick, + ] + .spacing(8) + .align_y(iced::Center) + .into() + }; + let options: Element<'a, Message> = match state.kind { + BgKind::None => card( + crate::tr!("view-background", "info"), + note(crate::tr!("view-background", "none-info")), + ), + BgKind::Solid => card( + crate::tr!("view-background", "solid-color"), + color_row(crate::tr!("view-background", "color"), 0, state.solid, true), + ), + BgKind::Gradient => card( + crate::tr!("view-background", "gradient"), + column![ + row![ + chip( + crate::tr!("view-background", "three-colors"), + state.three, + msg(PdfDialogMsg::BgThree(!state.three)) + ), + Space::new().width(Fill), + text(crate::tr!("view-background", "rotation")) + .size(11) + .style(muted_style), + text_input("", &state.rotation) + .on_input(|v| msg(PdfDialogMsg::BgRotation(v))) + .size(11) + .padding([4, 8]) + .width(Length::Fixed(64.0)) + .style(field_style), + ] + .spacing(8) + .align_y(iced::Center), + color_row(crate::tr!("view-background", "top"), 1, state.top, true), + color_row(crate::tr!("view-background", "middle"), 2, state.middle, state.three), + color_row(crate::tr!("view-background", "bottom"), 3, state.bottom, true), + ] + .spacing(8), + ), + BgKind::Image => card( + crate::tr!("view-background", "image"), + column![ + row![ + text_input("", &state.image) + .on_input(|v| msg(PdfDialogMsg::BgImage(v))) + .size(11) + .padding([4, 8]) + .width(Fill) + .style(field_style), + button(text(crate::tr!("view-background", "browse")).size(11)) + .on_press(msg(PdfDialogMsg::BgBrowse)) + .style(button_style(false)) + .padding([4, 10]), + ] + .spacing(8) + .align_y(iced::Center), + row![ + label(crate::tr!("view-background", "position"), true), + pick_list(Some(state.position), BgPosition::ALL.to_vec(), |p: &BgPosition| { + p.to_string() + }) + .on_select(|p| msg(PdfDialogMsg::BgPosition(p))) + .text_size(12) + .padding([4, 8]) + .width(Fill), + ] + .spacing(8) + .align_y(iced::Center), + row![ + label(crate::tr!("view-background", "scale"), true), + pick_list( + Some(BgAspect(state.aspect)), + vec![BgAspect(false), BgAspect(true)], + |a: &BgAspect| a.to_string() + ) + .on_select(|a: BgAspect| msg(PdfDialogMsg::BgAspect(a.0))) + .text_size(12) + .padding([4, 8]) + .width(Fill), + ] + .spacing(8) + .align_y(iced::Center), + ] + .spacing(8), + ), + }; + column![ + kind, + preview, + options, + footer( + "background", + None, + crate::tr!("view-background", "apply").into(), + PdfDialogMsg::BgApply + ), + ] + .spacing(10) + .padding([10, 12]) + .width(sizing.width) + .into() +}