forked from 1b-agent/opencadstudio-upstream
Merge pull request #1722 from ramox81/codex/view-background
# Conflicts: # locales/ar-SA/opencadstudio.ftl # locales/bg-BG/opencadstudio.ftl # locales/cs-CZ/opencadstudio.ftl # locales/de-DE/opencadstudio.ftl # locales/el-GR/opencadstudio.ftl # locales/en-US/opencadstudio.ftl # locales/es-ES/opencadstudio.ftl # locales/fi-FI/opencadstudio.ftl # locales/fr-FR/opencadstudio.ftl # locales/hi-IN/opencadstudio.ftl # locales/hu-HU/opencadstudio.ftl # locales/it-IT/opencadstudio.ftl # locales/ja-JP/opencadstudio.ftl # locales/ko-KR/opencadstudio.ftl # locales/lo-LA/opencadstudio.ftl # locales/nl-NL/opencadstudio.ftl # locales/pl-PL/opencadstudio.ftl # locales/pt-BR/opencadstudio.ftl # locales/ru-RU/opencadstudio.ftl # locales/tr-TR/opencadstudio.ftl # locales/zh-CN/opencadstudio.ftl # locales/zh-TW/opencadstudio.ftl
This commit is contained in:
commit
03445a540d
40 files changed
+1801
-46
No files matched your search
@@ -6323,3 +6323,35 @@ property-patch =
|
||||
.patch-05 = Open 3Dconnexion settings...
|
||||
.patch-06 = Decimal Separator
|
||||
.patch-07 = Operational Symbols
|
||||
|
||||
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
|
||||
@@ -6311,3 +6311,35 @@ property-patch =
|
||||
.patch-05 = Open 3Dconnexion settings...
|
||||
.patch-06 = Decimal Separator
|
||||
.patch-07 = Operational Symbols
|
||||
|
||||
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
|
||||
@@ -6308,3 +6308,35 @@ property-patch =
|
||||
.patch-05 = Open 3Dconnexion settings...
|
||||
.patch-06 = Decimal Separator
|
||||
.patch-07 = Operational Symbols
|
||||
|
||||
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
|
||||
@@ -6309,3 +6309,35 @@ property-patch =
|
||||
.patch-05 = Open 3Dconnexion settings...
|
||||
.patch-06 = Decimal Separator
|
||||
.patch-07 = Operational Symbols
|
||||
|
||||
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
|
||||
@@ -6311,3 +6311,35 @@ property-patch =
|
||||
.patch-05 = Open 3Dconnexion settings...
|
||||
.patch-06 = Decimal Separator
|
||||
.patch-07 = Operational Symbols
|
||||
|
||||
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
|
||||
@@ -6310,3 +6310,35 @@ property-patch =
|
||||
.patch-05 = Open 3Dconnexion settings...
|
||||
.patch-06 = Decimal Separator
|
||||
.patch-07 = Operational Symbols
|
||||
|
||||
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
|
||||
@@ -6311,3 +6311,35 @@ property-patch =
|
||||
.patch-05 = Open 3Dconnexion settings...
|
||||
.patch-06 = Decimal Separator
|
||||
.patch-07 = Operational Symbols
|
||||
|
||||
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
|
||||
@@ -6308,3 +6308,35 @@ property-patch =
|
||||
.patch-05 = Open 3Dconnexion settings...
|
||||
.patch-06 = Decimal Separator
|
||||
.patch-07 = Operational Symbols
|
||||
|
||||
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
|
||||
@@ -6311,3 +6311,35 @@ property-patch =
|
||||
.patch-05 = Open 3Dconnexion settings...
|
||||
.patch-06 = Decimal Separator
|
||||
.patch-07 = Operational Symbols
|
||||
|
||||
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
|
||||
@@ -6299,3 +6299,35 @@ property-patch =
|
||||
.patch-05 = Open 3Dconnexion settings...
|
||||
.patch-06 = Decimal Separator
|
||||
.patch-07 = Operational Symbols
|
||||
|
||||
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
|
||||
@@ -6295,3 +6295,35 @@ property-patch =
|
||||
.patch-05 = 3Dconnexion-beállítások megnyitása…
|
||||
.patch-06 = Tizedesjel szerint
|
||||
.patch-07 = Műveleti jelek szerint
|
||||
|
||||
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
|
||||
@@ -6310,3 +6310,35 @@ property-patch =
|
||||
.patch-05 = Open 3Dconnexion settings...
|
||||
.patch-06 = Decimal Separator
|
||||
.patch-07 = Operational Symbols
|
||||
|
||||
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
|
||||
@@ -6299,3 +6299,35 @@ property-patch =
|
||||
.patch-05 = Open 3Dconnexion settings...
|
||||
.patch-06 = Decimal Separator
|
||||
.patch-07 = Operational Symbols
|
||||
|
||||
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
|
||||
@@ -6310,3 +6310,35 @@ property-patch =
|
||||
.patch-05 = Open 3Dconnexion settings...
|
||||
.patch-06 = Decimal Separator
|
||||
.patch-07 = Operational Symbols
|
||||
|
||||
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
|
||||
@@ -6311,3 +6311,35 @@ property-patch =
|
||||
.patch-05 = Open 3Dconnexion settings...
|
||||
.patch-06 = Decimal Separator
|
||||
.patch-07 = Operational Symbols
|
||||
|
||||
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
|
||||
@@ -6309,3 +6309,35 @@ property-patch =
|
||||
.patch-05 = Open 3Dconnexion settings...
|
||||
.patch-06 = Decimal Separator
|
||||
.patch-07 = Operational Symbols
|
||||
|
||||
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
|
||||
@@ -6310,3 +6310,35 @@ property-patch =
|
||||
.patch-05 = Open 3Dconnexion settings...
|
||||
.patch-06 = Decimal Separator
|
||||
.patch-07 = Operational Symbols
|
||||
|
||||
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
|
||||
@@ -6311,3 +6311,35 @@ property-patch =
|
||||
.patch-05 = Open 3Dconnexion settings...
|
||||
.patch-06 = Decimal Separator
|
||||
.patch-07 = Operational Symbols
|
||||
|
||||
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
|
||||
@@ -6312,3 +6312,35 @@ property-patch =
|
||||
.patch-05 = Open 3Dconnexion settings...
|
||||
.patch-06 = Decimal Separator
|
||||
.patch-07 = Operational Symbols
|
||||
|
||||
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
|
||||
@@ -6232,3 +6232,35 @@ property-patch =
|
||||
.patch-05 = Open 3Dconnexion settings...
|
||||
.patch-06 = Decimal Separator
|
||||
.patch-07 = Operational Symbols
|
||||
|
||||
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ç
|
||||
@@ -6294,3 +6294,35 @@ property-patch =
|
||||
.patch-05 = Open 3Dconnexion settings...
|
||||
.patch-06 = Decimal Separator
|
||||
.patch-07 = Operational Symbols
|
||||
|
||||
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
|
||||
@@ -6306,3 +6306,35 @@ property-patch =
|
||||
.patch-05 = Open 3Dconnexion settings...
|
||||
.patch-06 = Decimal Separator
|
||||
.patch-07 = Operational Symbols
|
||||
|
||||
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
|
||||
@@ -891,7 +891,8 @@ impl OpenCADStudio {
|
||||
self.command_line
|
||||
.push_error(crate::t!("Usage: VIEW DELETE <name>").as_ref());
|
||||
} else {
|
||||
if self.tabs[i].scene.document.views.remove(&name).is_some() {
|
||||
if let Some(view) = self.tabs[i].scene.document.views.remove(&name) {
|
||||
crate::scene::erase_background(&mut self.tabs[i].scene.document, view.background_handle);
|
||||
self.command_line
|
||||
.push_output(crate::tf!("View '{}' deleted.", name).as_ref());
|
||||
} else {
|
||||
@@ -909,6 +910,7 @@ impl OpenCADStudio {
|
||||
let found = self.tabs[i].scene.document.views.get(&name).cloned();
|
||||
if let Some(v) = found {
|
||||
self.tabs[i].scene.restore_named_view(&v);
|
||||
self.apply_view_render_mode(i, &v);
|
||||
self.command_line.push_output(
|
||||
crate::tf!("View '{}' restored.", v.name).as_ref(),
|
||||
);
|
||||
@@ -932,6 +934,7 @@ impl OpenCADStudio {
|
||||
let found = self.tabs[i].scene.document.views.get(&name).cloned();
|
||||
if let Some(v) = found {
|
||||
self.tabs[i].scene.restore_named_view(&v);
|
||||
self.apply_view_render_mode(i, &v);
|
||||
self.command_line
|
||||
.push_output(crate::tf!("View '{}' restored.", v.name).as_ref());
|
||||
} else {
|
||||
|
||||
@@ -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(_)
|
||||
|
||||
@@ -53,12 +53,33 @@ impl OpenCADStudio {
|
||||
.to_string(),
|
||||
visual_style: name_of(&visual_styles, view.visual_style_handle),
|
||||
live_section: name_of(&live_sections, view.live_section_handle),
|
||||
paper_space: view.paper_space,
|
||||
})
|
||||
.collect();
|
||||
let scene = &self.tabs[i].scene;
|
||||
let sheet_viewports = (scene.current_layout != "Model" && scene.active_viewport.is_none()).then(|| {
|
||||
scene
|
||||
.layout_content_viewports()
|
||||
.iter()
|
||||
.filter_map(|handle| match document.get_entity(*handle) {
|
||||
Some(codec::EntityType::Viewport(value)) => Some((
|
||||
handle.value(),
|
||||
[
|
||||
value.center.x - value.width / 2.0,
|
||||
value.center.y - value.height / 2.0,
|
||||
value.center.x + value.width / 2.0,
|
||||
value.center.y + value.height / 2.0,
|
||||
],
|
||||
)),
|
||||
_ => None,
|
||||
})
|
||||
.collect()
|
||||
});
|
||||
ViewNames {
|
||||
views,
|
||||
visual_styles: visual_styles.into_iter().map(|(name, _)| name).collect(),
|
||||
live_sections: live_sections.into_iter().map(|(name, _)| name).collect(),
|
||||
sheet_viewports,
|
||||
}
|
||||
}
|
||||
|
||||
@@ -111,7 +132,7 @@ impl OpenCADStudio {
|
||||
}
|
||||
|
||||
/// Carry out what -VIEW gathered (`-VIEW >VERB args`).
|
||||
pub(super) fn run_dash_view(&mut self, rest: &str, i: usize) -> Task<Message> {
|
||||
pub(crate) fn run_dash_view(&mut self, rest: &str, i: usize) -> Task<Message> {
|
||||
let (verb, arg) = rest.split_once(' ').unwrap_or((rest, ""));
|
||||
let mut settings_again = false;
|
||||
match verb {
|
||||
@@ -132,18 +153,43 @@ impl OpenCADStudio {
|
||||
if !doomed.is_empty() {
|
||||
self.push_undo_snapshot(i, "-VIEW");
|
||||
for name in &doomed {
|
||||
self.tabs[i].scene.document.views.remove(name);
|
||||
let removed = self.tabs[i].scene.document.views.remove(name);
|
||||
if let Some(view) = removed {
|
||||
crate::scene::erase_background(&mut self.tabs[i].scene.document, view.background_handle);
|
||||
}
|
||||
self.tabs[i].scene.document.delete_layer_state(&snapshot_name(name));
|
||||
}
|
||||
self.tabs[i].dirty = true;
|
||||
}
|
||||
}
|
||||
"RESTOREVP" => {
|
||||
let (name, handle) = arg.split_once('\u{1f}').unwrap_or((arg, ""));
|
||||
let Some(view) = self.tabs[i].scene.document.views.get(name).cloned() else {
|
||||
return Task::none();
|
||||
};
|
||||
let Some(handle) = u64::from_str_radix(handle, 16).ok().map(Handle::new) else {
|
||||
return Task::none();
|
||||
};
|
||||
// The reference enters the selected viewport and restores the
|
||||
// view in it.
|
||||
let task = self.update(Message::EnterViewport(handle));
|
||||
self.restore_view_in_active_viewport(i, &view);
|
||||
return task;
|
||||
}
|
||||
"RESTORE" => {
|
||||
let Some(view) = self.tabs[i].scene.document.views.get(arg).cloned() else {
|
||||
return Task::none();
|
||||
};
|
||||
if self.tabs[i].scene.current_layout != "Model"
|
||||
&& self.tabs[i].scene.active_viewport.is_some()
|
||||
&& !view.paper_space
|
||||
{
|
||||
self.restore_view_in_active_viewport(i, &view);
|
||||
return Task::none();
|
||||
}
|
||||
let before = self.tabs[i].scene.active_gaze_dir();
|
||||
self.tabs[i].scene.restore_named_view(&view);
|
||||
self.apply_view_render_mode(i, &view);
|
||||
if view.ucs_associated && self.tabs[i].active_block_edit.is_none() {
|
||||
let world = view.ucs_x_axis == Vector3::new(1.0, 0.0, 0.0)
|
||||
&& view.ucs_y_axis == Vector3::new(0.0, 1.0, 0.0)
|
||||
@@ -197,6 +243,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();
|
||||
@@ -241,18 +295,42 @@ impl OpenCADStudio {
|
||||
}
|
||||
}
|
||||
|
||||
/// A model-space view restored in the entered paper-space viewport, with
|
||||
/// its background and visual style.
|
||||
fn restore_view_in_active_viewport(&mut self, i: usize, view: &codec::tables::View) {
|
||||
let Some(viewport) = self.tabs[i].scene.active_viewport else { return };
|
||||
self.tabs[i].scene.restore_named_view_in_viewport(view, viewport);
|
||||
if let Some(ObjectType::VisualStyle(style)) =
|
||||
self.tabs[i].scene.document.objects.get(&view.visual_style_handle)
|
||||
{
|
||||
if let Some((mode, _)) = crate::modules::view::visual_style::mode_for_style_name(&style.description) {
|
||||
self.tabs[i].scene.set_active_viewport_render_mode(mode);
|
||||
self.tabs[i].scene.bump_geometry_no_blocks();
|
||||
}
|
||||
}
|
||||
self.tabs[i].dirty = true;
|
||||
}
|
||||
|
||||
/// Add or replace a named view, keeping the record (and so its settings)
|
||||
/// 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);
|
||||
let mut replaced = Handle::NULL;
|
||||
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;
|
||||
}
|
||||
replaced = old.background_handle;
|
||||
}
|
||||
None => {
|
||||
view.handle = document.allocate_handle();
|
||||
@@ -265,6 +343,7 @@ impl OpenCADStudio {
|
||||
}
|
||||
}
|
||||
document.views.add_or_replace(view);
|
||||
crate::scene::erase_background(document, replaced);
|
||||
self.tabs[i].dirty = true;
|
||||
}
|
||||
|
||||
@@ -275,6 +354,7 @@ impl OpenCADStudio {
|
||||
};
|
||||
self.push_undo_snapshot(i, "-VIEW");
|
||||
let document = &mut self.tabs[i].scene.document;
|
||||
let mut replaced = Handle::NULL;
|
||||
if view.handle.is_null() {
|
||||
view.handle = document.allocate_handle();
|
||||
}
|
||||
@@ -282,7 +362,7 @@ impl OpenCADStudio {
|
||||
list.into_iter().find(|(entry, _)| entry.eq_ignore_ascii_case(value)).map_or(Handle::NULL, |(_, h)| h)
|
||||
};
|
||||
match setting {
|
||||
"BACKGROUND" => view.background_handle = Handle::NULL,
|
||||
"BACKGROUND" => replaced = std::mem::replace(&mut view.background_handle, Handle::NULL),
|
||||
"VISUALSTYLE" => view.visual_style_handle = find(crate::io::visual_styles::offered(document)),
|
||||
"SECTION" => {
|
||||
let sections: Vec<(String, Handle)> = document
|
||||
@@ -336,6 +416,224 @@ impl OpenCADStudio {
|
||||
_ => {}
|
||||
}
|
||||
document.views.add_or_replace(view);
|
||||
crate::scene::erase_background(document, replaced);
|
||||
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,
|
||||
offset: [0.0, 0.0],
|
||||
scale: [1.0, 1.0],
|
||||
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;
|
||||
state.offset = [value.offset.x, value.offset.y];
|
||||
state.scale = [value.scale.x, value.scale.y];
|
||||
}
|
||||
_ => {}
|
||||
}
|
||||
}
|
||||
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<Message> {
|
||||
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::<f64>()
|
||||
.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(state.offset[0], state.offset[1]),
|
||||
scale: codec::types::Vector2::new(state.scale[0], state.scale[1]),
|
||||
}))
|
||||
}
|
||||
};
|
||||
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) {
|
||||
let old = std::mem::replace(&mut view.background_handle, handle);
|
||||
crate::scene::erase_background(document, old);
|
||||
}
|
||||
self.tabs[i].dirty = true;
|
||||
}
|
||||
return self.run_dash_view("SETTINGS", i);
|
||||
}
|
||||
_ => {}
|
||||
}
|
||||
Task::none()
|
||||
}
|
||||
}
|
||||
|
||||
impl OpenCADStudio {
|
||||
/// A restored view's visual style becomes the model tab's render mode
|
||||
/// (the closest of OCS's modes); a view without one leaves it.
|
||||
pub(crate) fn apply_view_render_mode(&mut self, i: usize, view: &codec::tables::View) {
|
||||
if self.tabs[i].scene.current_layout != "Model" {
|
||||
return;
|
||||
}
|
||||
let Some(ObjectType::VisualStyle(style)) =
|
||||
self.tabs[i].scene.document.objects.get(&view.visual_style_handle)
|
||||
else {
|
||||
return;
|
||||
};
|
||||
use crate::modules::view::visual_style;
|
||||
let Some((mode, named)) = visual_style::mode_for_style_name(&style.description) else {
|
||||
return;
|
||||
};
|
||||
// A style of its own keeps its name; the render modes keep theirs.
|
||||
let label = if named { style.description.clone() } else { visual_style::label_for(mode).to_string() };
|
||||
self.apply_render_mode(i, mode, &label);
|
||||
}
|
||||
}
|
||||
@@ -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=<path>", "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<u8> = value
|
||||
.split(',')
|
||||
.map(|part| part.trim().parse::<u8>())
|
||||
.collect::<Result<_, _>>()
|
||||
.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 {
|
||||
|
||||
@@ -567,6 +567,8 @@ pub(super) struct OpenCADStudio {
|
||||
field_dialog: Option<crate::ui::window::field_dialog::FieldDialogState>,
|
||||
point_cloud_color_map: Option<crate::ui::window::pdf_dialogs::PointCloudColorMapState>,
|
||||
pc_section: Option<crate::ui::window::pdf_dialogs::PcSectionState>,
|
||||
/// -VIEW sEttings Background dialog.
|
||||
view_background: Option<crate::ui::window::pdf_dialogs::ViewBackgroundState>,
|
||||
underlay_layers: Option<crate::ui::window::pdf_dialogs::UnderlayLayersState>,
|
||||
pdf_import_settings: Option<crate::modules::insert::pdf_import::PdfImportSettings>,
|
||||
pdf_import_file: Option<crate::ui::window::pdf_dialogs::PdfImportFileState>,
|
||||
@@ -1775,6 +1777,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
|
||||
@@ -2115,6 +2119,7 @@ pub enum ModalKind {
|
||||
PointCloudAttach,
|
||||
PointCloudColorMap,
|
||||
PcSection,
|
||||
ViewBackground,
|
||||
UnderlayLayers,
|
||||
PdfImportSettings,
|
||||
PdfImportFile,
|
||||
@@ -4494,6 +4499,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,
|
||||
|
||||
@@ -9315,6 +9315,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();
|
||||
|
||||
@@ -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)),
|
||||
),
|
||||
|
||||
@@ -1090,6 +1090,28 @@ impl OpenCADStudio {
|
||||
.push_output(crate::tf!("Viewport visual style: {label}").as_ref());
|
||||
return Task::none();
|
||||
}
|
||||
self.apply_render_mode(i, mode, label);
|
||||
// The viewport record carries the style the mode is saved as.
|
||||
self.tabs[i]
|
||||
.scene
|
||||
.set_active_visual_style_named(crate::modules::view::visual_style::style_name_for(mode));
|
||||
self.command_line
|
||||
.push_output(crate::tf!("Visual style: {label}").as_ref());
|
||||
// The point cloud move gizmo follows the visual style.
|
||||
if i == self.active_tab {
|
||||
self.refresh_selected_grips();
|
||||
}
|
||||
Task::none()
|
||||
}
|
||||
|
||||
/// The model tab's visual style, without a message.
|
||||
pub(crate) fn apply_render_mode(
|
||||
&mut self,
|
||||
i: usize,
|
||||
mode: codec::entities::ViewportRenderMode,
|
||||
label: &str,
|
||||
) {
|
||||
use codec::entities::ViewportRenderMode as M;
|
||||
self.tabs[i].render_mode = mode;
|
||||
// Write the style onto the active Model tile alone so it
|
||||
// sticks when that tile loses focus and the other tiles keep
|
||||
@@ -1105,13 +1127,6 @@ impl OpenCADStudio {
|
||||
// pipeline keys its upload cache off `geometry_epoch`.
|
||||
self.tabs[i].scene.bump_geometry_no_blocks();
|
||||
self.tabs[i].dirty = true;
|
||||
self.command_line
|
||||
.push_output(crate::tf!("Visual style: {label}").as_ref());
|
||||
// The point cloud move gizmo follows the visual style.
|
||||
if i == self.active_tab {
|
||||
self.refresh_selected_grips();
|
||||
}
|
||||
Task::none()
|
||||
}
|
||||
|
||||
pub(super) fn on_cursor_moved(
|
||||
|
||||
@@ -30,6 +30,7 @@ pub(super) fn viewport_controls<'a>(
|
||||
tile_count: usize,
|
||||
render_mode_menu_open: bool,
|
||||
render_mode_preview: Option<codec::entities::ViewportRenderMode>,
|
||||
style_name: Option<String>,
|
||||
) -> Element<'a, Message> {
|
||||
let render_modes: Vec<RenderModeChoice> = crate::modules::view::visual_style::VISUAL_STYLES
|
||||
.iter()
|
||||
@@ -98,7 +99,7 @@ pub(super) fn viewport_controls<'a>(
|
||||
// flyout keeps the current style unchanged while hovering; a click commits.
|
||||
let picker_head = button(
|
||||
row![
|
||||
text(RenderModeChoice(render_mode).to_string()).size(11),
|
||||
text(style_name.unwrap_or_else(|| RenderModeChoice(render_mode).to_string())).size(11),
|
||||
crate::ui::icons::themed_arrow_toggle(render_mode_menu_open, 9.0),
|
||||
]
|
||||
.spacing(6)
|
||||
@@ -379,7 +380,7 @@ mod tests {
|
||||
#[test]
|
||||
fn test_viewport_controls_construction() {
|
||||
for style in crate::modules::view::visual_style::VISUAL_STYLES {
|
||||
let _el = viewport_controls(style.mode, true, true, true, 1, false, None);
|
||||
let _el = viewport_controls(style.mode, true, true, true, 1, false, None, None);
|
||||
let _el_open = viewport_controls(
|
||||
style.mode,
|
||||
false,
|
||||
@@ -388,6 +389,7 @@ mod tests {
|
||||
2,
|
||||
true,
|
||||
Some(M::Wireframe3D),
|
||||
None,
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1297,6 +1297,18 @@ bg={bg_ms:.1}ms n={view_count}"
|
||||
let rect = tab.scene.active_model_tile_bounds(vw, vh);
|
||||
// Unified control chip: split buttons + render-mode picker +
|
||||
// grid / grid-snap toggles, for the active Model tile.
|
||||
// A named style (Realistic, …) the tile draws as shows by name.
|
||||
let style_name = match tab.scene.document.objects.get(&tab.scene.active_visual_style()) {
|
||||
Some(codec::objects::ObjectType::VisualStyle(style)) => {
|
||||
match crate::modules::view::visual_style::mode_for_style_name(&style.description) {
|
||||
Some((mode, true)) if mode == tab.render_mode => {
|
||||
Some(crate::t!(style.description.as_str()).into_owned())
|
||||
}
|
||||
_ => None,
|
||||
}
|
||||
}
|
||||
_ => None,
|
||||
};
|
||||
let bar = viewport_controls(
|
||||
tab.render_mode,
|
||||
self.show_grid,
|
||||
@@ -1305,6 +1317,7 @@ bg={bg_ms:.1}ms n={view_count}"
|
||||
tab.scene.model_tiles.borrow().len(),
|
||||
self.render_mode_menu_open,
|
||||
self.render_mode_preview,
|
||||
style_name,
|
||||
);
|
||||
// Adaptive: DensitySwap measures the bar's real width every frame
|
||||
// (reported into `render_bar_w`, which the ViewCube reads to decide
|
||||
@@ -1397,6 +1410,7 @@ bg={bg_ms:.1}ms n={view_count}"
|
||||
0,
|
||||
self.render_mode_menu_open,
|
||||
self.render_mode_preview,
|
||||
None,
|
||||
);
|
||||
let adaptive: Element<'_, Message> = DensitySwap::new(vec![
|
||||
iced::widget::opaque(bar),
|
||||
|
||||
@@ -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(),
|
||||
@@ -512,6 +517,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| {
|
||||
|
||||
@@ -57,6 +57,8 @@ pub struct ViewInfo {
|
||||
pub category: String,
|
||||
pub visual_style: Option<String>,
|
||||
pub live_section: Option<String>,
|
||||
/// Saved in paper space.
|
||||
pub paper_space: bool,
|
||||
}
|
||||
|
||||
/// What -VIEW checks typed names against.
|
||||
@@ -65,6 +67,10 @@ pub struct ViewNames {
|
||||
pub views: Vec<ViewInfo>,
|
||||
pub visual_styles: Vec<String>,
|
||||
pub live_sections: Vec<String>,
|
||||
/// On a layout's sheet (no viewport entered): the layout's viewports as
|
||||
/// (handle, [min x, min y, max x, max y]), one of which takes a
|
||||
/// model-space view being restored.
|
||||
pub sheet_viewports: Option<Vec<(u64, [f64; 4])>>,
|
||||
}
|
||||
|
||||
#[derive(Clone, Copy, PartialEq, Eq, Debug)]
|
||||
@@ -106,6 +112,7 @@ enum Step {
|
||||
List,
|
||||
Delete,
|
||||
Restore,
|
||||
RestoreViewport(String),
|
||||
Save,
|
||||
WindowName,
|
||||
WindowFirst(String),
|
||||
@@ -226,9 +233,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())
|
||||
@@ -343,6 +357,7 @@ impl CadCommand for DashViewCommand {
|
||||
Step::List | Step::SettingViewList(_) => "Enter view name(s) to list <*>:".into(),
|
||||
Step::Delete => "Enter view name(s) to delete:".into(),
|
||||
Step::Restore => "Enter view name to restore:".into(),
|
||||
Step::RestoreViewport(_) => "Select Viewport for view:".into(),
|
||||
Step::Save | Step::WindowName => "Enter view name to save:".into(),
|
||||
Step::WindowFirst(_) => "Specify first corner:".into(),
|
||||
Step::WindowSecond(..) => "Specify opposite corner:".into(),
|
||||
@@ -414,9 +429,19 @@ impl CadCommand for DashViewCommand {
|
||||
}
|
||||
}
|
||||
Step::Restore => match self.view(t) {
|
||||
// On a sheet a model-space view goes into a viewport the
|
||||
// user selects.
|
||||
Some(view) if !view.paper_space && self.names.sheet_viewports.is_some() => {
|
||||
self.step = Step::RestoreViewport(view.name.clone());
|
||||
CmdResult::ReportMeasurement("Restoring Model space view.".into())
|
||||
}
|
||||
Some(view) => CmdResult::Dispatch(format!("-VIEW >RESTORE {}", view.name)),
|
||||
None => Self::end(&format!("Cannot find view \"{t}\".")),
|
||||
},
|
||||
Step::RestoreViewport(name) => {
|
||||
self.step = Step::RestoreViewport(name);
|
||||
CmdResult::ReportError("*Invalid selection*".into())
|
||||
}
|
||||
Step::Save => CmdResult::Dispatch(format!("-VIEW >SAVE {t}")),
|
||||
Step::WindowName => {
|
||||
self.step = Step::WindowFirst(t.to_string());
|
||||
@@ -487,6 +512,19 @@ impl CadCommand for DashViewCommand {
|
||||
"-VIEW >WINDOW {}\u{1f}{},{}\u{1f}{},{}",
|
||||
name, first.x, first.y, pt.x, pt.y
|
||||
)),
|
||||
Step::RestoreViewport(name) => {
|
||||
// The topmost viewport under the pick.
|
||||
let hit = self.names.sheet_viewports.iter().flatten().rev().find(|(_, [x0, y0, x1, y1])| {
|
||||
pt.x >= *x0 && pt.x <= *x1 && pt.y >= *y0 && pt.y <= *y1
|
||||
});
|
||||
match hit {
|
||||
Some((handle, _)) => CmdResult::Dispatch(format!("-VIEW >RESTOREVP {name}\u{1f}{handle:X}")),
|
||||
None => {
|
||||
self.step = Step::RestoreViewport(name);
|
||||
CmdResult::NeedPoint
|
||||
}
|
||||
}
|
||||
}
|
||||
step => {
|
||||
self.step = step;
|
||||
CmdResult::NeedPoint
|
||||
@@ -496,7 +534,7 @@ impl CadCommand for DashViewCommand {
|
||||
|
||||
fn on_enter(&mut self) -> CmdResult {
|
||||
match std::mem::replace(&mut self.step, Step::Main) {
|
||||
Step::Main | Step::Delete | Step::Restore | Step::Save | Step::WindowName => {
|
||||
Step::Main | Step::Delete | Step::Restore | Step::RestoreViewport(_) | Step::Save | Step::WindowName => {
|
||||
CmdResult::Dispatch("-VIEW >END".into())
|
||||
}
|
||||
Step::Orthographic => CmdResult::Dispatch("-VIEW >PRESET TOP".into()),
|
||||
|
||||
@@ -106,6 +106,38 @@ pub fn label_for(mode: Mode) -> &'static str {
|
||||
.unwrap_or("Wireframe 2D")
|
||||
}
|
||||
|
||||
/// The render mode a named visual style draws as, and whether the name is a
|
||||
/// style of its own (Realistic, Conceptual, …) rather than one of the modes.
|
||||
pub fn mode_for_style_name(name: &str) -> Option<(Mode, bool)> {
|
||||
Some(match name {
|
||||
"2dWireframe" => (Mode::Wireframe2D, false),
|
||||
"Wireframe" => (Mode::Wireframe3D, false),
|
||||
"Hidden" => (Mode::HiddenLine, false),
|
||||
"Flat" => (Mode::FlatShaded, false),
|
||||
"FlatWithEdges" => (Mode::FlatShadedWithEdges, false),
|
||||
"Gouraud" => (Mode::GouraudShaded, false),
|
||||
"GouraudWithEdges" => (Mode::GouraudShadedWithEdges, false),
|
||||
"Shaded with edges" => (Mode::GouraudShadedWithEdges, true),
|
||||
"Realistic" | "Shaded" | "Conceptual" | "Shades of Gray" | "Sketchy" | "X-Ray" => {
|
||||
(Mode::GouraudShaded, true)
|
||||
}
|
||||
_ => return None,
|
||||
})
|
||||
}
|
||||
|
||||
/// The name of the visual style a render mode is saved as.
|
||||
pub fn style_name_for(mode: Mode) -> &'static str {
|
||||
match mode {
|
||||
Mode::Wireframe2D => "2dWireframe",
|
||||
Mode::Wireframe3D => "Wireframe",
|
||||
Mode::HiddenLine => "Hidden",
|
||||
Mode::FlatShaded => "Flat",
|
||||
Mode::FlatShadedWithEdges => "FlatWithEdges",
|
||||
Mode::GouraudShaded => "Gouraud",
|
||||
Mode::GouraudShadedWithEdges => "GouraudWithEdges",
|
||||
}
|
||||
}
|
||||
|
||||
/// The style a style keyword names. Only the seven exist; nothing maps onto a
|
||||
/// nearest neighbour, because there is no longer anything else to map from.
|
||||
pub fn mode_for_keyword(keyword: &str) -> Option<Mode> {
|
||||
|
||||
+220
-2
@@ -214,8 +214,80 @@ 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;
|
||||
let cam = &mut *self.camera.borrow_mut();
|
||||
let before = {
|
||||
let cam = self.camera.borrow();
|
||||
(cam.rotation, cam.target, cam.distance)
|
||||
};
|
||||
self.restore_named_view_camera(view);
|
||||
let unchanged = {
|
||||
let cam = self.camera.borrow();
|
||||
cam.rotation.abs_diff_eq(before.0, 1e-6)
|
||||
&& cam.target.abs_diff_eq(before.1, 1e-6 * before.1.length().max(1.0))
|
||||
&& (cam.distance - before.2).abs() <= 1e-6 * before.2.abs().max(1.0)
|
||||
};
|
||||
// The model viewport takes a copy of the view's background; a view
|
||||
// without one clears it, unless the view is what the viewport already
|
||||
// shows (the reference leaves the viewport as it is then).
|
||||
if self.current_layout != "Model" && self.active_viewport.is_none() && view.paper_space {
|
||||
self.set_sheet_background(view);
|
||||
}
|
||||
if self.current_layout == "Model" {
|
||||
if view.background_handle.is_valid() || !unchanged {
|
||||
let background = copy_background(&mut self.document, view.background_handle);
|
||||
self.set_active_background(background);
|
||||
}
|
||||
if view.visual_style_handle.is_valid() {
|
||||
if let Some(handle) = self.active_tile_vport_handle() {
|
||||
if let Some(value) = self.document.vports.iter_mut().find(|v| v.handle == handle) {
|
||||
value.visual_style_handle = view.visual_style_handle;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
fn restore_named_view_camera(&mut self, view: &codec::tables::View) {
|
||||
named_view_camera(view, &mut self.camera.borrow_mut());
|
||||
self.camera_generation += 1;
|
||||
}
|
||||
|
||||
/// Restores a model-space view into a paper-space viewport: its camera,
|
||||
/// a copy of the view's background (or none) and the view's visual style.
|
||||
pub fn restore_named_view_in_viewport(&mut self, view: &codec::tables::View, viewport: Handle) {
|
||||
let mut camera = self.navigation_camera();
|
||||
named_view_camera(view, &mut camera);
|
||||
self.apply_navigation_camera(camera);
|
||||
let background = copy_background(&mut self.document, view.background_handle);
|
||||
let mut old = Handle::NULL;
|
||||
if let Some(EntityType::Viewport(value)) = self.document.get_entity_mut(viewport) {
|
||||
old = std::mem::replace(&mut value.background_handle, background);
|
||||
if view.visual_style_handle.is_valid() {
|
||||
value.visual_style_handle = view.visual_style_handle;
|
||||
}
|
||||
}
|
||||
erase_background(&mut self.document, old);
|
||||
self.notify_viewport_changed(viewport);
|
||||
self.camera_generation += 1;
|
||||
}
|
||||
|
||||
/// The paper-space viewport of the current layout takes a copy of a
|
||||
/// paper-space view's background (kept with the layout; the sheet itself
|
||||
/// is not drawn over).
|
||||
fn set_sheet_background(&mut self, view: &codec::tables::View) {
|
||||
let sheet = self.current_layout_sheet_viewport_handle();
|
||||
let background = copy_background(&mut self.document, view.background_handle);
|
||||
let old = match self.document.get_entity_mut(sheet) {
|
||||
Some(EntityType::Viewport(value)) => std::mem::replace(&mut value.background_handle, background),
|
||||
_ => background,
|
||||
};
|
||||
erase_background(&mut self.document, old);
|
||||
}
|
||||
}
|
||||
|
||||
/// Points a camera along a named view: direction, target and height.
|
||||
fn named_view_camera(view: &codec::tables::View, cam: &mut view::camera::Camera) {
|
||||
use glam::Vec3;
|
||||
{
|
||||
// The stored direction points from the target toward the eye.
|
||||
let eye_dir = Vec3::new(
|
||||
view.direction.x as f32,
|
||||
@@ -242,7 +314,56 @@ impl Scene {
|
||||
} else {
|
||||
cam.distance
|
||||
};
|
||||
}
|
||||
}
|
||||
|
||||
impl Scene {
|
||||
/// The background the active model-space viewport shows (null for the
|
||||
/// drawing area colour).
|
||||
pub fn active_background(&self) -> Handle {
|
||||
if self.current_layout != "Model" {
|
||||
// Inside a viewport, that viewport's; the sheet's is not saved
|
||||
// with a paper-space view.
|
||||
return match self.active_viewport.and_then(|handle| self.document.get_entity(handle)) {
|
||||
Some(EntityType::Viewport(value)) => value.background_handle,
|
||||
_ => Handle::NULL,
|
||||
};
|
||||
}
|
||||
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)
|
||||
}
|
||||
|
||||
/// The visual style of the active Model tile's viewport record.
|
||||
pub fn active_visual_style(&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.visual_style_handle)
|
||||
}
|
||||
|
||||
/// Records the visual style named `name` on the active Model tile's
|
||||
/// viewport record, when the drawing has it.
|
||||
pub fn set_active_visual_style_named(&mut self, name: &str) {
|
||||
let style = self.document.objects.iter().find_map(|(handle, object)| match object {
|
||||
codec::objects::ObjectType::VisualStyle(style) if style.description == name => Some(*handle),
|
||||
_ => None,
|
||||
});
|
||||
let (Some(style), Some(handle)) = (style, self.active_tile_vport_handle()) else { return };
|
||||
if let Some(value) = self.document.vports.iter_mut().find(|value| value.handle == handle) {
|
||||
value.visual_style_handle = style;
|
||||
}
|
||||
}
|
||||
|
||||
fn set_active_background(&mut self, background: Handle) {
|
||||
let Some(handle) = self.active_tile_vport_handle() else { return };
|
||||
let Some(value) = self.document.vports.iter_mut().find(|value| value.handle == handle) else {
|
||||
return;
|
||||
};
|
||||
let old = std::mem::replace(&mut value.background_handle, background);
|
||||
self.camera_generation += 1;
|
||||
if old != background {
|
||||
erase_background(&mut self.document, old);
|
||||
}
|
||||
}
|
||||
|
||||
/// Save the current camera state into a new named view entry.
|
||||
@@ -1638,3 +1759,100 @@ impl Scene {
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// The drawing's ACAD_BACKGROUND dictionary, made when it has none.
|
||||
fn background_dictionary(document: &mut codec::CadDocument) -> Option<Handle> {
|
||||
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
|
||||
}
|
||||
|
||||
/// Erases a background object once no view or viewport refers to it (the
|
||||
/// reference erases a view's background when it is replaced, cleared or the
|
||||
/// view deleted, and a viewport's when the viewport takes another).
|
||||
pub fn erase_background(document: &mut codec::CadDocument, handle: Handle) {
|
||||
use codec::objects::{ClassObjectData as D, ObjectType};
|
||||
if !handle.is_valid()
|
||||
|| document.views.iter().any(|view| view.background_handle == handle)
|
||||
|| document.vports.iter().any(|vport| vport.background_handle == handle)
|
||||
|| document.entities().any(|entity| {
|
||||
matches!(entity, codec::EntityType::Viewport(viewport) if viewport.background_handle == handle)
|
||||
})
|
||||
{
|
||||
return;
|
||||
}
|
||||
let owner = 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(_)
|
||||
) =>
|
||||
{
|
||||
object.owner
|
||||
}
|
||||
_ => return,
|
||||
};
|
||||
document.objects.remove(&handle);
|
||||
if let Some(ObjectType::Dictionary(entries)) = document.objects.get_mut(&owner) {
|
||||
entries.entries.retain(|(_, entry)| *entry != 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,
|
||||
}
|
||||
}
|
||||
@@ -31,6 +31,7 @@ pub enum BlockScalePolicy {
|
||||
mod block_base_point_tests;
|
||||
mod boundary;
|
||||
mod camera_ops;
|
||||
pub use camera_ops::{add_background, copy_background, erase_background};
|
||||
pub(crate) mod centerline;
|
||||
pub(crate) mod centermark;
|
||||
pub(crate) mod dimension_assoc;
|
||||
|
||||
+34
-13
@@ -431,7 +431,14 @@ impl shader::Primitive for Primitive {
|
||||
vp.background_image.as_ref(),
|
||||
vp.environment_image.as_ref(),
|
||||
);
|
||||
inner.upload_uniforms(device, queue, &vp.uniforms);
|
||||
// The scene works in logical pixels; an image background keeps
|
||||
// its size in device pixels.
|
||||
let mut uniforms = vp.uniforms;
|
||||
// The image's size and offset are in device pixels.
|
||||
for value in &mut uniforms.background_image_transform {
|
||||
*value /= scale.max(1e-3);
|
||||
}
|
||||
inner.upload_uniforms(device, queue, &uniforms);
|
||||
|
||||
// ── Scene-render cache ────────────────────────────────────────
|
||||
// A pure cursor move — or any frame where the view, geometry,
|
||||
@@ -2034,6 +2041,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<Handle> {
|
||||
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];
|
||||
@@ -2547,30 +2559,32 @@ impl Scene {
|
||||
if viewport.paper_sheet {
|
||||
let handle = self.current_layout_sheet_viewport_handle();
|
||||
if let Some(EntityType::Viewport(value)) = self.document.get_entity(handle) {
|
||||
return build(
|
||||
value.visual_style_handle,
|
||||
value.brightness,
|
||||
value.contrast,
|
||||
value.background_handle,
|
||||
);
|
||||
// The sheet keeps its page and desk: a paper-space view's
|
||||
// background is kept with the layout but not drawn.
|
||||
return build(value.visual_style_handle, value.brightness, value.contrast, Handle::NULL);
|
||||
}
|
||||
} else if viewport.handle.is_valid() {
|
||||
if let Some(EntityType::Viewport(value)) = self.document.get_entity(viewport.handle) {
|
||||
let flat = value.render_mode == codec::entities::ViewportRenderMode::Wireframe2D;
|
||||
return build(
|
||||
value.visual_style_handle,
|
||||
value.brightness,
|
||||
value.contrast,
|
||||
value.background_handle,
|
||||
if flat { Handle::NULL } else { value.background_handle },
|
||||
);
|
||||
}
|
||||
} else if self.current_layout == "Model" {
|
||||
let index = viewport.tile_idx.unwrap_or(0);
|
||||
if let Some(value) = self.model_tile_vport(index) {
|
||||
// A 2D wireframe view shows no background, as in the reference.
|
||||
let flat = self.model_tiles.borrow().get(index).is_some_and(|tile| {
|
||||
tile.render_mode == codec::entities::ViewportRenderMode::Wireframe2D
|
||||
});
|
||||
return build(
|
||||
value.visual_style_handle,
|
||||
value.brightness,
|
||||
value.contrast,
|
||||
value.background_handle,
|
||||
if flat { Handle::NULL } else { value.background_handle },
|
||||
);
|
||||
}
|
||||
}
|
||||
@@ -2973,7 +2987,8 @@ impl Scene {
|
||||
2.0,
|
||||
background.horizon as f32,
|
||||
background.height as f32,
|
||||
background.rotation as f32,
|
||||
// Stored in degrees.
|
||||
(background.rotation as f32).to_radians(),
|
||||
];
|
||||
result
|
||||
}
|
||||
@@ -2999,11 +3014,13 @@ impl Scene {
|
||||
background.use_tiling as u8 as f32,
|
||||
image.width as f32 / image.height.max(1) as f32,
|
||||
];
|
||||
// Offset, then the image's size in pixels at its scale:
|
||||
// an image not fitted to the screen keeps its pixel size.
|
||||
result.image_transform = [
|
||||
background.offset.x as f32,
|
||||
background.offset.y as f32,
|
||||
background.scale.x as f32,
|
||||
background.scale.y as f32,
|
||||
background.scale.x as f32 * image.width as f32,
|
||||
background.scale.y as f32 * image.height as f32,
|
||||
];
|
||||
result.params[0] = 4.0;
|
||||
result.image = Some(image);
|
||||
@@ -4890,7 +4907,11 @@ impl Scene {
|
||||
// (hatched) frame once it settles. Only applied to the on-screen
|
||||
// Model / paper content — the paper *sheet* keeps its fills.
|
||||
skip_hatch: self.hatch_lod_enabled() && navigating,
|
||||
skip_background: !inst.paper_sheet && self.current_layout != "Model",
|
||||
// A floating viewport shows the page behind it unless it has a
|
||||
// background of its own.
|
||||
skip_background: !inst.paper_sheet
|
||||
&& self.current_layout != "Model"
|
||||
&& display.background.params[0] < 0.5,
|
||||
geometry_epoch: self.geometry_epoch,
|
||||
camera_generation: self.camera_generation,
|
||||
wire_content_id,
|
||||
|
||||
+31
-14
@@ -81,18 +81,25 @@ fn equirect_uv(ndc: vec2<f32>, rotation: f32) -> vec2<f32> {
|
||||
fn image_uv(source_uv: vec2<f32>) -> vec2<f32> {
|
||||
var uv = source_uv;
|
||||
if u.background_image_params.x < 0.5 {
|
||||
uv = (source_uv - u.background_image_transform.xy)
|
||||
/ max(abs(u.background_image_transform.zw), vec2<f32>(1e-6));
|
||||
// Not fitted: the image at its own pixel size times its scale (a
|
||||
// negative scale mirrors it), centred on the viewport and moved by
|
||||
// its offset in pixels, right and down.
|
||||
let pixel = (source_uv - vec2<f32>(0.5)) * max(u.viewport_size, vec2<f32>(1.0))
|
||||
- u.background_image_transform.xy;
|
||||
let size = u.background_image_transform.zw;
|
||||
let safe = select(size, vec2<f32>(1e-6), abs(size) < vec2<f32>(1e-6));
|
||||
uv = pixel / safe + vec2<f32>(0.5);
|
||||
return select(uv, fract(uv), u.background_image_params.z > 0.5);
|
||||
}
|
||||
// Fitted: the image fills the viewport; keeping its aspect ratio it
|
||||
// covers the viewport and the overflow is cut off evenly.
|
||||
if u.background_image_params.y > 0.5 {
|
||||
let image_aspect = max(u.background_image_params.w, 1e-6);
|
||||
let viewport_aspect = max(u.viewport_size.x / max(u.viewport_size.y, 1.0), 1e-6);
|
||||
if image_aspect > viewport_aspect {
|
||||
let scale = viewport_aspect / image_aspect;
|
||||
uv.y = (uv.y - 0.5) / scale + 0.5;
|
||||
uv.x = (uv.x - 0.5) * (viewport_aspect / image_aspect) + 0.5;
|
||||
} else {
|
||||
let scale = image_aspect / viewport_aspect;
|
||||
uv.x = (uv.x - 0.5) / scale + 0.5;
|
||||
uv.y = (uv.y - 0.5) * (image_aspect / viewport_aspect) + 0.5;
|
||||
}
|
||||
}
|
||||
if u.background_image_params.z > 0.5 {
|
||||
@@ -108,15 +115,25 @@ fn fs_main(in: VertexOutput) -> @location(0) vec4<f32> {
|
||||
if mode == 1u {
|
||||
color = u.viewport_background.rgb;
|
||||
} else if mode == 2u {
|
||||
// Bottom to top along the "up" direction turned counter-clockwise by
|
||||
// the rotation, in pixels from the viewport centre. As the reference
|
||||
// draws it: the middle colour sits (horizon - 0.5) viewport widths
|
||||
// from the centre and blends linearly into the top and bottom colours
|
||||
// over height viewport heights on each side; two colours are one blend
|
||||
// over a viewport height on each side of the centre.
|
||||
let angle = u.background_params.w;
|
||||
let axis = vec2<f32>(-sin(angle), cos(angle));
|
||||
let coordinate = dot(in.uv - vec2<f32>(0.5), axis) + 0.5;
|
||||
let horizon = clamp(u.background_params.y, 0.0, 1.0);
|
||||
let half_height = max(abs(u.background_params.z) * 0.5, 1e-3);
|
||||
let lower = smoothstep(horizon - half_height, horizon, coordinate);
|
||||
let upper = smoothstep(horizon, horizon + half_height, coordinate);
|
||||
let low_color = mix(u.background_bottom.rgb, u.background_middle.rgb, lower);
|
||||
color = mix(low_color, u.background_top.rgb, upper);
|
||||
let up = vec2<f32>(-sin(angle), cos(angle));
|
||||
let size = max(u.viewport_size, vec2<f32>(1.0));
|
||||
let pixel = (vec2<f32>(in.uv.x, 1.0 - in.uv.y) - vec2<f32>(0.5)) * size;
|
||||
let along = dot(pixel, up);
|
||||
let three = abs(u.background_params.z) > 1e-6;
|
||||
let middle_at = select(0.0, (u.background_params.y - 0.5) * size.x, three);
|
||||
let half_band = select(1.0, abs(u.background_params.z), three) * size.y;
|
||||
let middle = select(mix(u.background_bottom.rgb, u.background_top.rgb, 0.5),
|
||||
u.background_middle.rgb, three);
|
||||
let lower = clamp((along - middle_at + half_band) / half_band, 0.0, 1.0);
|
||||
let upper = clamp((along - middle_at) / half_band, 0.0, 1.0);
|
||||
color = mix(mix(u.background_bottom.rgb, middle, lower), u.background_top.rgb, upper);
|
||||
} else if mode == 3u {
|
||||
let direction = world_direction(in.ndc);
|
||||
if direction.z >= 0.0 {
|
||||
|
||||
@@ -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,288 @@ 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 image's offset (pixels) and scale, kept from the view's image.
|
||||
pub offset: [f64; 2],
|
||||
pub scale: [f64; 2],
|
||||
/// 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::<f32>().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()
|
||||
}
|
||||
Reference in new issue
Block a user