Improve CAD workspace functionality and user experience
No files matched your search
@@ -0,0 +1,135 @@
|
||||
<?xml version="1.0" encoding="UTF-8" standalone="no"?>
|
||||
<svg
|
||||
viewBox="0 0 24 24"
|
||||
width="64"
|
||||
height="64"
|
||||
version="1.1"
|
||||
id="svg3"
|
||||
sodipodi:docname="gradient_curved.svg"
|
||||
inkscape:version="1.4.4 (dcaf3e7d9e, 2026-05-05)"
|
||||
xmlns:inkscape="http://www.inkscape.org/namespaces/inkscape"
|
||||
xmlns:sodipodi="http://sodipodi.sourceforge.net/DTD/sodipodi-0.dtd"
|
||||
xmlns="http://www.w3.org/2000/svg"
|
||||
xmlns:svg="http://www.w3.org/2000/svg">
|
||||
<sodipodi:namedview
|
||||
id="namedview3"
|
||||
pagecolor="#B4B6B9"
|
||||
bordercolor="#B4B6B9"
|
||||
borderopacity="0.25"
|
||||
inkscape:showpageshadow="2"
|
||||
inkscape:pageopacity="0.0"
|
||||
inkscape:pagecheckerboard="0"
|
||||
inkscape:deskcolor="#B4B6B9"
|
||||
inkscape:zoom="1.9394531"
|
||||
inkscape:cx="49.240685"
|
||||
inkscape:cy="-85.333334"
|
||||
inkscape:window-width="2560"
|
||||
inkscape:window-height="1368"
|
||||
inkscape:window-x="0"
|
||||
inkscape:window-y="0"
|
||||
inkscape:window-maximized="1"
|
||||
inkscape:current-layer="svg3" />
|
||||
<defs
|
||||
id="defs2">
|
||||
<linearGradient
|
||||
id="g"
|
||||
x1="4"
|
||||
y1="4"
|
||||
x2="20"
|
||||
y2="4"
|
||||
gradientUnits="userSpaceOnUse"
|
||||
gradientTransform="translate(22.421511,-1.7454447)"
|
||||
inkscape:label="0">
|
||||
<stop
|
||||
offset="0"
|
||||
stop-color="#6DB7ED"
|
||||
stop-opacity="0.9"
|
||||
id="stop1"
|
||||
style="stop-color:#B4B6B9;stop-opacity:1;" />
|
||||
<stop
|
||||
offset="1"
|
||||
stop-color="#6DB7ED"
|
||||
stop-opacity="0.0"
|
||||
id="stop2"
|
||||
style="stop-color:#B4B6B9;stop-opacity:0;" />
|
||||
</linearGradient>
|
||||
<radialGradient
|
||||
id="domeColor"
|
||||
cx="425.3569"
|
||||
cy="810.08398"
|
||||
r="487.05515"
|
||||
fx="425.3569"
|
||||
fy="810.08398"
|
||||
gradientTransform="matrix(0.63134428,7.5044275e-5,-0.00132862,0.0415918,-254.82322,-32.945166)"
|
||||
gradientUnits="userSpaceOnUse">
|
||||
<stop
|
||||
offset="0"
|
||||
stop-color="#B4B6B9"
|
||||
id="stop1-3"
|
||||
style="stop-color:#6DB7ED;stop-opacity:1;" />
|
||||
<stop
|
||||
offset="1"
|
||||
stop-color="#B4B6B9"
|
||||
id="stop6"
|
||||
style="stop-color:#B4B6B9;stop-opacity:1;" />
|
||||
</radialGradient>
|
||||
<radialGradient
|
||||
id="domeColor-6"
|
||||
cx="425.3569"
|
||||
cy="810.08398"
|
||||
r="487.05515"
|
||||
fx="425.3569"
|
||||
fy="810.08398"
|
||||
gradientTransform="matrix(0.04040603,7.8734977e-5,-8.5031467e-5,0.0436373,5.7983172,-63.225256)"
|
||||
gradientUnits="userSpaceOnUse">
|
||||
<stop
|
||||
offset="0"
|
||||
stop-color="#B4B6B9"
|
||||
id="stop1-7"
|
||||
style="stop-color:#B4B6B9;stop-opacity:0.29402253;" />
|
||||
<stop
|
||||
offset="1"
|
||||
stop-color="#B4B6B9"
|
||||
id="stop6-5"
|
||||
style="stop-color:#B4B6B9;stop-opacity:1;" />
|
||||
</radialGradient>
|
||||
<radialGradient
|
||||
id="domeColor-5"
|
||||
cx="425.3569"
|
||||
cy="810.08398"
|
||||
r="487.05515"
|
||||
fx="425.3569"
|
||||
fy="810.08398"
|
||||
gradientTransform="matrix(0.038512,7.5044275e-5,-8.1045617e-5,0.0415918,-4.2762155,-8.9451656)"
|
||||
gradientUnits="userSpaceOnUse">
|
||||
<stop
|
||||
offset="0.2"
|
||||
stop-color="#B4B6B9"
|
||||
id="stop1-3-6"
|
||||
style="stop-color:#B4B6B9;stop-opacity:1;" />
|
||||
<stop
|
||||
offset="1"
|
||||
stop-color="#B4B6B9"
|
||||
id="stop6-2"
|
||||
style="stop-color:#B4B6B9;stop-opacity:1;" />
|
||||
</radialGradient>
|
||||
</defs>
|
||||
<rect
|
||||
x="-175.5"
|
||||
y="-23.4375"
|
||||
width="375"
|
||||
height="22.875"
|
||||
fill="url(#domeColor)"
|
||||
id="rect15"
|
||||
style="fill:url(#domeColor);stroke:none;stroke-width:0;stroke-dasharray:none;stroke-opacity:1"
|
||||
transform="rotate(90)" />
|
||||
<rect
|
||||
x="0.5625"
|
||||
y="-23.4375"
|
||||
width="22.875"
|
||||
height="22.875"
|
||||
fill="url(#domeColor)"
|
||||
id="rect15-1"
|
||||
style="fill:none;stroke:#b4b6b9;stroke-width:1.125;stroke-dasharray:none;stroke-opacity:1"
|
||||
transform="rotate(90)" />
|
||||
</svg>
|
||||
|
After Width: | Height: | Size: 3.8 KiB |
@@ -0,0 +1,155 @@
|
||||
<?xml version="1.0" encoding="UTF-8" standalone="no"?>
|
||||
<svg
|
||||
viewBox="0 0 24 24"
|
||||
width="64"
|
||||
height="64"
|
||||
version="1.1"
|
||||
id="svg3"
|
||||
sodipodi:docname="gradient_curved_inverted.svg"
|
||||
inkscape:version="1.4.4 (dcaf3e7d9e, 2026-05-05)"
|
||||
xmlns:inkscape="http://www.inkscape.org/namespaces/inkscape"
|
||||
xmlns:sodipodi="http://sodipodi.sourceforge.net/DTD/sodipodi-0.dtd"
|
||||
xmlns="http://www.w3.org/2000/svg"
|
||||
xmlns:svg="http://www.w3.org/2000/svg">
|
||||
<sodipodi:namedview
|
||||
id="namedview3"
|
||||
pagecolor="#B4B6B9"
|
||||
bordercolor="#B4B6B9"
|
||||
borderopacity="0.25"
|
||||
inkscape:showpageshadow="2"
|
||||
inkscape:pageopacity="0.0"
|
||||
inkscape:pagecheckerboard="0"
|
||||
inkscape:deskcolor="#B4B6B9"
|
||||
inkscape:zoom="0.68570022"
|
||||
inkscape:cx="-67.813891"
|
||||
inkscape:cy="-212.19185"
|
||||
inkscape:window-width="2560"
|
||||
inkscape:window-height="1368"
|
||||
inkscape:window-x="0"
|
||||
inkscape:window-y="0"
|
||||
inkscape:window-maximized="1"
|
||||
inkscape:current-layer="svg3" />
|
||||
<defs
|
||||
id="defs2">
|
||||
<linearGradient
|
||||
id="g"
|
||||
x1="4"
|
||||
y1="4"
|
||||
x2="20"
|
||||
y2="4"
|
||||
gradientUnits="userSpaceOnUse"
|
||||
gradientTransform="translate(22.421511,-1.7454447)"
|
||||
inkscape:label="0">
|
||||
<stop
|
||||
offset="0"
|
||||
stop-color="#6DB7ED"
|
||||
stop-opacity="0.9"
|
||||
id="stop1"
|
||||
style="stop-color:#B4B6B9;stop-opacity:1;" />
|
||||
<stop
|
||||
offset="1"
|
||||
stop-color="#6DB7ED"
|
||||
stop-opacity="0.0"
|
||||
id="stop2"
|
||||
style="stop-color:#B4B6B9;stop-opacity:0;" />
|
||||
</linearGradient>
|
||||
<radialGradient
|
||||
id="domeColor"
|
||||
cx="425.3569"
|
||||
cy="810.08398"
|
||||
r="487.05515"
|
||||
fx="425.3569"
|
||||
fy="810.08398"
|
||||
gradientTransform="matrix(0.63134428,7.5044275e-5,-0.00132862,0.0415918,-254.82322,-8.9451655)"
|
||||
gradientUnits="userSpaceOnUse">
|
||||
<stop
|
||||
offset="0"
|
||||
stop-color="#B4B6B9"
|
||||
id="stop1-3"
|
||||
style="stop-color:#B4B6B9;stop-opacity:0.29402253;" />
|
||||
<stop
|
||||
offset="1"
|
||||
stop-color="#B4B6B9"
|
||||
id="stop6"
|
||||
style="stop-color:#B4B6B9;stop-opacity:1;" />
|
||||
</radialGradient>
|
||||
<radialGradient
|
||||
id="domeColor-6"
|
||||
cx="425.3569"
|
||||
cy="810.08398"
|
||||
r="487.05515"
|
||||
fx="425.3569"
|
||||
fy="810.08398"
|
||||
gradientTransform="matrix(0.04040603,7.8734977e-5,-8.5031467e-5,0.0436373,5.7983172,-63.225256)"
|
||||
gradientUnits="userSpaceOnUse">
|
||||
<stop
|
||||
offset="0"
|
||||
stop-color="#B4B6B9"
|
||||
id="stop1-7"
|
||||
style="stop-color:#B4B6B9;stop-opacity:0.29402253;" />
|
||||
<stop
|
||||
offset="1"
|
||||
stop-color="#B4B6B9"
|
||||
id="stop6-5"
|
||||
style="stop-color:#B4B6B9;stop-opacity:1;" />
|
||||
</radialGradient>
|
||||
<radialGradient
|
||||
id="domeColor-5"
|
||||
cx="425.3569"
|
||||
cy="810.08398"
|
||||
r="487.05515"
|
||||
fx="425.3569"
|
||||
fy="810.08398"
|
||||
gradientTransform="matrix(0.038512,7.5044275e-5,-8.1045617e-5,0.0415918,-4.2762155,-8.9451656)"
|
||||
gradientUnits="userSpaceOnUse">
|
||||
<stop
|
||||
offset="0.2"
|
||||
stop-color="#B4B6B9"
|
||||
id="stop1-3-6"
|
||||
style="stop-color:#B4B6B9;stop-opacity:1;" />
|
||||
<stop
|
||||
offset="1"
|
||||
stop-color="#B4B6B9"
|
||||
id="stop6-2"
|
||||
style="stop-color:#B4B6B9;stop-opacity:1;" />
|
||||
</radialGradient>
|
||||
<radialGradient
|
||||
id="domeColor-2"
|
||||
cx="425.3569"
|
||||
cy="810.08398"
|
||||
r="487.05515"
|
||||
fx="425.3569"
|
||||
fy="810.08398"
|
||||
gradientTransform="matrix(0.63134425,7.5044275e-5,-0.00132862,0.0415918,-254.8232,-32.945166)"
|
||||
gradientUnits="userSpaceOnUse">
|
||||
<stop
|
||||
offset="0.2"
|
||||
stop-color="#B4B6B9"
|
||||
id="stop1-3-7"
|
||||
style="stop-color:#B4B6B9;stop-opacity:1;" />
|
||||
<stop
|
||||
offset="1"
|
||||
stop-color="#B4B6B9"
|
||||
id="stop6-0"
|
||||
style="stop-color:#6DB7ED;stop-opacity:1;" />
|
||||
</radialGradient>
|
||||
</defs>
|
||||
<rect
|
||||
x="-175.5"
|
||||
y="-23.4375"
|
||||
width="375"
|
||||
height="22.875"
|
||||
fill="url(#domeColor)"
|
||||
id="rect15"
|
||||
style="fill:url(#domeColor-2);stroke:none;stroke-width:1.125;stroke-dasharray:none;stroke-opacity:1"
|
||||
transform="rotate(90)" />
|
||||
<rect
|
||||
x="0.5625"
|
||||
y="-23.4375"
|
||||
width="22.875"
|
||||
height="22.875"
|
||||
fill="url(#domeColor)"
|
||||
id="rect15-1"
|
||||
style="fill:none;stroke:#b4b6b9;stroke-width:1.125;stroke-dasharray:none;stroke-opacity:1"
|
||||
transform="rotate(90)" />
|
||||
</svg>
|
||||
|
After Width: | Height: | Size: 4.4 KiB |
@@ -0,0 +1,97 @@
|
||||
<?xml version="1.0" encoding="UTF-8" standalone="no"?>
|
||||
<svg
|
||||
viewBox="0 0 24 24"
|
||||
width="64"
|
||||
height="64"
|
||||
version="1.1"
|
||||
id="svg3"
|
||||
sodipodi:docname="gradient_cylindrical_inverted.svg"
|
||||
inkscape:version="1.4.3 (0d15f75042, 2025-12-25)"
|
||||
xmlns:inkscape="http://www.inkscape.org/namespaces/inkscape"
|
||||
xmlns:sodipodi="http://sodipodi.sourceforge.net/DTD/sodipodi-0.dtd"
|
||||
xmlns:xlink="http://www.w3.org/1999/xlink"
|
||||
xmlns="http://www.w3.org/2000/svg"
|
||||
xmlns:svg="http://www.w3.org/2000/svg">
|
||||
<sodipodi:namedview
|
||||
id="namedview3"
|
||||
pagecolor="#B4B6B9"
|
||||
bordercolor="#B4B6B9"
|
||||
borderopacity="0.25"
|
||||
inkscape:showpageshadow="2"
|
||||
inkscape:pageopacity="0.0"
|
||||
inkscape:pagecheckerboard="0"
|
||||
inkscape:deskcolor="#B4B6B9"
|
||||
inkscape:zoom="4.5273438"
|
||||
inkscape:cx="-35.451251"
|
||||
inkscape:cy="6.2950819"
|
||||
inkscape:window-width="2560"
|
||||
inkscape:window-height="1368"
|
||||
inkscape:window-x="0"
|
||||
inkscape:window-y="0"
|
||||
inkscape:window-maximized="1"
|
||||
inkscape:current-layer="svg3" />
|
||||
<defs
|
||||
id="defs2">
|
||||
<linearGradient
|
||||
id="linearGradient15"
|
||||
inkscape:collect="always">
|
||||
<stop
|
||||
style="stop-color:#6DB7ED;stop-opacity:1;"
|
||||
offset="0"
|
||||
id="stop15" />
|
||||
<stop
|
||||
style="stop-color:#B4B6B9;stop-opacity:1;"
|
||||
offset="0.47"
|
||||
id="stop17" />
|
||||
<stop
|
||||
style="stop-color:#B4B6B9;stop-opacity:1;"
|
||||
offset="0.52999997"
|
||||
id="stop18" />
|
||||
<stop
|
||||
style="stop-color:#6DB7ED;stop-opacity:1;"
|
||||
offset="1"
|
||||
id="stop16" />
|
||||
</linearGradient>
|
||||
<linearGradient
|
||||
id="g"
|
||||
x1="4"
|
||||
y1="4"
|
||||
x2="20"
|
||||
y2="4"
|
||||
gradientUnits="userSpaceOnUse"
|
||||
gradientTransform="translate(22.421511,-1.7454447)"
|
||||
inkscape:label="0">
|
||||
<stop
|
||||
offset="0"
|
||||
stop-color="#6DB7ED"
|
||||
stop-opacity="0.9"
|
||||
id="stop1"
|
||||
style="stop-color:#B4B6B9;stop-opacity:1;" />
|
||||
<stop
|
||||
offset="1"
|
||||
stop-color="#6DB7ED"
|
||||
stop-opacity="0.0"
|
||||
id="stop2"
|
||||
style="stop-color:#B4B6B9;stop-opacity:0;" />
|
||||
</linearGradient>
|
||||
<linearGradient
|
||||
inkscape:collect="always"
|
||||
xlink:href="#linearGradient15"
|
||||
id="linearGradient16"
|
||||
x1="0"
|
||||
y1="12"
|
||||
x2="24"
|
||||
y2="12"
|
||||
gradientUnits="userSpaceOnUse" />
|
||||
</defs>
|
||||
<rect
|
||||
x="0.5625"
|
||||
y="0.5625"
|
||||
width="22.875"
|
||||
height="22.875"
|
||||
stroke="#B4B6B9"
|
||||
stroke-width="1.5"
|
||||
fill="none"
|
||||
id="rect3"
|
||||
style="fill:url(#linearGradient16);stroke-width:1.125;stroke-dasharray:none;stroke:#B4B6B9;stroke-opacity:1" />
|
||||
</svg>
|
||||
|
After Width: | Height: | Size: 2.6 KiB |
@@ -0,0 +1,97 @@
|
||||
<?xml version="1.0" encoding="UTF-8" standalone="no"?>
|
||||
<svg
|
||||
viewBox="0 0 24 24"
|
||||
width="64"
|
||||
height="64"
|
||||
version="1.1"
|
||||
id="svg3"
|
||||
sodipodi:docname="gradient_cylindrical.svg"
|
||||
inkscape:version="1.4.3 (0d15f75042, 2025-12-25)"
|
||||
xmlns:inkscape="http://www.inkscape.org/namespaces/inkscape"
|
||||
xmlns:sodipodi="http://sodipodi.sourceforge.net/DTD/sodipodi-0.dtd"
|
||||
xmlns:xlink="http://www.w3.org/1999/xlink"
|
||||
xmlns="http://www.w3.org/2000/svg"
|
||||
xmlns:svg="http://www.w3.org/2000/svg">
|
||||
<sodipodi:namedview
|
||||
id="namedview3"
|
||||
pagecolor="#B4B6B9"
|
||||
bordercolor="#B4B6B9"
|
||||
borderopacity="0.25"
|
||||
inkscape:showpageshadow="2"
|
||||
inkscape:pageopacity="0.0"
|
||||
inkscape:pagecheckerboard="0"
|
||||
inkscape:deskcolor="#B4B6B9"
|
||||
inkscape:zoom="4.5273438"
|
||||
inkscape:cx="-35.451251"
|
||||
inkscape:cy="6.2950819"
|
||||
inkscape:window-width="2560"
|
||||
inkscape:window-height="1368"
|
||||
inkscape:window-x="0"
|
||||
inkscape:window-y="0"
|
||||
inkscape:window-maximized="1"
|
||||
inkscape:current-layer="svg3" />
|
||||
<defs
|
||||
id="defs2">
|
||||
<linearGradient
|
||||
id="linearGradient15"
|
||||
inkscape:collect="always">
|
||||
<stop
|
||||
style="stop-color:#B4B6B9;stop-opacity:1;"
|
||||
offset="0"
|
||||
id="stop15" />
|
||||
<stop
|
||||
style="stop-color:#6DB7ED;stop-opacity:1;"
|
||||
offset="0.47"
|
||||
id="stop17" />
|
||||
<stop
|
||||
style="stop-color:#6DB7ED;stop-opacity:1;"
|
||||
offset="0.52999997"
|
||||
id="stop18" />
|
||||
<stop
|
||||
style="stop-color:#B4B6B9;stop-opacity:1;"
|
||||
offset="0.99787325"
|
||||
id="stop16" />
|
||||
</linearGradient>
|
||||
<linearGradient
|
||||
id="g"
|
||||
x1="4"
|
||||
y1="4"
|
||||
x2="20"
|
||||
y2="4"
|
||||
gradientUnits="userSpaceOnUse"
|
||||
gradientTransform="translate(22.421511,-1.7454447)"
|
||||
inkscape:label="0">
|
||||
<stop
|
||||
offset="0"
|
||||
stop-color="#6DB7ED"
|
||||
stop-opacity="0.9"
|
||||
id="stop1"
|
||||
style="stop-color:#B4B6B9;stop-opacity:1;" />
|
||||
<stop
|
||||
offset="1"
|
||||
stop-color="#6DB7ED"
|
||||
stop-opacity="0.0"
|
||||
id="stop2"
|
||||
style="stop-color:#B4B6B9;stop-opacity:0;" />
|
||||
</linearGradient>
|
||||
<linearGradient
|
||||
inkscape:collect="always"
|
||||
xlink:href="#linearGradient15"
|
||||
id="linearGradient16"
|
||||
x1="0"
|
||||
y1="12"
|
||||
x2="24"
|
||||
y2="12"
|
||||
gradientUnits="userSpaceOnUse" />
|
||||
</defs>
|
||||
<rect
|
||||
x="0.5625"
|
||||
y="0.5625"
|
||||
width="22.875"
|
||||
height="22.875"
|
||||
stroke="#B4B6B9"
|
||||
stroke-width="1.5"
|
||||
fill="none"
|
||||
id="rect3"
|
||||
style="fill:url(#linearGradient16);stroke-width:1.125;stroke-dasharray:none;stroke:#B4B6B9;stroke-opacity:1" />
|
||||
</svg>
|
||||
|
After Width: | Height: | Size: 2.6 KiB |
@@ -0,0 +1,90 @@
|
||||
<?xml version="1.0" encoding="UTF-8" standalone="no"?>
|
||||
<svg
|
||||
viewBox="0 0 24 24"
|
||||
width="64"
|
||||
height="64"
|
||||
version="1.1"
|
||||
id="svg3"
|
||||
sodipodi:docname="gradient_hemispherical.svg"
|
||||
inkscape:version="1.4.4 (dcaf3e7d9e, 2026-05-05)"
|
||||
xmlns:inkscape="http://www.inkscape.org/namespaces/inkscape"
|
||||
xmlns:sodipodi="http://sodipodi.sourceforge.net/DTD/sodipodi-0.dtd"
|
||||
xmlns:xlink="http://www.w3.org/1999/xlink"
|
||||
xmlns="http://www.w3.org/2000/svg"
|
||||
xmlns:svg="http://www.w3.org/2000/svg">
|
||||
<sodipodi:namedview
|
||||
id="namedview3"
|
||||
pagecolor="#B4B6B9"
|
||||
bordercolor="#B4B6B9"
|
||||
borderopacity="0.25"
|
||||
inkscape:showpageshadow="2"
|
||||
inkscape:pageopacity="0.0"
|
||||
inkscape:pagecheckerboard="0"
|
||||
inkscape:deskcolor="#B4B6B9"
|
||||
inkscape:zoom="6.4026309"
|
||||
inkscape:cx="14.759558"
|
||||
inkscape:cy="17.25853"
|
||||
inkscape:window-width="2560"
|
||||
inkscape:window-height="1368"
|
||||
inkscape:window-x="0"
|
||||
inkscape:window-y="0"
|
||||
inkscape:window-maximized="1"
|
||||
inkscape:current-layer="svg3" />
|
||||
<defs
|
||||
id="defs2">
|
||||
<linearGradient
|
||||
id="linearGradient4"
|
||||
inkscape:collect="always">
|
||||
<stop
|
||||
style="stop-color:#B4B6B9;stop-opacity:1;"
|
||||
offset="0"
|
||||
id="stop4" />
|
||||
<stop
|
||||
style="stop-color:#6DB7ED;stop-opacity:1;"
|
||||
offset="0.69999999"
|
||||
id="stop5" />
|
||||
</linearGradient>
|
||||
<linearGradient
|
||||
id="g"
|
||||
x1="4"
|
||||
y1="4"
|
||||
x2="20"
|
||||
y2="4"
|
||||
gradientUnits="userSpaceOnUse"
|
||||
gradientTransform="translate(22.421511,-1.7454447)"
|
||||
inkscape:label="0">
|
||||
<stop
|
||||
offset="0"
|
||||
stop-color="#6DB7ED"
|
||||
stop-opacity="0.9"
|
||||
id="stop1"
|
||||
style="stop-color:#B4B6B9;stop-opacity:1;" />
|
||||
<stop
|
||||
offset="1"
|
||||
stop-color="#6DB7ED"
|
||||
stop-opacity="0.0"
|
||||
id="stop2"
|
||||
style="stop-color:#B4B6B9;stop-opacity:0;" />
|
||||
</linearGradient>
|
||||
<radialGradient
|
||||
inkscape:collect="always"
|
||||
xlink:href="#linearGradient4"
|
||||
id="radialGradient7"
|
||||
cx="12"
|
||||
cy="12"
|
||||
fx="12"
|
||||
fy="12"
|
||||
r="12"
|
||||
gradientUnits="userSpaceOnUse" />
|
||||
</defs>
|
||||
<rect
|
||||
x="0.5625"
|
||||
y="0.5625"
|
||||
width="22.875"
|
||||
height="22.875"
|
||||
stroke="#B4B6B9"
|
||||
stroke-width="1.5"
|
||||
fill="none"
|
||||
id="rect3"
|
||||
style="fill:url(#radialGradient7);stroke-width:1.125;stroke-dasharray:none;fill-opacity:1;stroke:#B4B6B9;stroke-opacity:1" />
|
||||
</svg>
|
||||
|
After Width: | Height: | Size: 2.4 KiB |
@@ -0,0 +1,100 @@
|
||||
<?xml version="1.0" encoding="UTF-8" standalone="no"?>
|
||||
<svg
|
||||
viewBox="0 0 24 24"
|
||||
width="64"
|
||||
height="64"
|
||||
version="1.1"
|
||||
id="svg3"
|
||||
sodipodi:docname="gradient_hemispherical_inverted.svg"
|
||||
inkscape:version="1.4.4 (dcaf3e7d9e, 2026-05-05)"
|
||||
xmlns:inkscape="http://www.inkscape.org/namespaces/inkscape"
|
||||
xmlns:sodipodi="http://sodipodi.sourceforge.net/DTD/sodipodi-0.dtd"
|
||||
xmlns:xlink="http://www.w3.org/1999/xlink"
|
||||
xmlns="http://www.w3.org/2000/svg"
|
||||
xmlns:svg="http://www.w3.org/2000/svg">
|
||||
<sodipodi:namedview
|
||||
id="namedview3"
|
||||
pagecolor="#B4B6B9"
|
||||
bordercolor="#B4B6B9"
|
||||
borderopacity="0.25"
|
||||
inkscape:showpageshadow="2"
|
||||
inkscape:pageopacity="0.0"
|
||||
inkscape:pagecheckerboard="0"
|
||||
inkscape:deskcolor="#B4B6B9"
|
||||
inkscape:zoom="6.4026309"
|
||||
inkscape:cx="14.759558"
|
||||
inkscape:cy="17.25853"
|
||||
inkscape:window-width="2560"
|
||||
inkscape:window-height="1368"
|
||||
inkscape:window-x="0"
|
||||
inkscape:window-y="0"
|
||||
inkscape:window-maximized="1"
|
||||
inkscape:current-layer="svg3" />
|
||||
<defs
|
||||
id="defs2">
|
||||
<linearGradient
|
||||
id="linearGradient4"
|
||||
inkscape:collect="always">
|
||||
<stop
|
||||
style="stop-color:#6db7ed;stop-opacity:1;"
|
||||
offset="0"
|
||||
id="stop5" />
|
||||
<stop
|
||||
style="stop-color:#b4b6b9;stop-opacity:0.25348836;"
|
||||
offset="0.51998693"
|
||||
id="stop4" />
|
||||
</linearGradient>
|
||||
<linearGradient
|
||||
id="g"
|
||||
x1="4"
|
||||
y1="4"
|
||||
x2="20"
|
||||
y2="4"
|
||||
gradientUnits="userSpaceOnUse"
|
||||
gradientTransform="translate(22.421511,-1.7454447)"
|
||||
inkscape:label="0">
|
||||
<stop
|
||||
offset="0"
|
||||
stop-color="#6DB7ED"
|
||||
stop-opacity="0.9"
|
||||
id="stop1"
|
||||
style="stop-color:#B4B6B9;stop-opacity:1;" />
|
||||
<stop
|
||||
offset="1"
|
||||
stop-color="#6DB7ED"
|
||||
stop-opacity="0.0"
|
||||
id="stop2"
|
||||
style="stop-color:#B4B6B9;stop-opacity:0;" />
|
||||
</linearGradient>
|
||||
<radialGradient
|
||||
inkscape:collect="always"
|
||||
xlink:href="#linearGradient4"
|
||||
id="radialGradient7"
|
||||
cx="12"
|
||||
cy="12"
|
||||
fx="12"
|
||||
fy="12"
|
||||
r="12"
|
||||
gradientUnits="userSpaceOnUse" />
|
||||
</defs>
|
||||
<rect
|
||||
x="0.5625"
|
||||
y="0.5625"
|
||||
width="22.875"
|
||||
height="22.875"
|
||||
stroke="#B4B6B9"
|
||||
stroke-width="1.5"
|
||||
fill="none"
|
||||
id="rect3-3"
|
||||
style="fill:#b4b6b9;fill-opacity:1;stroke:#b4b6b9;stroke-width:1.125;stroke-dasharray:none;stroke-opacity:1" />
|
||||
<rect
|
||||
x="0.5625"
|
||||
y="0.5625"
|
||||
width="22.875"
|
||||
height="22.875"
|
||||
stroke="#B4B6B9"
|
||||
stroke-width="1.5"
|
||||
fill="none"
|
||||
id="rect3"
|
||||
style="fill:url(#radialGradient7);stroke-width:1.125;stroke-dasharray:none;fill-opacity:1;stroke:#B4B6B9;stroke-opacity:1" />
|
||||
</svg>
|
||||
|
After Width: | Height: | Size: 2.7 KiB |
@@ -0,0 +1,89 @@
|
||||
<?xml version="1.0" encoding="UTF-8" standalone="no"?>
|
||||
<svg
|
||||
viewBox="0 0 24 24"
|
||||
width="64"
|
||||
height="64"
|
||||
version="1.1"
|
||||
id="svg3"
|
||||
sodipodi:docname="gradient_linear.svg"
|
||||
inkscape:version="1.4.3 (0d15f75042, 2025-12-25)"
|
||||
xmlns:inkscape="http://www.inkscape.org/namespaces/inkscape"
|
||||
xmlns:sodipodi="http://sodipodi.sourceforge.net/DTD/sodipodi-0.dtd"
|
||||
xmlns:xlink="http://www.w3.org/1999/xlink"
|
||||
xmlns="http://www.w3.org/2000/svg"
|
||||
xmlns:svg="http://www.w3.org/2000/svg">
|
||||
<sodipodi:namedview
|
||||
id="namedview3"
|
||||
pagecolor="#B4B6B9"
|
||||
bordercolor="#B4B6B9"
|
||||
borderopacity="0.25"
|
||||
inkscape:showpageshadow="2"
|
||||
inkscape:pageopacity="0.0"
|
||||
inkscape:pagecheckerboard="0"
|
||||
inkscape:deskcolor="#B4B6B9"
|
||||
inkscape:zoom="6.4026309"
|
||||
inkscape:cx="14.759558"
|
||||
inkscape:cy="17.25853"
|
||||
inkscape:window-width="2560"
|
||||
inkscape:window-height="1368"
|
||||
inkscape:window-x="0"
|
||||
inkscape:window-y="0"
|
||||
inkscape:window-maximized="1"
|
||||
inkscape:current-layer="svg3" />
|
||||
<defs
|
||||
id="defs2">
|
||||
<linearGradient
|
||||
id="linearGradient4"
|
||||
inkscape:collect="always">
|
||||
<stop
|
||||
style="stop-color:#6DB7ED;stop-opacity:1;"
|
||||
offset="0"
|
||||
id="stop5" />
|
||||
<stop
|
||||
style="stop-color:#B4B6B9;stop-opacity:1;"
|
||||
offset="0.85000002"
|
||||
id="stop4" />
|
||||
</linearGradient>
|
||||
<linearGradient
|
||||
id="g"
|
||||
x1="4"
|
||||
y1="4"
|
||||
x2="20"
|
||||
y2="4"
|
||||
gradientUnits="userSpaceOnUse"
|
||||
gradientTransform="translate(22.421511,-1.7454447)"
|
||||
inkscape:label="0">
|
||||
<stop
|
||||
offset="0"
|
||||
stop-color="#6DB7ED"
|
||||
stop-opacity="0.9"
|
||||
id="stop1"
|
||||
style="stop-color:#B4B6B9;stop-opacity:1;" />
|
||||
<stop
|
||||
offset="1"
|
||||
stop-color="#6DB7ED"
|
||||
stop-opacity="0.0"
|
||||
id="stop2"
|
||||
style="stop-color:#B4B6B9;stop-opacity:0;" />
|
||||
</linearGradient>
|
||||
<linearGradient
|
||||
inkscape:collect="always"
|
||||
xlink:href="#linearGradient4"
|
||||
id="linearGradient7"
|
||||
x1="0"
|
||||
y1="12"
|
||||
x2="24"
|
||||
y2="12"
|
||||
gradientUnits="userSpaceOnUse" />
|
||||
</defs>
|
||||
<rect
|
||||
x="0.5625"
|
||||
y="0.5625"
|
||||
width="22.875"
|
||||
height="22.875"
|
||||
stroke="#B4B6B9"
|
||||
stroke-width="1.5"
|
||||
fill="none"
|
||||
id="rect3"
|
||||
style="fill:url(#linearGradient7);stroke-width:1.125;stroke-dasharray:none;fill-opacity:1;stroke:#B4B6B9;stroke-opacity:1" />
|
||||
</svg>
|
||||
|
After Width: | Height: | Size: 2.4 KiB |
@@ -0,0 +1,90 @@
|
||||
<?xml version="1.0" encoding="UTF-8" standalone="no"?>
|
||||
<svg
|
||||
viewBox="0 0 24 24"
|
||||
width="64"
|
||||
height="64"
|
||||
version="1.1"
|
||||
id="svg3"
|
||||
sodipodi:docname="gradient_spherical.svg"
|
||||
inkscape:version="1.4.3 (0d15f75042, 2025-12-25)"
|
||||
xmlns:inkscape="http://www.inkscape.org/namespaces/inkscape"
|
||||
xmlns:sodipodi="http://sodipodi.sourceforge.net/DTD/sodipodi-0.dtd"
|
||||
xmlns:xlink="http://www.w3.org/1999/xlink"
|
||||
xmlns="http://www.w3.org/2000/svg"
|
||||
xmlns:svg="http://www.w3.org/2000/svg">
|
||||
<sodipodi:namedview
|
||||
id="namedview3"
|
||||
pagecolor="#B4B6B9"
|
||||
bordercolor="#B4B6B9"
|
||||
borderopacity="0.25"
|
||||
inkscape:showpageshadow="2"
|
||||
inkscape:pageopacity="0.0"
|
||||
inkscape:pagecheckerboard="0"
|
||||
inkscape:deskcolor="#B4B6B9"
|
||||
inkscape:zoom="6.4026309"
|
||||
inkscape:cx="14.759558"
|
||||
inkscape:cy="17.25853"
|
||||
inkscape:window-width="2560"
|
||||
inkscape:window-height="1368"
|
||||
inkscape:window-x="0"
|
||||
inkscape:window-y="0"
|
||||
inkscape:window-maximized="1"
|
||||
inkscape:current-layer="svg3" />
|
||||
<defs
|
||||
id="defs2">
|
||||
<linearGradient
|
||||
id="linearGradient4"
|
||||
inkscape:collect="always">
|
||||
<stop
|
||||
style="stop-color:#B4B6B9;stop-opacity:1;"
|
||||
offset="0.30000001"
|
||||
id="stop4" />
|
||||
<stop
|
||||
style="stop-color:#6DB7ED;stop-opacity:1;"
|
||||
offset="0.94999999"
|
||||
id="stop5" />
|
||||
</linearGradient>
|
||||
<linearGradient
|
||||
id="g"
|
||||
x1="4"
|
||||
y1="4"
|
||||
x2="20"
|
||||
y2="4"
|
||||
gradientUnits="userSpaceOnUse"
|
||||
gradientTransform="translate(22.421511,-1.7454447)"
|
||||
inkscape:label="0">
|
||||
<stop
|
||||
offset="0"
|
||||
stop-color="#6DB7ED"
|
||||
stop-opacity="0.9"
|
||||
id="stop1"
|
||||
style="stop-color:#B4B6B9;stop-opacity:1;" />
|
||||
<stop
|
||||
offset="1"
|
||||
stop-color="#6DB7ED"
|
||||
stop-opacity="0.0"
|
||||
id="stop2"
|
||||
style="stop-color:#B4B6B9;stop-opacity:0;" />
|
||||
</linearGradient>
|
||||
<radialGradient
|
||||
inkscape:collect="always"
|
||||
xlink:href="#linearGradient4"
|
||||
id="radialGradient7"
|
||||
cx="12"
|
||||
cy="12"
|
||||
fx="12"
|
||||
fy="12"
|
||||
r="12"
|
||||
gradientUnits="userSpaceOnUse" />
|
||||
</defs>
|
||||
<rect
|
||||
x="0.5625"
|
||||
y="0.5625"
|
||||
width="22.875"
|
||||
height="22.875"
|
||||
stroke="#B4B6B9"
|
||||
stroke-width="1.5"
|
||||
fill="none"
|
||||
id="rect3"
|
||||
style="fill:url(#radialGradient7);stroke-width:1.125;stroke-dasharray:none;fill-opacity:1;stroke:#B4B6B9;stroke-opacity:1" />
|
||||
</svg>
|
||||
|
After Width: | Height: | Size: 2.5 KiB |
@@ -0,0 +1,90 @@
|
||||
<?xml version="1.0" encoding="UTF-8" standalone="no"?>
|
||||
<svg
|
||||
viewBox="0 0 24 24"
|
||||
width="64"
|
||||
height="64"
|
||||
version="1.1"
|
||||
id="svg3"
|
||||
sodipodi:docname="gradient_spherical_inverted.svg"
|
||||
inkscape:version="1.4.3 (0d15f75042, 2025-12-25)"
|
||||
xmlns:inkscape="http://www.inkscape.org/namespaces/inkscape"
|
||||
xmlns:sodipodi="http://sodipodi.sourceforge.net/DTD/sodipodi-0.dtd"
|
||||
xmlns:xlink="http://www.w3.org/1999/xlink"
|
||||
xmlns="http://www.w3.org/2000/svg"
|
||||
xmlns:svg="http://www.w3.org/2000/svg">
|
||||
<sodipodi:namedview
|
||||
id="namedview3"
|
||||
pagecolor="#B4B6B9"
|
||||
bordercolor="#B4B6B9"
|
||||
borderopacity="0.25"
|
||||
inkscape:showpageshadow="2"
|
||||
inkscape:pageopacity="0.0"
|
||||
inkscape:pagecheckerboard="0"
|
||||
inkscape:deskcolor="#B4B6B9"
|
||||
inkscape:zoom="6.4026309"
|
||||
inkscape:cx="14.759558"
|
||||
inkscape:cy="17.25853"
|
||||
inkscape:window-width="2560"
|
||||
inkscape:window-height="1368"
|
||||
inkscape:window-x="0"
|
||||
inkscape:window-y="0"
|
||||
inkscape:window-maximized="1"
|
||||
inkscape:current-layer="svg3" />
|
||||
<defs
|
||||
id="defs2">
|
||||
<linearGradient
|
||||
id="linearGradient4"
|
||||
inkscape:collect="always">
|
||||
<stop
|
||||
style="stop-color:#6DB7ED;stop-opacity:1;"
|
||||
offset="0.29615885"
|
||||
id="stop5" />
|
||||
<stop
|
||||
style="stop-color:#B4B6B9;stop-opacity:1;"
|
||||
offset="0.95343965"
|
||||
id="stop4" />
|
||||
</linearGradient>
|
||||
<linearGradient
|
||||
id="g"
|
||||
x1="4"
|
||||
y1="4"
|
||||
x2="20"
|
||||
y2="4"
|
||||
gradientUnits="userSpaceOnUse"
|
||||
gradientTransform="translate(22.421511,-1.7454447)"
|
||||
inkscape:label="0">
|
||||
<stop
|
||||
offset="0"
|
||||
stop-color="#6DB7ED"
|
||||
stop-opacity="0.9"
|
||||
id="stop1"
|
||||
style="stop-color:#B4B6B9;stop-opacity:1;" />
|
||||
<stop
|
||||
offset="1"
|
||||
stop-color="#6DB7ED"
|
||||
stop-opacity="0.0"
|
||||
id="stop2"
|
||||
style="stop-color:#B4B6B9;stop-opacity:0;" />
|
||||
</linearGradient>
|
||||
<radialGradient
|
||||
inkscape:collect="always"
|
||||
xlink:href="#linearGradient4"
|
||||
id="radialGradient7"
|
||||
cx="12"
|
||||
cy="12"
|
||||
fx="12"
|
||||
fy="12"
|
||||
r="12"
|
||||
gradientUnits="userSpaceOnUse" />
|
||||
</defs>
|
||||
<rect
|
||||
x="0.5625"
|
||||
y="0.5625"
|
||||
width="22.875"
|
||||
height="22.875"
|
||||
stroke="#B4B6B9"
|
||||
stroke-width="1.5"
|
||||
fill="none"
|
||||
id="rect3"
|
||||
style="fill:url(#radialGradient7);stroke-width:1.125;stroke-dasharray:none;fill-opacity:1;stroke:#B4B6B9;stroke-opacity:1" />
|
||||
</svg>
|
||||
|
After Width: | Height: | Size: 2.5 KiB |
@@ -1,10 +1,87 @@
|
||||
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24" width="64" height="64">
|
||||
<defs>
|
||||
<linearGradient id="g" x1="0" y1="0" x2="1" y2="0">
|
||||
<stop offset="0%" stop-color="#6DB7ED" stop-opacity="0.9"/>
|
||||
<stop offset="100%" stop-color="#6DB7ED" stop-opacity="0.0"/>
|
||||
<?xml version="1.0" encoding="UTF-8" standalone="no"?>
|
||||
<svg
|
||||
viewBox="0 0 24 24"
|
||||
width="64"
|
||||
height="64"
|
||||
version="1.1"
|
||||
id="svg3"
|
||||
sodipodi:docname="hatch_gradient.svg"
|
||||
inkscape:version="1.4.3 (0d15f75042, 2025-12-25)"
|
||||
xmlns:inkscape="http://www.inkscape.org/namespaces/inkscape"
|
||||
xmlns:sodipodi="http://sodipodi.sourceforge.net/DTD/sodipodi-0.dtd"
|
||||
xmlns:xlink="http://www.w3.org/1999/xlink"
|
||||
xmlns="http://www.w3.org/2000/svg"
|
||||
xmlns:svg="http://www.w3.org/2000/svg">
|
||||
<sodipodi:namedview
|
||||
id="namedview3"
|
||||
pagecolor="#B4B6B9"
|
||||
bordercolor="#B4B6B9"
|
||||
borderopacity="0.25"
|
||||
inkscape:showpageshadow="2"
|
||||
inkscape:pageopacity="0.0"
|
||||
inkscape:pagecheckerboard="0"
|
||||
inkscape:deskcolor="#B4B6B9"
|
||||
inkscape:zoom="15.601563"
|
||||
inkscape:cx="22.401602"
|
||||
inkscape:cy="32.849273"
|
||||
inkscape:window-width="2560"
|
||||
inkscape:window-height="1368"
|
||||
inkscape:window-x="0"
|
||||
inkscape:window-y="0"
|
||||
inkscape:window-maximized="1"
|
||||
inkscape:current-layer="svg3" />
|
||||
<defs
|
||||
id="defs2">
|
||||
<linearGradient
|
||||
id="g"
|
||||
x1="4"
|
||||
y1="4"
|
||||
x2="20"
|
||||
y2="4"
|
||||
gradientUnits="userSpaceOnUse"
|
||||
gradientTransform="translate(22.421511,-1.7454447)">
|
||||
<stop
|
||||
offset="0%"
|
||||
stop-color="#6DB7ED"
|
||||
stop-opacity="0.9"
|
||||
id="stop1" />
|
||||
<stop
|
||||
offset="100%"
|
||||
stop-color="#6DB7ED"
|
||||
stop-opacity="0.0"
|
||||
id="stop2" />
|
||||
</linearGradient>
|
||||
<linearGradient
|
||||
inkscape:collect="always"
|
||||
xlink:href="#linearGradient3"
|
||||
id="linearGradient4"
|
||||
x1="0"
|
||||
y1="12"
|
||||
x2="24"
|
||||
y2="12"
|
||||
gradientUnits="userSpaceOnUse"
|
||||
gradientTransform="matrix(0.98333334,0,0,0.98333334,0.19999999,0.19999999)" />
|
||||
<linearGradient
|
||||
id="linearGradient3"
|
||||
inkscape:collect="always">
|
||||
<stop
|
||||
style="stop-color:#6DB7ED;stop-opacity:0.89999998;"
|
||||
offset="0"
|
||||
id="stop3" />
|
||||
<stop
|
||||
style="stop-color:#6DB7ED;stop-opacity:0;"
|
||||
offset="1"
|
||||
id="stop4" />
|
||||
</linearGradient>
|
||||
</defs>
|
||||
<rect x="4" y="4" width="16" height="16" fill="url(#g)"/>
|
||||
<rect x="4" y="4" width="16" height="16" stroke="#B4B6B9" stroke-width="1.5" fill="none"/>
|
||||
<rect
|
||||
x="0.9375"
|
||||
y="0.9375"
|
||||
width="22.125"
|
||||
height="22.125"
|
||||
stroke="#B4B6B9"
|
||||
stroke-width="1.5"
|
||||
fill="none"
|
||||
id="rect3"
|
||||
style="fill:url(#linearGradient4);stroke-width:1.875;stroke-dasharray:none" />
|
||||
</svg>
|
||||
|
Before Width: | Height: | Size: 477 B After Width: | Height: | Size: 2.3 KiB |
@@ -6,7 +6,7 @@
|
||||
version="1.1"
|
||||
id="svg6"
|
||||
sodipodi:docname="hatch_lines.svg"
|
||||
inkscape:version="1.4.4 (dcaf3e7d9e, 2026-05-05)"
|
||||
inkscape:version="1.4.3 (0d15f75042, 2025-12-25)"
|
||||
xmlns:inkscape="http://www.inkscape.org/namespaces/inkscape"
|
||||
xmlns:sodipodi="http://sodipodi.sourceforge.net/DTD/sodipodi-0.dtd"
|
||||
xmlns="http://www.w3.org/2000/svg"
|
||||
@@ -20,15 +20,15 @@
|
||||
inkscape:pageopacity="0"
|
||||
inkscape:pagecheckerboard="0"
|
||||
inkscape:deskcolor="#505050"
|
||||
inkscape:zoom="6.3152247"
|
||||
inkscape:cx="34.044711"
|
||||
inkscape:cy="32.698757"
|
||||
inkscape:window-width="1920"
|
||||
inkscape:window-height="939"
|
||||
inkscape:zoom="8.9310764"
|
||||
inkscape:cx="26.032696"
|
||||
inkscape:cy="34.878215"
|
||||
inkscape:window-width="2560"
|
||||
inkscape:window-height="1368"
|
||||
inkscape:window-x="0"
|
||||
inkscape:window-y="0"
|
||||
inkscape:window-maximized="1"
|
||||
inkscape:current-layer="svg6" />
|
||||
inkscape:current-layer="g6" />
|
||||
<defs
|
||||
id="defs1">
|
||||
<clipPath
|
||||
@@ -53,40 +53,47 @@
|
||||
y2="-2"
|
||||
stroke="#6db7ed"
|
||||
stroke-width="1.2"
|
||||
id="line1" />
|
||||
id="line1"
|
||||
style="stroke-width:1.25222463;stroke-dasharray:none" />
|
||||
<line
|
||||
x1="2"
|
||||
y1="26"
|
||||
y1="25.5"
|
||||
x2="26"
|
||||
y2="2"
|
||||
y2="1.5"
|
||||
stroke="#6db7ed"
|
||||
stroke-width="1.2"
|
||||
id="line2" />
|
||||
id="line2"
|
||||
style="stroke-width:1.25222;stroke-dasharray:none" />
|
||||
<line
|
||||
x1="-6"
|
||||
y1="18"
|
||||
y1="19"
|
||||
x2="18"
|
||||
y2="-6"
|
||||
y2="-5"
|
||||
stroke="#6db7ed"
|
||||
stroke-width="1.2"
|
||||
id="line3" />
|
||||
id="line3"
|
||||
inkscape:transform-center-x="1.4695877"
|
||||
inkscape:transform-center-y="-1.4695877"
|
||||
style="stroke-width:1.25222;stroke-dasharray:none" />
|
||||
<line
|
||||
x1="6"
|
||||
y1="30"
|
||||
y1="29"
|
||||
x2="30"
|
||||
y2="6"
|
||||
y2="5"
|
||||
stroke="#6db7ed"
|
||||
stroke-width="1.2"
|
||||
id="line5" />
|
||||
id="line5"
|
||||
style="stroke-width:1.25222;stroke-dasharray:none" />
|
||||
</g>
|
||||
<!-- Square boundary -->
|
||||
<rect
|
||||
x="0.87927544"
|
||||
y="0.87927294"
|
||||
width="22.241451"
|
||||
height="22.241451"
|
||||
x="0.9375"
|
||||
y="0.9375"
|
||||
width="22.125"
|
||||
height="22.125"
|
||||
stroke="#b4b6b9"
|
||||
stroke-width="1.75855"
|
||||
fill="none"
|
||||
id="rect6" />
|
||||
id="rect6"
|
||||
style="stroke-width:1.875;stroke-dasharray:none" />
|
||||
</svg>
|
||||
|
Before Width: | Height: | Size: 2.1 KiB After Width: | Height: | Size: 2.4 KiB |
@@ -0,0 +1,50 @@
|
||||
<?xml version="1.0" encoding="UTF-8" standalone="no"?>
|
||||
<svg
|
||||
viewBox="0 0 24 24"
|
||||
width="64"
|
||||
height="64"
|
||||
version="1.1"
|
||||
id="svg2"
|
||||
sodipodi:docname="hatch_none.svg"
|
||||
inkscape:version="1.4.3 (0d15f75042, 2025-12-25)"
|
||||
xmlns:inkscape="http://www.inkscape.org/namespaces/inkscape"
|
||||
xmlns:sodipodi="http://sodipodi.sourceforge.net/DTD/sodipodi-0.dtd"
|
||||
xmlns="http://www.w3.org/2000/svg"
|
||||
xmlns:svg="http://www.w3.org/2000/svg">
|
||||
<defs
|
||||
id="defs2" />
|
||||
<sodipodi:namedview
|
||||
id="namedview2"
|
||||
pagecolor="#ffffff"
|
||||
bordercolor="#000000"
|
||||
borderopacity="0.25"
|
||||
inkscape:showpageshadow="2"
|
||||
inkscape:pageopacity="0.0"
|
||||
inkscape:pagecheckerboard="0"
|
||||
inkscape:deskcolor="#d1d1d1"
|
||||
inkscape:zoom="12.805262"
|
||||
inkscape:cx="31.66667"
|
||||
inkscape:cy="32.75997"
|
||||
inkscape:window-width="2560"
|
||||
inkscape:window-height="1368"
|
||||
inkscape:window-x="0"
|
||||
inkscape:window-y="0"
|
||||
inkscape:window-maximized="1"
|
||||
inkscape:current-layer="svg2" />
|
||||
<!-- Solid fill -->
|
||||
<path
|
||||
style="fill:none;stroke:#ff0000;stroke-width:1.875;stroke-dasharray:none;stroke-opacity:1"
|
||||
d="M 0,24 24,0"
|
||||
id="path1"
|
||||
sodipodi:nodetypes="cc" />
|
||||
<rect
|
||||
x="0.9375"
|
||||
y="0.9375"
|
||||
width="22.125"
|
||||
height="22.125116"
|
||||
stroke="#b4b6b9"
|
||||
stroke-width="2.05714"
|
||||
fill="none"
|
||||
id="rect2"
|
||||
style="fill:none;fill-opacity:1;stroke-width:1.875;stroke-dasharray:none" />
|
||||
</svg>
|
||||
|
After Width: | Height: | Size: 1.4 KiB |
@@ -1,5 +1,45 @@
|
||||
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24" width="64" height="64">
|
||||
<?xml version="1.0" encoding="UTF-8" standalone="no"?>
|
||||
<svg
|
||||
viewBox="0 0 24 24"
|
||||
width="64"
|
||||
height="64"
|
||||
version="1.1"
|
||||
id="svg2"
|
||||
sodipodi:docname="hatch_solid.svg"
|
||||
inkscape:version="1.4.3 (0d15f75042, 2025-12-25)"
|
||||
xmlns:inkscape="http://www.inkscape.org/namespaces/inkscape"
|
||||
xmlns:sodipodi="http://sodipodi.sourceforge.net/DTD/sodipodi-0.dtd"
|
||||
xmlns="http://www.w3.org/2000/svg"
|
||||
xmlns:svg="http://www.w3.org/2000/svg">
|
||||
<defs
|
||||
id="defs2" />
|
||||
<sodipodi:namedview
|
||||
id="namedview2"
|
||||
pagecolor="#ffffff"
|
||||
bordercolor="#000000"
|
||||
borderopacity="0.25"
|
||||
inkscape:showpageshadow="2"
|
||||
inkscape:pageopacity="0.0"
|
||||
inkscape:pagecheckerboard="0"
|
||||
inkscape:deskcolor="#d1d1d1"
|
||||
inkscape:zoom="18.109375"
|
||||
inkscape:cx="32"
|
||||
inkscape:cy="32"
|
||||
inkscape:window-width="2560"
|
||||
inkscape:window-height="1368"
|
||||
inkscape:window-x="0"
|
||||
inkscape:window-y="0"
|
||||
inkscape:window-maximized="1"
|
||||
inkscape:current-layer="svg2" />
|
||||
<!-- Solid fill -->
|
||||
<rect x="4" y="4" width="16" height="16" fill="#6DB7ED" opacity="0.6"/>
|
||||
<rect x="4" y="4" width="16" height="16" stroke="#B4B6B9" stroke-width="1.5" fill="none"/>
|
||||
<rect
|
||||
x="0.9375"
|
||||
y="0.9375"
|
||||
width="22.125"
|
||||
height="22.125116"
|
||||
stroke="#b4b6b9"
|
||||
stroke-width="2.05714"
|
||||
fill="none"
|
||||
id="rect2"
|
||||
style="fill:#6db7ed;fill-opacity:1;stroke-width:1.875;stroke-dasharray:none" />
|
||||
</svg>
|
||||
|
Before Width: | Height: | Size: 280 B After Width: | Height: | Size: 1.3 KiB |
@@ -12,20 +12,18 @@ mod command_driver;
|
||||
pub(crate) mod commands;
|
||||
pub(crate) mod dim_viewport;
|
||||
#[cfg(test)]
|
||||
mod viewport_dimension_tests;
|
||||
#[cfg(test)]
|
||||
mod dimension_preview_tests;
|
||||
mod document;
|
||||
mod drafting_settings;
|
||||
pub(crate) mod expr_eval;
|
||||
mod options_session;
|
||||
mod find_replace;
|
||||
pub(crate) mod helpers;
|
||||
mod history;
|
||||
mod layers;
|
||||
mod model_ops;
|
||||
mod navigation;
|
||||
mod mtext_editor;
|
||||
mod navigation;
|
||||
mod options_session;
|
||||
#[cfg(not(target_arch = "wasm32"))]
|
||||
pub mod plugin_host;
|
||||
mod presspull_ops;
|
||||
@@ -40,6 +38,8 @@ mod text_inline;
|
||||
mod tolerance_dialog;
|
||||
mod update;
|
||||
mod view;
|
||||
#[cfg(test)]
|
||||
mod viewport_dimension_tests;
|
||||
mod visibility;
|
||||
|
||||
pub use style_ops::StyleKind;
|
||||
@@ -768,6 +768,12 @@ pub(super) struct OpenCADStudio {
|
||||
render_mode_preview: Option<acadrust::entities::ViewportRenderMode>,
|
||||
/// Whether the Properties panel is shown on the left (PROPERTIES).
|
||||
show_properties: bool,
|
||||
show_graphic_attributes: bool,
|
||||
graphic_attribute_menu_open: bool,
|
||||
solid_fill_color_menu_open: bool,
|
||||
fill_transparency_menu_open: bool,
|
||||
gradient_editor: Option<crate::ui::window::gradient_editor::GradientEditorState>,
|
||||
hatch_editor_handles: Option<Vec<acadrust::Handle>>,
|
||||
/// Docked Insert Block panel visibility.
|
||||
pub(crate) show_block_palette: bool,
|
||||
/// Docked External References panel visibility (EXTERNALREFERENCES).
|
||||
@@ -1524,6 +1530,8 @@ pub enum ColorPickerTab {
|
||||
/// Where a colour chosen in the standalone palette window should be applied.
|
||||
#[derive(Debug, Clone)]
|
||||
pub enum ColorPickTarget {
|
||||
Gradient(u8),
|
||||
GraphicAttributesSolid,
|
||||
DimStyle(DsField),
|
||||
MLeader(&'static str),
|
||||
Table(u8, &'static str),
|
||||
@@ -2046,6 +2054,31 @@ pub enum ArrowKey {
|
||||
|
||||
#[derive(Debug, Clone)]
|
||||
pub enum Message {
|
||||
ToggleGraphicAttributeDropdown,
|
||||
CloseGraphicAttributeDropdown,
|
||||
GraphicAttributeChanged(crate::ui::window::graphic_attributes::GraphicAttribute),
|
||||
ToggleSolidFillColorDropdown,
|
||||
CloseSolidFillColorDropdown,
|
||||
SolidFillColorChanged(AcadColor),
|
||||
ToggleFillTransparencyDropdown,
|
||||
CloseFillTransparencyDropdown,
|
||||
FillTransparencyChanged(u8),
|
||||
FillTransparencyByLayer,
|
||||
FillTransparencyByBlock,
|
||||
HatchEditorOpen,
|
||||
HatchEditorClose,
|
||||
GradientEditorOpen,
|
||||
GradientColorModeChanged(crate::ui::window::gradient_editor::GradientColorMode),
|
||||
GradientColorToggle(u8),
|
||||
GradientColorChanged(u8, AcadColor),
|
||||
GradientShadeTintChanged(f32),
|
||||
GradientTypeChanged(crate::scene::model::hatch_model::GradientKind),
|
||||
GradientInvertToggled,
|
||||
GradientAngleChanged(String),
|
||||
GradientAngleReset,
|
||||
GradientCenteredChanged(bool),
|
||||
GradientApply,
|
||||
GradientCancel,
|
||||
SpaceMouseWake,
|
||||
SpaceMouseFrame(iced::time::Instant),
|
||||
SpaceMouseFocus(iced::window::Id, bool),
|
||||
@@ -4095,6 +4128,12 @@ impl OpenCADStudio {
|
||||
render_mode_menu_open: false,
|
||||
render_mode_preview: None,
|
||||
show_properties: true,
|
||||
show_graphic_attributes: true,
|
||||
graphic_attribute_menu_open: false,
|
||||
solid_fill_color_menu_open: false,
|
||||
fill_transparency_menu_open: false,
|
||||
gradient_editor: None,
|
||||
hatch_editor_handles: None,
|
||||
show_block_palette: false,
|
||||
show_external_references: false,
|
||||
show_browser: false,
|
||||
|
||||
@@ -331,8 +331,7 @@ impl OpenCADStudio {
|
||||
{
|
||||
// Esc backs out of a pending ALIASEDIT draft first, mirroring
|
||||
// the shortcut editor's capture cancel; the next Esc closes.
|
||||
if self.active_modal == Some(super::ModalKind::Aliases) && self.alias_pending_add
|
||||
{
|
||||
if self.active_modal == Some(super::ModalKind::Aliases) && self.alias_pending_add {
|
||||
return self.update(Message::AliasEditorDraftCancel);
|
||||
}
|
||||
if self.active_modal == Some(super::ModalKind::BlockDefinition) {
|
||||
@@ -394,9 +393,12 @@ impl OpenCADStudio {
|
||||
// a mouse move included — without allocating.
|
||||
#[cfg(not(target_arch = "wasm32"))]
|
||||
let active_command = |app: &Self| -> Option<(u64, Option<&'static str>)> {
|
||||
app.tabs
|
||||
.get(app.active_tab)
|
||||
.map(|tab| (tab.id, tab.active_cmd.as_ref().map(|command| command.name())))
|
||||
app.tabs.get(app.active_tab).map(|tab| {
|
||||
(
|
||||
tab.id,
|
||||
tab.active_cmd.as_ref().map(|command| command.name()),
|
||||
)
|
||||
})
|
||||
};
|
||||
#[cfg(not(target_arch = "wasm32"))]
|
||||
let command_before = active_command(self);
|
||||
@@ -414,6 +416,22 @@ impl OpenCADStudio {
|
||||
}
|
||||
}
|
||||
self.refresh_gpu_status();
|
||||
if self.gradient_editor.as_ref().is_some_and(|editor| {
|
||||
let selected = self.tabs[self.active_tab].scene.selected_handles_in_order();
|
||||
selected.len() != editor.handles.len()
|
||||
|| selected
|
||||
.iter()
|
||||
.any(|handle| !editor.handles.contains(handle))
|
||||
}) {
|
||||
self.gradient_editor = None;
|
||||
}
|
||||
if self.hatch_editor_handles.as_ref().is_some_and(|handles| {
|
||||
let selected = self.tabs[self.active_tab].scene.selected_handles_in_order();
|
||||
selected.len() != handles.len()
|
||||
|| selected.iter().any(|handle| !handles.contains(handle))
|
||||
}) {
|
||||
self.hatch_editor_handles = None;
|
||||
}
|
||||
self.show_next_startup_modal();
|
||||
self.sync_open_command_history();
|
||||
// Close the document-level first-touch transaction started by
|
||||
@@ -507,9 +525,8 @@ impl OpenCADStudio {
|
||||
Message::SpaceMouseWake => self.on_spacemouse_wake(),
|
||||
Message::TrackpadPinch(magnification) => self.on_pinch_zoom(magnification),
|
||||
Message::SpaceMouseFrame(time) => {
|
||||
self.spacemouse.frame(
|
||||
time.saturating_duration_since(self.start).as_secs_f64() * 1000.,
|
||||
);
|
||||
self.spacemouse
|
||||
.frame(time.saturating_duration_since(self.start).as_secs_f64() * 1000.);
|
||||
Task::none()
|
||||
}
|
||||
Message::SpaceMouseFocus(id, focused) => {
|
||||
@@ -561,9 +578,8 @@ impl OpenCADStudio {
|
||||
if response["ok"] == false {
|
||||
let code = response["code"].as_str().unwrap_or("failed");
|
||||
let detail = response["error"].as_str().unwrap_or("");
|
||||
self.command_line.push_error(&format!(
|
||||
"automation {op}: {code} — {detail}"
|
||||
));
|
||||
self.command_line
|
||||
.push_error(&format!("automation {op}: {code} — {detail}"));
|
||||
} else if matches!(
|
||||
response["status"].as_str(),
|
||||
Some("completed" | "cancelled" | "waiting_input")
|
||||
@@ -1282,14 +1298,16 @@ impl OpenCADStudio {
|
||||
if let Some(path) = path {
|
||||
return Task::done(Message::OpenExternal(path));
|
||||
}
|
||||
self.command_line.push_info(crate::t!(
|
||||
"Save and reopen the drawing to apply the new fonts."
|
||||
).as_ref());
|
||||
self.command_line.push_info(
|
||||
crate::t!("Save and reopen the drawing to apply the new fonts.")
|
||||
.as_ref(),
|
||||
);
|
||||
}
|
||||
Err(e) => {
|
||||
// Keep the prompt open: the user can correct a custom
|
||||
// source or retry a transient network failure.
|
||||
self.command_line.push_error(crate::tf!("Font download failed: {e}").as_ref());
|
||||
self.command_line
|
||||
.push_error(crate::tf!("Font download failed: {e}").as_ref());
|
||||
}
|
||||
}
|
||||
Task::none()
|
||||
@@ -2477,7 +2495,8 @@ impl OpenCADStudio {
|
||||
use crate::app::config::DockSide;
|
||||
use crate::ui::dock::PanelId;
|
||||
if self.dock.location(PanelId::ExternalReferences).is_none() {
|
||||
self.dock.dock(PanelId::ExternalReferences, DockSide::Right, usize::MAX);
|
||||
self.dock
|
||||
.dock(PanelId::ExternalReferences, DockSide::Right, usize::MAX);
|
||||
}
|
||||
self.dock_expanded = Some(PanelId::ExternalReferences);
|
||||
self.refresh_xref_manager();
|
||||
@@ -2615,16 +2634,13 @@ impl OpenCADStudio {
|
||||
};
|
||||
let new_raw = path.to_string_lossy().into_owned();
|
||||
self.push_undo_snapshot(i, "XREF-PATH");
|
||||
match crate::io::xref::set_ref_path(
|
||||
&mut self.tabs[i].scene.document,
|
||||
key,
|
||||
&new_raw,
|
||||
) {
|
||||
match crate::io::xref::set_ref_path(&mut self.tabs[i].scene.document, key, &new_raw)
|
||||
{
|
||||
Ok(name) => {
|
||||
self.command_line.push_output(crate::tf!(
|
||||
"XREF: Path set for \"{}\" — Reload to apply.",
|
||||
name
|
||||
).as_ref());
|
||||
self.command_line.push_output(
|
||||
crate::tf!("XREF: Path set for \"{}\" — Reload to apply.", name)
|
||||
.as_ref(),
|
||||
);
|
||||
self.post_ref_op(i);
|
||||
}
|
||||
Err(msg) => self.command_line.push_error(msg.as_str()),
|
||||
@@ -2634,7 +2650,8 @@ impl OpenCADStudio {
|
||||
}
|
||||
Message::XrefPathPickResult(Err(e)) => {
|
||||
if e != "Cancelled" {
|
||||
self.command_line.push_error(crate::tf!("XREF: {e}").as_ref());
|
||||
self.command_line
|
||||
.push_error(crate::tf!("XREF: {e}").as_ref());
|
||||
}
|
||||
Task::none()
|
||||
}
|
||||
@@ -2659,9 +2676,11 @@ impl OpenCADStudio {
|
||||
if let Some(found) = entry.found_at.clone() {
|
||||
let is_dwg = found.to_ascii_lowercase().ends_with(".dwg")
|
||||
|| found.to_ascii_lowercase().ends_with(".dxf");
|
||||
self.command_line.push_output(crate::tf!("XOPEN: opening \"{}\".", found).as_ref());
|
||||
self.command_line
|
||||
.push_output(crate::tf!("XOPEN: opening \"{}\".", found).as_ref());
|
||||
if is_dwg {
|
||||
return self.update(Message::OpenRecent(std::path::PathBuf::from(found)));
|
||||
return self
|
||||
.update(Message::OpenRecent(std::path::PathBuf::from(found)));
|
||||
} else {
|
||||
#[cfg(not(target_arch = "wasm32"))]
|
||||
let _ = open::that_detached(&found);
|
||||
@@ -2697,7 +2716,10 @@ impl OpenCADStudio {
|
||||
self.xref_manager.row_change_path_open = false;
|
||||
let prefill = if let Some(entry) = self.xref_manager.entries.get(index) {
|
||||
let saved = &entry.saved_path;
|
||||
if let Some(parent) = std::path::Path::new(saved).parent().and_then(|p| p.to_str()) {
|
||||
if let Some(parent) = std::path::Path::new(saved)
|
||||
.parent()
|
||||
.and_then(|p| p.to_str())
|
||||
{
|
||||
if !parent.is_empty() {
|
||||
format!("XREF Path Find \"{}\" ", parent)
|
||||
} else {
|
||||
@@ -3697,6 +3719,9 @@ impl OpenCADStudio {
|
||||
}
|
||||
|
||||
Message::ViewportLeftPress => {
|
||||
self.graphic_attribute_menu_open = false;
|
||||
self.solid_fill_color_menu_open = false;
|
||||
self.fill_transparency_menu_open = false;
|
||||
let sweep = self.sync_active_field_if_any();
|
||||
Task::batch(vec![sweep, self.on_viewport_left_press()])
|
||||
}
|
||||
@@ -3704,6 +3729,9 @@ impl OpenCADStudio {
|
||||
Message::ViewportLeftRelease => self.on_viewport_left_release(),
|
||||
|
||||
Message::ViewportRightPress => {
|
||||
self.graphic_attribute_menu_open = false;
|
||||
self.solid_fill_color_menu_open = false;
|
||||
self.fill_transparency_menu_open = false;
|
||||
let i = self.active_tab;
|
||||
self.ribbon.close_dropdown();
|
||||
// Shift+RMB: the one-shot snap override menu at the cursor —
|
||||
@@ -3805,7 +3833,12 @@ impl OpenCADStudio {
|
||||
self.unfocus_widgets()
|
||||
}
|
||||
|
||||
Message::ViewportMiddlePress => self.on_viewport_middle_press(),
|
||||
Message::ViewportMiddlePress => {
|
||||
self.graphic_attribute_menu_open = false;
|
||||
self.solid_fill_color_menu_open = false;
|
||||
self.fill_transparency_menu_open = false;
|
||||
self.on_viewport_middle_press()
|
||||
}
|
||||
|
||||
Message::ViewportMiddleRelease => {
|
||||
let i = self.active_tab;
|
||||
@@ -4327,7 +4360,9 @@ impl OpenCADStudio {
|
||||
continue;
|
||||
};
|
||||
let current = (
|
||||
crate::scene::pe_url_of(entity).unwrap_or_default().to_owned(),
|
||||
crate::scene::pe_url_of(entity)
|
||||
.unwrap_or_default()
|
||||
.to_owned(),
|
||||
crate::scene::pe_url_description_of(entity)
|
||||
.unwrap_or_default()
|
||||
.to_owned(),
|
||||
@@ -4779,7 +4814,9 @@ impl OpenCADStudio {
|
||||
self.tabs[i].scene.bump_geometry();
|
||||
self.refresh_properties();
|
||||
crate::entities::common::set_unit_context(
|
||||
crate::entities::common::UnitContext::from_header(&self.tabs[i].scene.document.header),
|
||||
crate::entities::common::UnitContext::from_header(
|
||||
&self.tabs[i].scene.document.header,
|
||||
),
|
||||
);
|
||||
Task::none()
|
||||
}
|
||||
@@ -4938,12 +4975,14 @@ impl OpenCADStudio {
|
||||
};
|
||||
let name = state.name.trim().to_string();
|
||||
if name.is_empty() {
|
||||
state.error_message = Some(crate::t!("Block name cannot be empty.").into_owned());
|
||||
state.error_message =
|
||||
Some(crate::t!("Block name cannot be empty.").into_owned());
|
||||
return Task::none();
|
||||
}
|
||||
if let Some(_ch) = state.invalid_name_char() {
|
||||
state.error_message = Some(
|
||||
crate::t!("Block name cannot contain: \\ / : * ? \" < > | = `").into_owned(),
|
||||
crate::t!("Block name cannot contain: \\ / : * ? \" < > | = `")
|
||||
.into_owned(),
|
||||
);
|
||||
return Task::none();
|
||||
}
|
||||
@@ -4986,12 +5025,16 @@ impl OpenCADStudio {
|
||||
let current_path = std::path::Path::new(&state.file_path);
|
||||
let file_stem = current_path.file_stem().and_then(|s| s.to_str());
|
||||
let is_default_or_block = file_stem == Some("new_block")
|
||||
|| state.existing_blocks.iter().any(|b| Some(b.as_str()) == file_stem);
|
||||
|| state
|
||||
.existing_blocks
|
||||
.iter()
|
||||
.any(|b| Some(b.as_str()) == file_stem);
|
||||
if is_default_or_block {
|
||||
let new_file_name = format!("{}.dwg", trimmed);
|
||||
if let Some(parent) = current_path.parent() {
|
||||
if !parent.as_os_str().is_empty() {
|
||||
state.file_path = parent.join(new_file_name).to_string_lossy().to_string();
|
||||
state.file_path =
|
||||
parent.join(new_file_name).to_string_lossy().to_string();
|
||||
} else {
|
||||
state.file_path = new_file_name;
|
||||
}
|
||||
@@ -5112,12 +5155,19 @@ impl OpenCADStudio {
|
||||
WblockSourceMode::Block => {
|
||||
let name = state.block_name.trim();
|
||||
if name.is_empty() {
|
||||
state.error_message =
|
||||
Some(crate::t!("Please select or enter a block name.").into_owned());
|
||||
state.error_message = Some(
|
||||
crate::t!("Please select or enter a block name.").into_owned(),
|
||||
);
|
||||
return Task::none();
|
||||
}
|
||||
let i = self.active_tab;
|
||||
if self.tabs[i].scene.document.block_records.get(name).is_none() {
|
||||
if self.tabs[i]
|
||||
.scene
|
||||
.document
|
||||
.block_records
|
||||
.get(name)
|
||||
.is_none()
|
||||
{
|
||||
state.error_message = Some(
|
||||
crate::tf!("Block \"{}\" does not exist in drawing.", name)
|
||||
.into_owned(),
|
||||
@@ -5241,10 +5291,7 @@ impl OpenCADStudio {
|
||||
}
|
||||
WblockSourceMode::Objects => {
|
||||
crate::modules::insert::wblock::extract_entities_to_doc_with_base(
|
||||
&document,
|
||||
&handles,
|
||||
base_point,
|
||||
unit,
|
||||
&document, &handles, base_point, unit,
|
||||
)
|
||||
.map_err(|e| e.to_string())?
|
||||
}
|
||||
@@ -5303,7 +5350,9 @@ impl OpenCADStudio {
|
||||
self.tabs[i].scene.bump_geometry();
|
||||
self.refresh_properties();
|
||||
crate::entities::common::set_unit_context(
|
||||
crate::entities::common::UnitContext::from_header(&self.tabs[i].scene.document.header),
|
||||
crate::entities::common::UnitContext::from_header(
|
||||
&self.tabs[i].scene.document.header,
|
||||
),
|
||||
);
|
||||
let label = crate::modules::draw::units::linear_format_label(code);
|
||||
self.command_line
|
||||
@@ -5633,8 +5682,7 @@ impl OpenCADStudio {
|
||||
Task::none()
|
||||
}
|
||||
Message::AutoConstrainReset => {
|
||||
self.auto_constrain_settings =
|
||||
super::settings::AutoConstrainSettings::default();
|
||||
self.auto_constrain_settings = super::settings::AutoConstrainSettings::default();
|
||||
self.auto_constrain_selected_row = 0;
|
||||
self.auto_constrain_distance_input =
|
||||
format!("{}", self.auto_constrain_settings.distance_tolerance);
|
||||
@@ -5673,8 +5721,7 @@ impl OpenCADStudio {
|
||||
self.auto_constrain_settings.distance_tolerance = distance;
|
||||
self.auto_constrain_settings.angle_tolerance_deg = angle;
|
||||
self.auto_constrain_settings.sanitize();
|
||||
self.auto_constrain_saved =
|
||||
Some(self.auto_constrain_settings.clone());
|
||||
self.auto_constrain_saved = Some(self.auto_constrain_settings.clone());
|
||||
self.persist_settings_if_changed();
|
||||
if matches!(action, Message::AutoConstrainOk) {
|
||||
self.close_active_modal();
|
||||
@@ -9462,6 +9509,138 @@ impl OpenCADStudio {
|
||||
Message::PlotDialogOpen => self.on_plot_dialog_open(),
|
||||
Message::PlotDlg(m) => self.on_plot_dlg(m),
|
||||
Message::BlockPalette(m) => self.on_block_palette(m),
|
||||
Message::ToggleGraphicAttributeDropdown => {
|
||||
self.graphic_attribute_menu_open ^= true;
|
||||
self.solid_fill_color_menu_open = false;
|
||||
self.fill_transparency_menu_open = false;
|
||||
Task::none()
|
||||
}
|
||||
Message::CloseGraphicAttributeDropdown => {
|
||||
self.graphic_attribute_menu_open = false;
|
||||
Task::none()
|
||||
}
|
||||
Message::GraphicAttributeChanged(value) => self.on_graphic_attribute(value),
|
||||
Message::ToggleSolidFillColorDropdown => {
|
||||
self.solid_fill_color_menu_open ^= true;
|
||||
self.graphic_attribute_menu_open = false;
|
||||
self.fill_transparency_menu_open = false;
|
||||
Task::none()
|
||||
}
|
||||
Message::CloseSolidFillColorDropdown => {
|
||||
self.solid_fill_color_menu_open = false;
|
||||
Task::none()
|
||||
}
|
||||
Message::SolidFillColorChanged(color) => self.on_solid_fill_color(color),
|
||||
Message::ToggleFillTransparencyDropdown => {
|
||||
self.fill_transparency_menu_open ^= true;
|
||||
self.graphic_attribute_menu_open = false;
|
||||
self.solid_fill_color_menu_open = false;
|
||||
Task::none()
|
||||
}
|
||||
Message::CloseFillTransparencyDropdown => {
|
||||
self.fill_transparency_menu_open = false;
|
||||
Task::none()
|
||||
}
|
||||
Message::FillTransparencyChanged(value) => self.on_fill_transparency(
|
||||
acadrust::types::Transparency::from_percent(f64::from(value) / 100.0),
|
||||
),
|
||||
Message::FillTransparencyByLayer => {
|
||||
self.on_fill_transparency(acadrust::types::Transparency::BY_LAYER)
|
||||
}
|
||||
Message::FillTransparencyByBlock => {
|
||||
self.on_fill_transparency(acadrust::types::Transparency::BY_BLOCK)
|
||||
}
|
||||
Message::HatchEditorOpen => {
|
||||
self.gradient_editor = None;
|
||||
self.hatch_editor_handles = Some(
|
||||
self.tabs[self.active_tab]
|
||||
.scene
|
||||
.selected_handles_in_order(),
|
||||
);
|
||||
Task::none()
|
||||
}
|
||||
Message::HatchEditorClose => {
|
||||
self.hatch_editor_handles = None;
|
||||
Task::none()
|
||||
}
|
||||
Message::GradientEditorOpen => {
|
||||
self.hatch_editor_handles = None;
|
||||
self.open_gradient_editor();
|
||||
Task::none()
|
||||
}
|
||||
Message::GradientColorModeChanged(mode) => {
|
||||
if let Some(s) = &mut self.gradient_editor {
|
||||
s.color_mode = mode;
|
||||
}
|
||||
Task::none()
|
||||
}
|
||||
Message::GradientColorToggle(index) => {
|
||||
if let Some(s) = &mut self.gradient_editor {
|
||||
s.color_open = if s.color_open == Some(index) {
|
||||
None
|
||||
} else {
|
||||
Some(index)
|
||||
};
|
||||
}
|
||||
Task::none()
|
||||
}
|
||||
Message::GradientColorChanged(index, color) => {
|
||||
if let Some(s) = &mut self.gradient_editor {
|
||||
if index == 1 {
|
||||
s.color_1 = color
|
||||
} else {
|
||||
s.color_2 = color
|
||||
};
|
||||
s.color_open = None;
|
||||
}
|
||||
Task::none()
|
||||
}
|
||||
Message::GradientShadeTintChanged(value) => {
|
||||
if let Some(s) = &mut self.gradient_editor {
|
||||
s.shade_tint = value.clamp(0.0, 1.0);
|
||||
}
|
||||
Task::none()
|
||||
}
|
||||
Message::GradientTypeChanged(kind) => {
|
||||
if let Some(s) = &mut self.gradient_editor {
|
||||
s.kind = kind;
|
||||
if kind == crate::scene::model::hatch_model::GradientKind::Linear {
|
||||
s.inverted = false;
|
||||
}
|
||||
}
|
||||
Task::none()
|
||||
}
|
||||
Message::GradientInvertToggled => {
|
||||
if let Some(s) = &mut self.gradient_editor {
|
||||
if s.kind != crate::scene::model::hatch_model::GradientKind::Linear {
|
||||
s.inverted ^= true;
|
||||
}
|
||||
}
|
||||
Task::none()
|
||||
}
|
||||
Message::GradientAngleChanged(value) => {
|
||||
if let Some(s) = &mut self.gradient_editor {
|
||||
s.angle = value;
|
||||
}
|
||||
Task::none()
|
||||
}
|
||||
Message::GradientAngleReset => {
|
||||
if let Some(s) = &mut self.gradient_editor {
|
||||
s.angle = "0.0°".into();
|
||||
}
|
||||
Task::none()
|
||||
}
|
||||
Message::GradientCenteredChanged(value) => {
|
||||
if let Some(s) = &mut self.gradient_editor {
|
||||
s.centered = value;
|
||||
}
|
||||
Task::none()
|
||||
}
|
||||
Message::GradientApply => self.apply_gradient_editor(),
|
||||
Message::GradientCancel => {
|
||||
self.gradient_editor = None;
|
||||
Task::none()
|
||||
}
|
||||
Message::Dock(m) => self.on_dock(m),
|
||||
Message::PrintAllOpen => self.on_print_all_open(),
|
||||
Message::PrintAllToggle(name) => {
|
||||
@@ -10598,6 +10777,9 @@ impl OpenCADStudio {
|
||||
Task::none()
|
||||
}
|
||||
Message::OpenColorWindow(target, color) => {
|
||||
if matches!(target, crate::app::ColorPickTarget::GraphicAttributesSolid) {
|
||||
self.solid_fill_color_menu_open = false;
|
||||
}
|
||||
self.color_pick_target = Some((target, color));
|
||||
|
||||
// Always open the shared CAD colour picker on the indexed ACI page.
|
||||
|
||||
@@ -4,13 +4,12 @@
|
||||
use super::util::*;
|
||||
use super::{format_size, VIEWCUBE_HIT_SIZE};
|
||||
use crate::app::helpers::{
|
||||
parse_coord, polar_constrain_near, ucs_rotate_vec, ucs_to_wcs, ucs_z_axis,
|
||||
CoordKind,
|
||||
parse_coord, polar_constrain_near, ucs_rotate_vec, ucs_to_wcs, ucs_z_axis, CoordKind,
|
||||
};
|
||||
use crate::app::{Message, OpenCADStudio, POLY_START_DELAY_MS};
|
||||
use crate::modules::ModuleEvent;
|
||||
use crate::scene::pick::grip::{find_hit_grip, find_hit_grip_paper, find_hit_grip_rte, GripEdit};
|
||||
use crate::scene::model::object::GripApply;
|
||||
use crate::scene::pick::grip::{find_hit_grip, find_hit_grip_paper, find_hit_grip_rte, GripEdit};
|
||||
use crate::scene::{
|
||||
self, hover_id, CubeRegion, Scene, VIEWCUBE_DRAW_PX, VIEWCUBE_PAD, VIEWCUBE_PX,
|
||||
};
|
||||
@@ -20,7 +19,6 @@ use acadrust::{EntityType as AcadEntityType, Handle};
|
||||
use iced::time::Instant;
|
||||
use iced::{mouse, Point, Task};
|
||||
|
||||
|
||||
impl OpenCADStudio {
|
||||
pub(in crate::app) fn mlstyle_mut(
|
||||
&mut self,
|
||||
@@ -107,7 +105,10 @@ impl OpenCADStudio {
|
||||
}
|
||||
}
|
||||
|
||||
pub(in crate::app) fn tablestyle_mut(&mut self, tab: usize) -> Option<&mut acadrust::objects::TableStyle> {
|
||||
pub(in crate::app) fn tablestyle_mut(
|
||||
&mut self,
|
||||
tab: usize,
|
||||
) -> Option<&mut acadrust::objects::TableStyle> {
|
||||
use acadrust::objects::ObjectType;
|
||||
let name = self.tablestyle_selected.clone();
|
||||
self.tabs[tab]
|
||||
@@ -211,7 +212,10 @@ impl OpenCADStudio {
|
||||
|
||||
/// Mutable access to the currently selected multileader style.
|
||||
|
||||
pub(in crate::app) fn mleaderstyle_mut(&mut self, tab: usize) -> Option<&mut acadrust::objects::MultiLeaderStyle> {
|
||||
pub(in crate::app) fn mleaderstyle_mut(
|
||||
&mut self,
|
||||
tab: usize,
|
||||
) -> Option<&mut acadrust::objects::MultiLeaderStyle> {
|
||||
use acadrust::objects::ObjectType;
|
||||
let name = self.mleaderstyle_selected.clone();
|
||||
self.tabs[tab]
|
||||
@@ -1129,6 +1133,12 @@ pub(super) fn on_text_style_dialog_open(&mut self) -> Task<Message> {
|
||||
}
|
||||
let s = crate::ui::color_select::color_to_aci_string(color);
|
||||
let edit = match self.color_pick_target.take().map(|(target, _)| target) {
|
||||
Some(crate::app::ColorPickTarget::Gradient(index)) => {
|
||||
Some(Message::GradientColorChanged(index, color))
|
||||
}
|
||||
Some(crate::app::ColorPickTarget::GraphicAttributesSolid) => {
|
||||
Some(Message::SolidFillColorChanged(color))
|
||||
}
|
||||
Some(crate::app::ColorPickTarget::DimStyle(f)) => Some(Message::DsEdit(f, s)),
|
||||
Some(crate::app::ColorPickTarget::MLeader(f)) => {
|
||||
Some(Message::MLeaderStyleEdit { field: f, value: s })
|
||||
|
||||
@@ -613,7 +613,10 @@ bg={bg_ms:.1}ms n={view_count}"
|
||||
let control_polygon = tab.selected_handle.and_then(|handle| {
|
||||
let spline = match tab.scene.document.get_entity(handle) {
|
||||
Some(acadrust::EntityType::Spline(spline))
|
||||
if crate::entities::spline::shows_control_vertices(spline) => spline,
|
||||
if crate::entities::spline::shows_control_vertices(spline) =>
|
||||
{
|
||||
spline
|
||||
}
|
||||
_ => return None,
|
||||
};
|
||||
if tab
|
||||
@@ -1109,8 +1112,12 @@ bg={bg_ms:.1}ms n={view_count}"
|
||||
(None, _) if rectangle_values.is_some() => {
|
||||
let (width, height) = rectangle_values.unwrap();
|
||||
match f.role {
|
||||
crate::command::DynRole::Width => crate::entities::common::format_length(width),
|
||||
crate::command::DynRole::Height => crate::entities::common::format_length(height),
|
||||
crate::command::DynRole::Width => {
|
||||
crate::entities::common::format_length(width)
|
||||
}
|
||||
crate::command::DynRole::Height => {
|
||||
crate::entities::common::format_length(height)
|
||||
}
|
||||
_ => String::new(),
|
||||
}
|
||||
}
|
||||
@@ -1839,6 +1846,7 @@ bg={bg_ms:.1}ms n={view_count}"
|
||||
}
|
||||
match id {
|
||||
crate::ui::dock::PanelId::Properties => self.show_properties,
|
||||
crate::ui::dock::PanelId::GraphicAttributes => self.show_graphic_attributes,
|
||||
crate::ui::dock::PanelId::BlockPalette => self.show_block_palette,
|
||||
crate::ui::dock::PanelId::ExternalReferences => self.show_external_references,
|
||||
crate::ui::dock::PanelId::Browser => self.show_browser,
|
||||
@@ -2742,7 +2750,9 @@ impl OpenCADStudio {
|
||||
} else {
|
||||
Subscription::none()
|
||||
},
|
||||
self.spacemouse.subscription().map(|_| Message::SpaceMouseWake),
|
||||
self.spacemouse
|
||||
.subscription()
|
||||
.map(|_| Message::SpaceMouseWake),
|
||||
crate::input::trackpad::subscription().map(Message::TrackpadPinch),
|
||||
event::listen_with(|event, _, id| match event {
|
||||
iced::Event::Window(window::Event::Focused) => {
|
||||
@@ -2929,18 +2939,42 @@ impl OpenCADStudio {
|
||||
let auto_collapse = self.dock.auto_collapse(id);
|
||||
let panel: Element<'_, Message> = match id {
|
||||
crate::ui::dock::PanelId::Properties => tab.properties.view(width, auto_collapse),
|
||||
crate::ui::dock::PanelId::GraphicAttributes => {
|
||||
let draw_depth = tab.scene.draw_depth_map();
|
||||
crate::ui::window::graphic_attributes::view(
|
||||
&tab.scene.document,
|
||||
&tab.scene
|
||||
.selected_entities()
|
||||
.into_iter()
|
||||
.map(|(h, _)| h)
|
||||
.collect::<Vec<_>>(),
|
||||
draw_depth.as_ref(),
|
||||
width,
|
||||
auto_collapse,
|
||||
side,
|
||||
self.graphic_attribute_menu_open,
|
||||
self.solid_fill_color_menu_open,
|
||||
self.fill_transparency_menu_open,
|
||||
self.hatch_editor_handles.is_some(),
|
||||
self.gradient_editor.as_ref(),
|
||||
matches!(
|
||||
self.color_pick_target.as_ref().map(|(target, _)| target),
|
||||
Some(crate::app::ColorPickTarget::Gradient(_))
|
||||
),
|
||||
)
|
||||
}
|
||||
crate::ui::dock::PanelId::BlockPalette => {
|
||||
crate::ui::window::block_palette::view(&self.block_palette, width, auto_collapse)
|
||||
}
|
||||
crate::ui::dock::PanelId::ExternalReferences => self.xref_manager.view(
|
||||
width,
|
||||
auto_collapse,
|
||||
tab.xref_missing,
|
||||
&tab.scene.document,
|
||||
),
|
||||
crate::ui::dock::PanelId::ExternalReferences => {
|
||||
self.xref_manager
|
||||
.view(width, auto_collapse, tab.xref_missing, &tab.scene.document)
|
||||
}
|
||||
crate::ui::dock::PanelId::Browser => crate::ui::window::browser::view(
|
||||
&tab.scene.document,
|
||||
tab.sketch_session.as_ref().map(|session| session.name.as_str()),
|
||||
tab.sketch_session
|
||||
.as_ref()
|
||||
.map(|session| session.name.as_str()),
|
||||
width,
|
||||
auto_collapse,
|
||||
),
|
||||
|
||||
@@ -261,6 +261,13 @@ impl HatchCommand {
|
||||
|
||||
pub fn with_origin(mut self, origin: [f64; 2]) -> Self { self.default_origin = origin; self }
|
||||
|
||||
pub fn with_pattern(mut self, name: impl Into<String>, pattern: HatchPattern) -> Self {
|
||||
self.pattern_override = Some((name.into(), pattern));
|
||||
self
|
||||
}
|
||||
|
||||
pub fn finish_selected(mut self) -> CmdResult { self.on_enter() }
|
||||
|
||||
fn set_object_selection(&mut self, handles: Vec<Handle>) {
|
||||
let mut segments = Vec::new();
|
||||
for handle in &handles {
|
||||
|
||||
@@ -103,7 +103,7 @@ fn family_from_stored_line(
|
||||
|
||||
/// Preserve the selected hue while moving its HSL lightness towards the
|
||||
/// persisted one-colour tint/shade target (0 = black, 1 = white).
|
||||
fn gradient_tint_color(base: [f32; 4], target: f32) -> [f32; 4] {
|
||||
pub(crate) fn gradient_tint_color(base: [f32; 4], target: f32) -> [f32; 4] {
|
||||
let max = base[0].max(base[1]).max(base[2]);
|
||||
let min = base[0].min(base[1]).min(base[2]);
|
||||
let lightness = (max + min) * 0.5;
|
||||
@@ -2002,9 +2002,11 @@ impl Scene {
|
||||
let range = boundary.len()..boundary.len() + ring.len();
|
||||
boundary.extend(ring);
|
||||
local_boundary.extend(local_ring.iter().map(|&[x, y]| [x as f32, y as f32]));
|
||||
if path.edges.iter().any(|edge| {
|
||||
matches!(edge, acadrust::entities::BoundaryEdge::Spline(_))
|
||||
}) {
|
||||
if path
|
||||
.edges
|
||||
.iter()
|
||||
.any(|edge| matches!(edge, acadrust::entities::BoundaryEdge::Spline(_)))
|
||||
{
|
||||
spline_paths.push((path, local_ring, range));
|
||||
}
|
||||
boundary_exterior.push(depth == 0);
|
||||
@@ -2210,13 +2212,21 @@ impl Scene {
|
||||
}
|
||||
let project = |point: [f64; 2]| {
|
||||
let [x, y] = to_xy(point[0], point[1]);
|
||||
[(x - world_origin[0]) as f32 as f64, (y - world_origin[1]) as f32 as f64]
|
||||
[
|
||||
(x - world_origin[0]) as f32 as f64,
|
||||
(y - world_origin[1]) as f32 as f64,
|
||||
]
|
||||
};
|
||||
for (path, ring, range) in spline_paths.into_iter().rev() {
|
||||
if range.end > boundary.len() {
|
||||
continue;
|
||||
}
|
||||
let curves = || path.edges.iter().filter_map(crate::entities::hatch::edge_curve).collect();
|
||||
let curves = || {
|
||||
path.edges
|
||||
.iter()
|
||||
.filter_map(crate::entities::hatch::edge_curve)
|
||||
.collect()
|
||||
};
|
||||
if let Some(refined) =
|
||||
cadkernel::geom2d::refine_spline_boundary(&ring, curves, &project)
|
||||
{
|
||||
@@ -2224,7 +2234,10 @@ impl Scene {
|
||||
continue;
|
||||
}
|
||||
boundary.splice(range.clone(), refined.iter().map(|p| to_xy(p[0], p[1])));
|
||||
local_boundary.splice(range, refined.into_iter().map(|[x, y]| [x as f32, y as f32]));
|
||||
local_boundary.splice(
|
||||
range,
|
||||
refined.into_iter().map(|[x, y]| [x as f32, y as f32]),
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -37,6 +37,7 @@ pub use page_setup::{apply_default_page_setup, document_page_setups, rotated_mar
|
||||
mod dwg_native_constraints;
|
||||
mod entity;
|
||||
pub use entity::CreateBlockOptions;
|
||||
pub(crate) use entity::gradient_tint_color;
|
||||
#[cfg(test)]
|
||||
mod hatch_boundary;
|
||||
mod group_layer;
|
||||
|
||||
@@ -232,18 +232,7 @@ fn color_selector_with_indicator<'a>(
|
||||
}
|
||||
|
||||
let popup = container(color_list(extras, on_select, on_more))
|
||||
.style(|theme: &Theme| {
|
||||
let palette = theme.palette();
|
||||
container::Style {
|
||||
background: Some(Background::Color(palette.background.weak.color)),
|
||||
border: Border {
|
||||
color: palette.background.neutral.color,
|
||||
width: 1.0,
|
||||
radius: 2.0.into(),
|
||||
},
|
||||
..Default::default()
|
||||
}
|
||||
})
|
||||
.style(popup_panel_style)
|
||||
.padding(2);
|
||||
|
||||
// `DropDown` keeps the popup outside the surrounding form layout and
|
||||
@@ -257,7 +246,20 @@ fn color_selector_with_indicator<'a>(
|
||||
.into()
|
||||
}
|
||||
|
||||
fn list_row_style(theme: &Theme, status: button::Status) -> button::Style {
|
||||
pub(crate) fn popup_panel_style(theme: &Theme) -> container::Style {
|
||||
let palette = theme.palette();
|
||||
container::Style {
|
||||
background: Some(Background::Color(palette.background.weak.color)),
|
||||
border: Border {
|
||||
color: palette.background.neutral.color,
|
||||
width: 1.0,
|
||||
radius: 2.0.into(),
|
||||
},
|
||||
..Default::default()
|
||||
}
|
||||
}
|
||||
|
||||
pub(crate) fn list_row_style(theme: &Theme, status: button::Status) -> button::Style {
|
||||
let palette = theme.palette();
|
||||
let hovered = matches!(status, button::Status::Hovered);
|
||||
let text_color = if hovered {
|
||||
|
||||
@@ -40,6 +40,7 @@ pub enum DockMsg {
|
||||
#[serde(rename_all = "snake_case")]
|
||||
pub enum PanelId {
|
||||
Properties,
|
||||
GraphicAttributes,
|
||||
BlockPalette,
|
||||
ExternalReferences,
|
||||
/// Outline of the drawing's origin planes, open sketch and solid bodies.
|
||||
@@ -51,6 +52,7 @@ impl PanelId {
|
||||
pub fn title(self) -> &'static str {
|
||||
match self {
|
||||
PanelId::Properties => "Properties",
|
||||
PanelId::GraphicAttributes => "Graphic Attributes",
|
||||
PanelId::BlockPalette => "Block Palette",
|
||||
PanelId::ExternalReferences => "External References",
|
||||
PanelId::Browser => "Browser",
|
||||
@@ -61,6 +63,7 @@ impl PanelId {
|
||||
fn default_width(self) -> f32 {
|
||||
match self {
|
||||
PanelId::Properties => 250.0,
|
||||
PanelId::GraphicAttributes => 250.0,
|
||||
PanelId::BlockPalette => 260.0,
|
||||
PanelId::ExternalReferences => 460.0,
|
||||
PanelId::Browser => 230.0,
|
||||
@@ -120,7 +123,7 @@ pub struct DockState {
|
||||
impl Default for DockState {
|
||||
fn default() -> Self {
|
||||
Self {
|
||||
left: vec![PanelId::Properties],
|
||||
left: vec![PanelId::Properties, PanelId::GraphicAttributes],
|
||||
right: vec![PanelId::BlockPalette],
|
||||
panels: BTreeMap::new(),
|
||||
}
|
||||
@@ -149,12 +152,20 @@ impl DockState {
|
||||
pub fn ensure_settings(&mut self) {
|
||||
for id in [
|
||||
PanelId::Properties,
|
||||
PanelId::GraphicAttributes,
|
||||
PanelId::BlockPalette,
|
||||
PanelId::ExternalReferences,
|
||||
PanelId::Browser,
|
||||
] {
|
||||
self.panels.entry(id).or_insert_with(|| DockPanel::for_id(id));
|
||||
}
|
||||
if self.location(PanelId::GraphicAttributes).is_none() {
|
||||
if let Some((side, index)) = self.location(PanelId::Properties) {
|
||||
self.stack_mut(side).insert(index + 1, PanelId::GraphicAttributes);
|
||||
} else {
|
||||
self.left.push(PanelId::GraphicAttributes);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// Where (if anywhere) a panel is currently docked.
|
||||
@@ -262,6 +273,7 @@ mod tests {
|
||||
fn default_docks_each_known_panel_on_an_edge() {
|
||||
let state = DockState::default();
|
||||
assert_eq!(state.location(PanelId::Properties), Some((DockSide::Left, 0)));
|
||||
assert_eq!(state.location(PanelId::GraphicAttributes), Some((DockSide::Left, 1)));
|
||||
assert_eq!(
|
||||
state.location(PanelId::BlockPalette),
|
||||
Some((DockSide::Right, 0))
|
||||
@@ -273,6 +285,7 @@ mod tests {
|
||||
let mut state = DockState::default();
|
||||
state.ensure_settings();
|
||||
assert_eq!(state.width(PanelId::Properties, 1600.0), 250.0);
|
||||
assert_eq!(state.width(PanelId::GraphicAttributes, 1600.0), 250.0);
|
||||
assert_eq!(state.width(PanelId::BlockPalette, 1600.0), 260.0);
|
||||
assert!(!state.auto_collapse(PanelId::Properties));
|
||||
}
|
||||
@@ -286,7 +299,7 @@ mod tests {
|
||||
Some((DockSide::Right, 0))
|
||||
);
|
||||
// It no longer occupies the left edge.
|
||||
assert!(state.left.is_empty());
|
||||
assert_eq!(state.left, vec![PanelId::GraphicAttributes]);
|
||||
assert_eq!(state.right.len(), 2);
|
||||
}
|
||||
|
||||
@@ -360,4 +373,4 @@ mod tests {
|
||||
assert_eq!(drop_index(10.0, 2, 900.0), 0);
|
||||
assert_eq!(drop_index(890.0, 2, 900.0), 2);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -95,6 +95,7 @@ impl fmt::Display for SelectionGroup {
|
||||
#[derive(Clone)]
|
||||
struct HatchPatternPreview {
|
||||
pattern: crate::scene::model::hatch_model::HatchPattern,
|
||||
padding: f32,
|
||||
}
|
||||
|
||||
impl canvas::Program<Message> for HatchPatternPreview {
|
||||
@@ -112,7 +113,7 @@ impl canvas::Program<Message> for HatchPatternPreview {
|
||||
|
||||
let mut frame = canvas::Frame::new(renderer, bounds.size());
|
||||
let palette = theme.palette();
|
||||
let pad = 4.0;
|
||||
let pad = self.padding;
|
||||
let sample = canvas::Path::rectangle(
|
||||
Point::new(pad, pad),
|
||||
Size::new(
|
||||
@@ -201,6 +202,31 @@ fn hatch_preview_scale(pattern: &crate::scene::model::hatch_model::HatchPattern)
|
||||
}
|
||||
}
|
||||
|
||||
pub(crate) fn hatch_pattern_preview(
|
||||
pattern: crate::scene::model::hatch_model::HatchPattern,
|
||||
height: f32,
|
||||
) -> Element<'static, Message> {
|
||||
canvas(HatchPatternPreview {
|
||||
pattern,
|
||||
padding: 4.0,
|
||||
})
|
||||
.width(Length::Fill)
|
||||
.height(height)
|
||||
.into()
|
||||
}
|
||||
|
||||
pub(crate) fn compact_hatch_pattern_preview(
|
||||
pattern: crate::scene::model::hatch_model::HatchPattern,
|
||||
) -> Element<'static, Message> {
|
||||
canvas(HatchPatternPreview {
|
||||
pattern,
|
||||
padding: 0.5,
|
||||
})
|
||||
.width(Length::Fill)
|
||||
.height(ROW_H)
|
||||
.into()
|
||||
}
|
||||
|
||||
fn hatch_pattern_matches(
|
||||
entry: &crate::scene::model::hatch_patterns::PatternEntry,
|
||||
search: &str,
|
||||
@@ -1553,11 +1579,7 @@ impl PropertiesPanel {
|
||||
let selected = current.eq_ignore_ascii_case(&entry.name);
|
||||
let focused = self.hatch_pattern_focus == index;
|
||||
let name = entry.name.clone();
|
||||
let preview = canvas(HatchPatternPreview {
|
||||
pattern: entry.gpu.clone(),
|
||||
})
|
||||
.width(Length::Fill)
|
||||
.height(PATTERN_PREVIEW_H);
|
||||
let preview = hatch_pattern_preview(entry.gpu.clone(), PATTERN_PREVIEW_H);
|
||||
let card = button(
|
||||
column![
|
||||
preview,
|
||||
|
||||
@@ -0,0 +1,424 @@
|
||||
use crate::app::Message;
|
||||
use crate::scene::model::hatch_model::GradientKind;
|
||||
use acadrust::types::Color as AcadColor;
|
||||
use iced::widget::{
|
||||
button, checkbox, column, container, image, mouse_area, row, slider, text, text_input, tooltip,
|
||||
Space,
|
||||
};
|
||||
use iced::{Background, Border, Color, Element, Fill, Length, Theme};
|
||||
|
||||
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
|
||||
pub enum GradientColorMode {
|
||||
One,
|
||||
Two,
|
||||
}
|
||||
|
||||
#[derive(Debug, Clone)]
|
||||
pub struct GradientEditorState {
|
||||
pub handles: Vec<acadrust::Handle>,
|
||||
pub color_mode: GradientColorMode,
|
||||
pub color_1: AcadColor,
|
||||
pub color_2: AcadColor,
|
||||
pub shade_tint: f32,
|
||||
pub kind: GradientKind,
|
||||
pub inverted: bool,
|
||||
pub angle: String,
|
||||
pub centered: bool,
|
||||
pub color_open: Option<u8>,
|
||||
}
|
||||
|
||||
impl GradientEditorState {
|
||||
pub fn from_hatch(
|
||||
handles: Vec<acadrust::Handle>,
|
||||
hatch: &acadrust::entities::Hatch,
|
||||
) -> Self {
|
||||
let color = |index: usize| {
|
||||
hatch
|
||||
.gradient_color
|
||||
.colors
|
||||
.get(index)
|
||||
.map(|entry| entry.color.clone())
|
||||
.unwrap_or(AcadColor::Index(7))
|
||||
};
|
||||
let (kind, inverted) = GradientKind::from_name(&hatch.gradient_color.name);
|
||||
Self {
|
||||
handles,
|
||||
color_mode: if hatch.gradient_color.is_single_color {
|
||||
GradientColorMode::One
|
||||
} else {
|
||||
GradientColorMode::Two
|
||||
},
|
||||
color_1: color(0),
|
||||
color_2: color(1),
|
||||
shade_tint: hatch.gradient_color.color_tint as f32,
|
||||
kind,
|
||||
inverted,
|
||||
angle: format!("{:.1}°", hatch.gradient_color.angle.to_degrees()),
|
||||
centered: hatch.gradient_color.shift.abs() < 1e-9,
|
||||
color_open: None,
|
||||
}
|
||||
}
|
||||
|
||||
pub fn angle_radians(&self) -> f64 {
|
||||
self.angle
|
||||
.trim()
|
||||
.trim_end_matches('°')
|
||||
.replace(',', ".")
|
||||
.parse::<f64>()
|
||||
.unwrap_or(0.0)
|
||||
.rem_euclid(360.0)
|
||||
.to_radians()
|
||||
}
|
||||
}
|
||||
|
||||
fn rgb(color: &AcadColor) -> Color {
|
||||
let (r, g, b) = match color {
|
||||
AcadColor::Rgb { r, g, b } => (*r, *g, *b),
|
||||
AcadColor::Index(i) => {
|
||||
acadrust::types::aci_table::aci_to_rgb(*i).unwrap_or((128, 128, 128))
|
||||
}
|
||||
_ => (255, 255, 255),
|
||||
};
|
||||
Color::from_rgb8(r, g, b)
|
||||
}
|
||||
|
||||
fn rgb_array(color: &AcadColor) -> [f32; 4] {
|
||||
let c = rgb(color);
|
||||
[c.r, c.g, c.b, 1.0]
|
||||
}
|
||||
|
||||
fn mix_rgba(a: Color, b: Color, t: f32) -> [u8; 4] {
|
||||
let channel = |a: f32, b: f32| ((a + (b - a) * t) * 255.0).round() as u8;
|
||||
[channel(a.r, b.r), channel(a.g, b.g), channel(a.b, b.b), 255]
|
||||
}
|
||||
|
||||
fn preview_sized(
|
||||
state: &GradientEditorState,
|
||||
width: u32,
|
||||
height: u32,
|
||||
) -> iced::widget::image::Handle {
|
||||
let c1 = rgb(&state.color_1);
|
||||
let c2 = if state.color_mode == GradientColorMode::One {
|
||||
let c = crate::scene::gradient_tint_color(rgb_array(&state.color_1), state.shade_tint);
|
||||
Color::from_rgb(c[0], c[1], c[2])
|
||||
} else {
|
||||
rgb(&state.color_2)
|
||||
};
|
||||
let angle = state.angle_radians();
|
||||
let frame = cadkernel::geom2d::gradient_frame(
|
||||
&[[0.0, 0.0], [1.0, 0.0], [1.0, 1.0], [0.0, 1.0]],
|
||||
angle,
|
||||
if state.centered { 0.0 } else { 1.0 },
|
||||
cadkernel::geom2d::Tolerance::default(),
|
||||
)
|
||||
.expect("the preview rectangle always defines a gradient frame");
|
||||
let (sin, cos) = angle.sin_cos();
|
||||
let radial = state.kind.radial();
|
||||
let mut pixels = Vec::with_capacity((width * height * 4) as usize);
|
||||
for y in 0..height {
|
||||
let py = y as f64 / (height - 1) as f64;
|
||||
for x in 0..width {
|
||||
let px = x as f64 / (width - 1) as f64;
|
||||
let mut t = if radial {
|
||||
((px - frame.center[0]).hypot(py - frame.center[1]) / frame.radius).clamp(0.0, 1.0)
|
||||
as f32
|
||||
} else {
|
||||
(((px * cos + py * sin) - frame.projection_min) / frame.projection_span)
|
||||
.clamp(0.0, 1.0) as f32
|
||||
};
|
||||
if state.kind == GradientKind::Cylinder {
|
||||
t = 1.0 - (2.0 * t - 1.0).abs();
|
||||
} else if state.kind == GradientKind::Curved {
|
||||
t *= t;
|
||||
} else if state.kind == GradientKind::Hemispherical {
|
||||
t = t.sqrt();
|
||||
}
|
||||
if state.inverted {
|
||||
t = 1.0 - t;
|
||||
}
|
||||
pixels.extend_from_slice(&if radial {
|
||||
mix_rgba(c2, c1, t)
|
||||
} else {
|
||||
mix_rgba(c1, c2, t)
|
||||
});
|
||||
}
|
||||
}
|
||||
iced::widget::image::Handle::from_rgba(width, height, pixels)
|
||||
}
|
||||
|
||||
fn preview(state: &GradientEditorState) -> iced::widget::image::Handle {
|
||||
preview_sized(state, 370, 80)
|
||||
}
|
||||
|
||||
pub(crate) fn compact_preview(
|
||||
hatch: &acadrust::entities::Hatch,
|
||||
) -> iced::widget::image::Handle {
|
||||
preview_sized(&GradientEditorState::from_hatch(Vec::new(), hatch), 256, 26)
|
||||
}
|
||||
|
||||
fn icon(kind: GradientKind, inverted: bool) -> &'static [u8] {
|
||||
match (kind, inverted) {
|
||||
(GradientKind::Linear, _) => {
|
||||
include_bytes!("../../../assets/icons/gradient/gradient_linear.svg")
|
||||
}
|
||||
(GradientKind::Cylinder, false) => {
|
||||
include_bytes!("../../../assets/icons/gradient/gradient_cylindrical.svg")
|
||||
}
|
||||
(GradientKind::Cylinder, true) => {
|
||||
include_bytes!("../../../assets/icons/gradient/gradient_cylindrical_inverted.svg")
|
||||
}
|
||||
(GradientKind::Spherical, false) => {
|
||||
include_bytes!("../../../assets/icons/gradient/gradient_spherical.svg")
|
||||
}
|
||||
(GradientKind::Spherical, true) => {
|
||||
include_bytes!("../../../assets/icons/gradient/gradient_spherical_inverted.svg")
|
||||
}
|
||||
(GradientKind::Hemispherical, false) => {
|
||||
include_bytes!("../../../assets/icons/gradient/gradient_hemispherical.svg")
|
||||
}
|
||||
(GradientKind::Hemispherical, true) => {
|
||||
include_bytes!("../../../assets/icons/gradient/gradient_hemispherical_inverted.svg")
|
||||
}
|
||||
(GradientKind::Curved, false) => {
|
||||
include_bytes!("../../../assets/icons/gradient/gradient_curved.svg")
|
||||
}
|
||||
(GradientKind::Curved, true) => {
|
||||
include_bytes!("../../../assets/icons/gradient/gradient_curved_inverted.svg")
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
pub fn view(state: &GradientEditorState, dimmed: bool) -> Element<'_, Message> {
|
||||
let segment = |label, mode| {
|
||||
let selected = state.color_mode == mode;
|
||||
button(text(label).size(11))
|
||||
.on_press(Message::GradientColorModeChanged(mode))
|
||||
.width(Fill)
|
||||
.style(move |theme: &Theme, status| {
|
||||
if selected {
|
||||
button::primary(theme, status)
|
||||
} else {
|
||||
button::secondary(theme, status)
|
||||
}
|
||||
})
|
||||
};
|
||||
let color_field = |label: &'static str, index, color: AcadColor| {
|
||||
let (background, _) = crate::ui::properties::acad_color_display(color.clone());
|
||||
let value = crate::ui::color_select::color_display_name(color.clone());
|
||||
let picker = button(
|
||||
row![
|
||||
crate::ui::color_select::swatch(background),
|
||||
text(value).size(10),
|
||||
Space::new().width(Fill),
|
||||
]
|
||||
.spacing(5)
|
||||
.align_y(iced::Center),
|
||||
)
|
||||
.on_press(Message::OpenColorWindow(
|
||||
crate::app::ColorPickTarget::Gradient(index),
|
||||
color,
|
||||
))
|
||||
.style(|theme: &Theme, status| {
|
||||
let palette = theme.palette();
|
||||
button::Style {
|
||||
background: Some(Background::Color(
|
||||
if matches!(status, button::Status::Hovered) {
|
||||
palette.background.weak.color
|
||||
} else {
|
||||
palette.background.base.color
|
||||
},
|
||||
)),
|
||||
text_color: palette.background.base.text,
|
||||
border: Border {
|
||||
color: palette.background.neutral.color,
|
||||
width: 1.0,
|
||||
radius: 2.0.into(),
|
||||
},
|
||||
..Default::default()
|
||||
}
|
||||
})
|
||||
.height(crate::ui::ROW_H)
|
||||
.padding([3, 6])
|
||||
.width(Fill);
|
||||
row![text(label).size(11).width(43), picker]
|
||||
.spacing(4)
|
||||
.align_y(iced::Center)
|
||||
.width(Fill)
|
||||
};
|
||||
let kinds = [
|
||||
(GradientKind::Linear, "Linear"),
|
||||
(GradientKind::Cylinder, "Cyl."),
|
||||
(GradientKind::Spherical, "Spher."),
|
||||
(GradientKind::Hemispherical, "Hemi."),
|
||||
(GradientKind::Curved, "Curved"),
|
||||
];
|
||||
let mut type_row = row![].spacing(3);
|
||||
for (kind, label) in kinds {
|
||||
let selected = state.kind == kind;
|
||||
let b = button(
|
||||
column![
|
||||
crate::ui::icons::semantic(
|
||||
icon(kind, state.inverted && kind != GradientKind::Linear),
|
||||
25.0
|
||||
),
|
||||
text(label).size(9)
|
||||
]
|
||||
.align_x(iced::Center),
|
||||
)
|
||||
.on_press(Message::GradientTypeChanged(kind))
|
||||
.width(48)
|
||||
.height(48)
|
||||
.style(move |theme: &Theme, status| {
|
||||
let mut s = button::subtle(theme, status);
|
||||
if selected {
|
||||
s.border.color = theme.palette().primary.base.color;
|
||||
s.border.width = 1.0;
|
||||
}
|
||||
s
|
||||
});
|
||||
type_row = type_row.push(tooltip(
|
||||
b,
|
||||
text(kind.choice_label(false)).size(10),
|
||||
tooltip::Position::Bottom,
|
||||
));
|
||||
}
|
||||
let invert_selected = state.inverted && state.kind != GradientKind::Linear;
|
||||
let invert = button(column![text("⇄").size(18), text("Invert").size(9)].align_x(iced::Center))
|
||||
.on_press_maybe(
|
||||
(state.kind != GradientKind::Linear).then_some(Message::GradientInvertToggled),
|
||||
)
|
||||
.width(42)
|
||||
.height(48)
|
||||
.style(move |theme: &Theme, status| {
|
||||
let mut style = button::secondary(theme, status);
|
||||
if invert_selected {
|
||||
style.border.color = theme.palette().primary.base.color;
|
||||
style.border.width = 1.0;
|
||||
}
|
||||
style
|
||||
});
|
||||
let separator =
|
||||
container(Space::new().width(1).height(36)).style(|theme: &Theme| container::Style {
|
||||
background: Some(Background::Color(theme.palette().background.neutral.color)),
|
||||
..Default::default()
|
||||
});
|
||||
type_row = type_row.push(separator).push(tooltip(
|
||||
invert,
|
||||
text("Invert gradient").size(10),
|
||||
tooltip::Position::Bottom,
|
||||
));
|
||||
let colors: Element<'_, Message> = if state.color_mode == GradientColorMode::Two {
|
||||
row![
|
||||
color_field("Color 1", 1, state.color_1.clone()),
|
||||
color_field("Color 2", 2, state.color_2.clone())
|
||||
]
|
||||
.spacing(6)
|
||||
.into()
|
||||
} else {
|
||||
row![
|
||||
color_field("Color 1", 1, state.color_1.clone()),
|
||||
row![
|
||||
text("Tone").size(11).width(43),
|
||||
slider(0..=100, (state.shade_tint * 100.0) as i32, |v| {
|
||||
Message::GradientShadeTintChanged(v as f32 / 100.0)
|
||||
})
|
||||
.width(Fill)
|
||||
]
|
||||
.spacing(4)
|
||||
.align_y(iced::Center)
|
||||
.height(crate::ui::ROW_H)
|
||||
.width(Fill)
|
||||
]
|
||||
.spacing(6)
|
||||
.align_y(iced::Center)
|
||||
.into()
|
||||
};
|
||||
let angle = row![
|
||||
text("Angle").size(11).width(43),
|
||||
text_input("0.0°", &state.angle)
|
||||
.on_input(Message::GradientAngleChanged)
|
||||
.size(11)
|
||||
.width(Length::Fixed(92.0)),
|
||||
tooltip(
|
||||
button(crate::ui::icons::themed_undo(11.0, true))
|
||||
.on_press(Message::GradientAngleReset)
|
||||
.style(button::subtle)
|
||||
.padding(3)
|
||||
.width(24)
|
||||
.height(crate::ui::ROW_H),
|
||||
text("Reset angle").size(10),
|
||||
tooltip::Position::Bottom,
|
||||
),
|
||||
Space::new().width(Fill),
|
||||
checkbox(state.centered)
|
||||
.label("Centered")
|
||||
.on_toggle(Message::GradientCenteredChanged)
|
||||
.size(14)
|
||||
.text_size(11)
|
||||
]
|
||||
.spacing(6)
|
||||
.align_y(iced::Center);
|
||||
let types = row![text("Type").size(11).width(43), type_row]
|
||||
.spacing(4)
|
||||
.align_y(iced::Center);
|
||||
let actions = container(
|
||||
row![
|
||||
button(text(crate::t!("Cancel")).size(11))
|
||||
.on_press(Message::GradientCancel)
|
||||
.style(button::secondary)
|
||||
.padding([5, 14]),
|
||||
button(text(crate::t!("OK")).size(11))
|
||||
.on_press(Message::GradientApply)
|
||||
.style(button::primary)
|
||||
.padding([5, 18]),
|
||||
]
|
||||
.spacing(8),
|
||||
)
|
||||
.width(Fill)
|
||||
.align_x(iced::alignment::Horizontal::Right);
|
||||
let content = column![
|
||||
image(preview(state)).width(Fill).height(80),
|
||||
row![
|
||||
segment("One Color", GradientColorMode::One),
|
||||
segment("Two Colors", GradientColorMode::Two)
|
||||
]
|
||||
.spacing(2),
|
||||
colors,
|
||||
types,
|
||||
angle,
|
||||
actions
|
||||
]
|
||||
.spacing(8)
|
||||
.padding(10);
|
||||
let panel: Element<'_, Message> = container(content)
|
||||
.width(Length::Fixed(390.0))
|
||||
.style(|theme: &Theme| container::Style {
|
||||
background: Some(Background::Color(theme.palette().background.base.color)),
|
||||
border: Border {
|
||||
color: theme.palette().primary.base.color,
|
||||
width: 1.0,
|
||||
radius: 3.0.into(),
|
||||
},
|
||||
..Default::default()
|
||||
})
|
||||
.into();
|
||||
let panel: Element<'_, Message> = if dimmed {
|
||||
let shield = container(Space::new())
|
||||
.width(Fill)
|
||||
.height(Fill)
|
||||
.style(|theme: &Theme| container::Style {
|
||||
background: Some(Background::Color(
|
||||
theme.palette().background.strongest.color.scale_alpha(0.55),
|
||||
)),
|
||||
..Default::default()
|
||||
});
|
||||
iced::widget::stack![panel, iced::widget::opaque(shield)]
|
||||
.width(Length::Fixed(390.0))
|
||||
.into()
|
||||
} else {
|
||||
panel
|
||||
};
|
||||
mouse_area(panel)
|
||||
.interaction(iced::mouse::Interaction::Pointer)
|
||||
.into()
|
||||
}
|
||||
@@ -0,0 +1,668 @@
|
||||
//! Graphic Attributes palette: apply or remove an associative object fill.
|
||||
|
||||
use crate::app::Message;
|
||||
use crate::ui::dock::{DockMsg, PanelId};
|
||||
use acadrust::{CadDocument, EntityType, Handle};
|
||||
use iced::widget::{
|
||||
button, column, container, image, mouse_area, row, slider, text, tooltip, Space,
|
||||
};
|
||||
use iced::{Background, Border, Element, Fill, Length, Theme};
|
||||
|
||||
pub(crate) struct SolidFillColorInfo {
|
||||
pub color: acadrust::types::Color,
|
||||
pub display: iced::Color,
|
||||
pub varies: bool,
|
||||
}
|
||||
|
||||
struct FillTransparencyInfo {
|
||||
value: u8,
|
||||
varies: bool,
|
||||
}
|
||||
|
||||
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
|
||||
pub enum GraphicAttribute {
|
||||
Varies,
|
||||
None,
|
||||
Solid,
|
||||
Hatch,
|
||||
Gradient,
|
||||
}
|
||||
|
||||
impl GraphicAttribute {
|
||||
pub const ALL: [Self; 4] = [Self::None, Self::Solid, Self::Hatch, Self::Gradient];
|
||||
|
||||
fn icon(self) -> &'static [u8] {
|
||||
match self {
|
||||
Self::Varies | Self::None => {
|
||||
include_bytes!("../../../assets/icons/hatch/hatch_none.svg")
|
||||
}
|
||||
Self::Solid => include_bytes!("../../../assets/icons/hatch/hatch_solid.svg"),
|
||||
Self::Hatch => include_bytes!("../../../assets/icons/hatch/hatch_lines.svg"),
|
||||
Self::Gradient => include_bytes!("../../../assets/icons/hatch/hatch_gradient.svg"),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl std::fmt::Display for GraphicAttribute {
|
||||
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
|
||||
f.write_str(match self {
|
||||
Self::Varies => "*VARIES*",
|
||||
Self::None => "None",
|
||||
Self::Solid => "Solid",
|
||||
Self::Hatch => "Hatch",
|
||||
Self::Gradient => "Gradient",
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
pub(crate) fn current(document: &CadDocument, selected: &[Handle]) -> GraphicAttribute {
|
||||
// Scan the document once, rather than once per selected object on every frame.
|
||||
let mut fills: rustc_hash::FxHashMap<Handle, Option<GraphicAttribute>> =
|
||||
selected.iter().map(|handle| (*handle, None)).collect();
|
||||
for entity in document.entities() {
|
||||
let EntityType::Hatch(hatch) = entity else {
|
||||
continue;
|
||||
};
|
||||
let value = if hatch.gradient_color.enabled {
|
||||
GraphicAttribute::Gradient
|
||||
} else if hatch.is_solid {
|
||||
GraphicAttribute::Solid
|
||||
} else {
|
||||
GraphicAttribute::Hatch
|
||||
};
|
||||
if let Some(fill) = fills.get_mut(&entity.common().handle) {
|
||||
fill.get_or_insert(value);
|
||||
}
|
||||
if !hatch.is_associative {
|
||||
continue;
|
||||
}
|
||||
for handle in hatch.paths.iter().flat_map(|path| &path.boundary_handles) {
|
||||
if let Some(fill) = fills.get_mut(handle) {
|
||||
fill.get_or_insert(value);
|
||||
}
|
||||
}
|
||||
}
|
||||
let Some((&first, rest)) = selected.split_first() else {
|
||||
return GraphicAttribute::None;
|
||||
};
|
||||
let first = fills[&first].unwrap_or(GraphicAttribute::None);
|
||||
if rest
|
||||
.iter()
|
||||
.any(|handle| fills[handle].unwrap_or(GraphicAttribute::None) != first)
|
||||
{
|
||||
GraphicAttribute::Varies
|
||||
} else {
|
||||
first
|
||||
}
|
||||
}
|
||||
|
||||
pub(crate) fn fill_handles(document: &CadDocument, selected: &[Handle]) -> Vec<Handle> {
|
||||
let selected: rustc_hash::FxHashSet<_> = selected.iter().copied().collect();
|
||||
document
|
||||
.entities()
|
||||
.filter_map(|entity| {
|
||||
let EntityType::Hatch(hatch) = entity else {
|
||||
return None;
|
||||
};
|
||||
let directly_selected = selected.contains(&entity.common().handle);
|
||||
let selected_boundary = hatch.is_associative
|
||||
&& hatch.paths.iter().any(|path| {
|
||||
path.boundary_handles
|
||||
.iter()
|
||||
.any(|handle| selected.contains(handle))
|
||||
});
|
||||
(directly_selected || selected_boundary).then_some(entity.common().handle)
|
||||
})
|
||||
.collect()
|
||||
}
|
||||
|
||||
pub(crate) fn solid_fill_handles(document: &CadDocument, selected: &[Handle]) -> Vec<Handle> {
|
||||
fill_handles(document, selected)
|
||||
.into_iter()
|
||||
.filter(|handle| {
|
||||
matches!(
|
||||
document.get_entity(*handle),
|
||||
Some(EntityType::Hatch(hatch))
|
||||
if hatch.is_solid && !hatch.gradient_color.enabled
|
||||
)
|
||||
})
|
||||
.collect()
|
||||
}
|
||||
|
||||
fn fill_transparency_info(
|
||||
document: &CadDocument,
|
||||
selected: &[Handle],
|
||||
draw_depth: &rustc_hash::FxHashMap<u64, [f32; 2]>,
|
||||
) -> Option<FillTransparencyInfo> {
|
||||
let handles = fill_handles(document, selected);
|
||||
let top = handles.iter().copied().max_by(|left, right| {
|
||||
let left = draw_depth.get(&left.value()).map_or(0.0, |depth| depth[0]);
|
||||
let right = draw_depth
|
||||
.get(&right.value())
|
||||
.map_or(0.0, |depth| depth[0]);
|
||||
left.total_cmp(&right)
|
||||
})?;
|
||||
let top_entity = document.get_entity(top)?;
|
||||
let stored = top_entity.common().transparency;
|
||||
let effective = if stored.is_by_layer() {
|
||||
document
|
||||
.layers
|
||||
.get(&top_entity.common().layer)
|
||||
.map_or(stored, |layer| layer.transparency)
|
||||
} else if stored.is_by_block() {
|
||||
acadrust::types::Transparency::from_percent(0.0)
|
||||
} else {
|
||||
stored
|
||||
};
|
||||
Some(FillTransparencyInfo {
|
||||
value: (effective.as_percent() * 100.0).round().clamp(0.0, 90.0) as u8,
|
||||
varies: handles.iter().any(|handle| {
|
||||
document
|
||||
.get_entity(*handle)
|
||||
.is_some_and(|entity| entity.common().transparency != stored)
|
||||
}),
|
||||
})
|
||||
}
|
||||
|
||||
fn solid_fill_color_info(
|
||||
document: &CadDocument,
|
||||
selected: &[Handle],
|
||||
draw_depth: &rustc_hash::FxHashMap<u64, [f32; 2]>,
|
||||
) -> Option<SolidFillColorInfo> {
|
||||
let handles = solid_fill_handles(document, selected);
|
||||
let top = handles.iter().copied().max_by(|left, right| {
|
||||
let left = draw_depth.get(&left.value()).map_or(0.0, |depth| depth[0]);
|
||||
let right = draw_depth
|
||||
.get(&right.value())
|
||||
.map_or(0.0, |depth| depth[0]);
|
||||
left.total_cmp(&right)
|
||||
})?;
|
||||
let top_entity = document.get_entity(top)?;
|
||||
let color = top_entity.common().color;
|
||||
let varies = handles.iter().any(|handle| {
|
||||
document
|
||||
.get_entity(*handle)
|
||||
.is_some_and(|entity| entity.common().color != color)
|
||||
});
|
||||
let rgba = crate::scene::view::render::render_style_for_common_viewport(
|
||||
document,
|
||||
top_entity.common(),
|
||||
None,
|
||||
)
|
||||
.0;
|
||||
Some(SolidFillColorInfo {
|
||||
color,
|
||||
display: iced::Color::from_rgba(rgba[0], rgba[1], rgba[2], 1.0),
|
||||
varies,
|
||||
})
|
||||
}
|
||||
|
||||
fn top_fill_hatch<'a>(
|
||||
document: &'a CadDocument,
|
||||
selected: &[Handle],
|
||||
draw_depth: &rustc_hash::FxHashMap<u64, [f32; 2]>,
|
||||
expected: GraphicAttribute,
|
||||
) -> Option<&'a acadrust::entities::Hatch> {
|
||||
let selected: rustc_hash::FxHashSet<_> = selected.iter().copied().collect();
|
||||
document
|
||||
.entities()
|
||||
.filter_map(|entity| {
|
||||
let EntityType::Hatch(hatch) = entity else {
|
||||
return None;
|
||||
};
|
||||
let value = if hatch.gradient_color.enabled {
|
||||
GraphicAttribute::Gradient
|
||||
} else if hatch.is_solid {
|
||||
GraphicAttribute::Solid
|
||||
} else {
|
||||
GraphicAttribute::Hatch
|
||||
};
|
||||
if value != expected {
|
||||
return None;
|
||||
}
|
||||
let directly_selected = selected.contains(&entity.common().handle);
|
||||
let selected_boundary = hatch.is_associative
|
||||
&& hatch.paths.iter().any(|path| {
|
||||
path.boundary_handles
|
||||
.iter()
|
||||
.any(|handle| selected.contains(handle))
|
||||
});
|
||||
(directly_selected || selected_boundary).then_some((entity.common().handle, hatch))
|
||||
})
|
||||
.max_by(|(left, _), (right, _)| {
|
||||
let left = draw_depth.get(&left.value()).map_or(0.0, |depth| depth[0]);
|
||||
let right = draw_depth
|
||||
.get(&right.value())
|
||||
.map_or(0.0, |depth| depth[0]);
|
||||
left.total_cmp(&right)
|
||||
})
|
||||
.map(|(_, hatch)| hatch)
|
||||
}
|
||||
|
||||
pub fn view<'a>(
|
||||
document: &'a CadDocument,
|
||||
selected: &[Handle],
|
||||
draw_depth: &rustc_hash::FxHashMap<u64, [f32; 2]>,
|
||||
width: f32,
|
||||
auto_collapse: bool,
|
||||
side: crate::app::config::DockSide,
|
||||
menu_open: bool,
|
||||
solid_color_menu_open: bool,
|
||||
transparency_menu_open: bool,
|
||||
hatch_editor_open: bool,
|
||||
gradient_editor: Option<&'a crate::ui::window::gradient_editor::GradientEditorState>,
|
||||
gradient_color_picker_open: bool,
|
||||
) -> Element<'a, Message> {
|
||||
let pin_icon = if auto_collapse {
|
||||
crate::ui::icons::themed_primary_weak_text(crate::ui::icons::PIN, 12.0)
|
||||
} else {
|
||||
crate::ui::icons::themed_secondary(crate::ui::icons::PIN, 12.0)
|
||||
};
|
||||
let pin = button(pin_icon)
|
||||
.on_press(Message::Dock(DockMsg::AutoCollapseToggle(
|
||||
PanelId::GraphicAttributes,
|
||||
)))
|
||||
.style(button::subtle)
|
||||
.padding([3, 5]);
|
||||
let pin = tooltip(
|
||||
pin,
|
||||
text(crate::t!("Auto")).size(10),
|
||||
tooltip::Position::Bottom,
|
||||
)
|
||||
.gap(4);
|
||||
let close = button(crate::ui::icons::themed_secondary(
|
||||
crate::ui::icons::CLOSE,
|
||||
12.0,
|
||||
))
|
||||
.on_press(Message::Dock(DockMsg::Close(PanelId::GraphicAttributes)))
|
||||
.style(button::subtle)
|
||||
.padding([3, 5]);
|
||||
let close = tooltip(
|
||||
close,
|
||||
text(crate::t!("Close")).size(10),
|
||||
tooltip::Position::Bottom,
|
||||
)
|
||||
.gap(4);
|
||||
let header = mouse_area(
|
||||
container(
|
||||
row![
|
||||
text(crate::t!("Graphic Attributes")).size(12),
|
||||
Space::new().width(Fill),
|
||||
pin,
|
||||
close
|
||||
]
|
||||
.spacing(3)
|
||||
.align_y(iced::Center),
|
||||
)
|
||||
.style(|theme: &Theme| container::Style {
|
||||
background: Some(Background::Color(theme.palette().background.weak.color)),
|
||||
..Default::default()
|
||||
})
|
||||
.width(Fill)
|
||||
.padding([3, 6]),
|
||||
)
|
||||
.on_press(Message::Dock(DockMsg::DockGrab(PanelId::GraphicAttributes)))
|
||||
.interaction(iced::mouse::Interaction::Grab);
|
||||
|
||||
let current = current(document, selected);
|
||||
let picker = button(
|
||||
row![
|
||||
crate::ui::icons::semantic(current.icon(), 14.0),
|
||||
text(current.to_string()).size(11),
|
||||
Space::new().width(Fill),
|
||||
crate::ui::icons::themed_arrow_toggle(menu_open, 9.0),
|
||||
]
|
||||
.spacing(5)
|
||||
.align_y(iced::Center),
|
||||
)
|
||||
.on_press(Message::ToggleGraphicAttributeDropdown)
|
||||
.style(move |theme: &Theme, _| {
|
||||
let palette = theme.palette();
|
||||
button::Style {
|
||||
background: Some(Background::Color(palette.background.base.color)),
|
||||
text_color: palette.background.base.text,
|
||||
border: Border {
|
||||
color: if menu_open {
|
||||
palette.primary.base.color
|
||||
} else {
|
||||
palette.background.neutral.color
|
||||
},
|
||||
width: 1.0,
|
||||
radius: 2.0.into(),
|
||||
},
|
||||
..Default::default()
|
||||
}
|
||||
})
|
||||
.height(crate::ui::ROW_H)
|
||||
.width(Fill)
|
||||
.padding([3, 6]);
|
||||
let picker: Element<'_, Message> = if menu_open {
|
||||
let mut rows = column![].spacing(0);
|
||||
for value in GraphicAttribute::ALL {
|
||||
rows = rows.push(
|
||||
button(
|
||||
row![
|
||||
crate::ui::icons::semantic(value.icon(), 14.0),
|
||||
text(value.to_string()).size(11),
|
||||
]
|
||||
.spacing(5)
|
||||
.align_y(iced::Center),
|
||||
)
|
||||
.on_press(Message::GraphicAttributeChanged(value))
|
||||
.style(crate::ui::color_select::list_row_style)
|
||||
.width(Fill)
|
||||
.padding([2, 4]),
|
||||
);
|
||||
}
|
||||
let popup = container(rows)
|
||||
.style(crate::ui::color_select::popup_panel_style)
|
||||
.padding(2);
|
||||
crate::ui::color_select::drop_down_below(
|
||||
picker.into(),
|
||||
popup.into(),
|
||||
None,
|
||||
Length::Shrink,
|
||||
Message::CloseGraphicAttributeDropdown,
|
||||
)
|
||||
} else {
|
||||
picker.into()
|
||||
};
|
||||
let mut body = column![text(crate::t!("Fill")).size(11), picker].spacing(6);
|
||||
if current == GraphicAttribute::Solid {
|
||||
if let Some(info) = solid_fill_color_info(document, selected, draw_depth) {
|
||||
let label = info.varies.then(|| {
|
||||
container(text("* VARIES *").size(10))
|
||||
.width(Fill)
|
||||
.align_x(iced::Center)
|
||||
.align_y(iced::Center)
|
||||
});
|
||||
let color = button(label.unwrap_or_else(|| {
|
||||
container(Space::new()).width(Fill).height(Fill)
|
||||
}))
|
||||
.on_press(Message::OpenColorWindow(
|
||||
crate::app::ColorPickTarget::GraphicAttributesSolid,
|
||||
info.color,
|
||||
))
|
||||
.width(Fill)
|
||||
.height(crate::ui::ROW_H)
|
||||
.padding(0)
|
||||
.style(move |theme: &Theme, status| button::Style {
|
||||
background: Some(Background::Color(info.display)),
|
||||
text_color: if info.display.r * 0.299
|
||||
+ info.display.g * 0.587
|
||||
+ info.display.b * 0.114
|
||||
> 0.55
|
||||
{
|
||||
iced::Color::BLACK
|
||||
} else {
|
||||
iced::Color::WHITE
|
||||
},
|
||||
border: Border {
|
||||
color: if matches!(status, button::Status::Hovered) {
|
||||
theme.palette().primary.base.color
|
||||
} else {
|
||||
theme.palette().background.neutral.color
|
||||
},
|
||||
width: 1.0,
|
||||
radius: 2.0.into(),
|
||||
},
|
||||
..Default::default()
|
||||
});
|
||||
let more = button(text("...").size(crate::ui::ROW_H * 0.42))
|
||||
.on_press(Message::ToggleSolidFillColorDropdown)
|
||||
.style(move |theme: &Theme, status| {
|
||||
if solid_color_menu_open {
|
||||
button::primary(theme, status)
|
||||
} else {
|
||||
button::secondary(theme, status)
|
||||
}
|
||||
})
|
||||
.height(crate::ui::ROW_H)
|
||||
.padding([2, 7]);
|
||||
let more: Element<'_, Message> = if solid_color_menu_open {
|
||||
let choice = |label, color| {
|
||||
button(text(label).size(11))
|
||||
.on_press(Message::SolidFillColorChanged(color))
|
||||
.style(crate::ui::color_select::list_row_style)
|
||||
.width(Fill)
|
||||
.padding([2, 4])
|
||||
};
|
||||
let popup = container(column![
|
||||
choice("ByLayer", acadrust::types::Color::ByLayer),
|
||||
choice("ByBlock", acadrust::types::Color::ByBlock),
|
||||
button(text("Custom...").size(11))
|
||||
.on_press(Message::OpenColorWindow(
|
||||
crate::app::ColorPickTarget::GraphicAttributesSolid,
|
||||
info.color,
|
||||
))
|
||||
.style(crate::ui::color_select::list_row_style)
|
||||
.width(Fill)
|
||||
.padding([2, 4])
|
||||
])
|
||||
.style(crate::ui::color_select::popup_panel_style)
|
||||
.padding(2);
|
||||
let alignment = match side {
|
||||
crate::app::config::DockSide::Left => {
|
||||
iced_aw::drop_down::Alignment::BottomEnd
|
||||
}
|
||||
crate::app::config::DockSide::Right => {
|
||||
iced_aw::drop_down::Alignment::BottomStart
|
||||
}
|
||||
};
|
||||
iced_aw::DropDown::new(more, popup, true)
|
||||
.width(Length::Fixed(110.0))
|
||||
.height(Length::Shrink)
|
||||
.alignment(alignment)
|
||||
.offset(2.0)
|
||||
.on_dismiss(Message::CloseSolidFillColorDropdown)
|
||||
.into()
|
||||
} else {
|
||||
more.into()
|
||||
};
|
||||
body = body.push(row![color, more].spacing(4).align_y(iced::Center));
|
||||
}
|
||||
} else if current == GraphicAttribute::Gradient {
|
||||
if let Some(hatch) = top_fill_hatch(
|
||||
document,
|
||||
selected,
|
||||
draw_depth,
|
||||
GraphicAttribute::Gradient,
|
||||
) {
|
||||
let preview = container(
|
||||
image(crate::ui::window::gradient_editor::compact_preview(hatch))
|
||||
.width(Fill)
|
||||
.height(crate::ui::ROW_H),
|
||||
)
|
||||
.width(Fill)
|
||||
.height(crate::ui::ROW_H)
|
||||
.style(|theme: &Theme| container::Style {
|
||||
border: Border {
|
||||
color: theme.palette().background.neutral.color,
|
||||
width: 1.0,
|
||||
radius: 2.0.into(),
|
||||
},
|
||||
..Default::default()
|
||||
});
|
||||
let edit = button(text("...").size(crate::ui::ROW_H * 0.42))
|
||||
.on_press(if gradient_editor.is_some() {
|
||||
Message::GradientCancel
|
||||
} else {
|
||||
Message::GradientEditorOpen
|
||||
})
|
||||
.style(move |theme: &Theme, status| {
|
||||
if gradient_editor.is_some() {
|
||||
button::primary(theme, status)
|
||||
} else {
|
||||
button::secondary(theme, status)
|
||||
}
|
||||
})
|
||||
.height(crate::ui::ROW_H)
|
||||
.padding([2, 7]);
|
||||
let edit = tooltip(
|
||||
edit,
|
||||
text("Edit gradient").size(10),
|
||||
tooltip::Position::Bottom,
|
||||
);
|
||||
let edit: Element<'_, Message> = if let Some(editor) = gradient_editor {
|
||||
let alignment = match side {
|
||||
crate::app::config::DockSide::Left => {
|
||||
iced_aw::drop_down::Alignment::BottomEnd
|
||||
}
|
||||
crate::app::config::DockSide::Right => {
|
||||
iced_aw::drop_down::Alignment::BottomStart
|
||||
}
|
||||
};
|
||||
iced_aw::DropDown::new(
|
||||
edit,
|
||||
crate::ui::window::gradient_editor::view(
|
||||
editor,
|
||||
gradient_color_picker_open,
|
||||
),
|
||||
true,
|
||||
)
|
||||
.width(Length::Fixed(390.0))
|
||||
.alignment(alignment)
|
||||
.offset(4.0)
|
||||
.into()
|
||||
} else {
|
||||
edit.into()
|
||||
};
|
||||
body = body.push(row![preview, edit].spacing(4).align_y(iced::Center));
|
||||
}
|
||||
} else if current == GraphicAttribute::Hatch {
|
||||
let preview: Element<'_, Message> = top_fill_hatch(
|
||||
document,
|
||||
selected,
|
||||
draw_depth,
|
||||
GraphicAttribute::Hatch,
|
||||
)
|
||||
.and_then(|hatch| {
|
||||
crate::scene::Scene::hatch_model_from_dxf(hatch, [1.0; 4])
|
||||
.map(|model| model.pattern)
|
||||
})
|
||||
.map(crate::ui::properties::compact_hatch_pattern_preview)
|
||||
.unwrap_or_else(|| {
|
||||
container(Space::new())
|
||||
.width(Fill)
|
||||
.height(crate::ui::ROW_H)
|
||||
.style(|theme: &Theme| container::Style {
|
||||
background: Some(Background::Color(theme.palette().background.base.color)),
|
||||
border: Border {
|
||||
color: theme.palette().background.neutral.color,
|
||||
width: 1.0,
|
||||
radius: 2.0.into(),
|
||||
},
|
||||
..Default::default()
|
||||
})
|
||||
.into()
|
||||
});
|
||||
let edit = button(text("...").size(crate::ui::ROW_H * 0.42))
|
||||
.on_press(if hatch_editor_open {
|
||||
Message::HatchEditorClose
|
||||
} else {
|
||||
Message::HatchEditorOpen
|
||||
})
|
||||
.style(move |theme: &Theme, status| {
|
||||
if hatch_editor_open {
|
||||
button::primary(theme, status)
|
||||
} else {
|
||||
button::secondary(theme, status)
|
||||
}
|
||||
})
|
||||
.height(crate::ui::ROW_H)
|
||||
.padding([2, 7]);
|
||||
let edit = tooltip(edit, text("Edit hatch").size(10), tooltip::Position::Bottom);
|
||||
let edit: Element<'_, Message> = if hatch_editor_open {
|
||||
let alignment = match side {
|
||||
crate::app::config::DockSide::Left => iced_aw::drop_down::Alignment::BottomEnd,
|
||||
crate::app::config::DockSide::Right => iced_aw::drop_down::Alignment::BottomStart,
|
||||
};
|
||||
iced_aw::DropDown::new(edit, crate::ui::window::hatch_editor::view(), true)
|
||||
.width(Length::Fixed(390.0))
|
||||
.alignment(alignment)
|
||||
.offset(4.0)
|
||||
.into()
|
||||
} else {
|
||||
edit.into()
|
||||
};
|
||||
body = body.push(row![preview, edit].spacing(4).align_y(iced::Center));
|
||||
}
|
||||
if current != GraphicAttribute::None {
|
||||
if let Some(info) = fill_transparency_info(document, selected, draw_depth) {
|
||||
let value_label = if info.varies {
|
||||
"*VARIES*".to_string()
|
||||
} else {
|
||||
format!("{}%", info.value)
|
||||
};
|
||||
let control = row![
|
||||
text(crate::t!("Transparency")).size(11).width(75),
|
||||
slider(0..=90, i32::from(info.value), |value| {
|
||||
Message::FillTransparencyChanged(value as u8)
|
||||
})
|
||||
.width(Fill),
|
||||
text(value_label).size(10).width(45),
|
||||
]
|
||||
.spacing(5)
|
||||
.align_y(iced::Center)
|
||||
.height(crate::ui::ROW_H);
|
||||
let more = button(text("...").size(crate::ui::ROW_H * 0.42))
|
||||
.on_press(Message::ToggleFillTransparencyDropdown)
|
||||
.style(move |theme: &Theme, status| {
|
||||
if transparency_menu_open {
|
||||
button::primary(theme, status)
|
||||
} else {
|
||||
button::secondary(theme, status)
|
||||
}
|
||||
})
|
||||
.height(crate::ui::ROW_H)
|
||||
.padding([2, 7]);
|
||||
let more: Element<'_, Message> = if transparency_menu_open {
|
||||
let row_button = |label, message| {
|
||||
button(text(label).size(11))
|
||||
.on_press(message)
|
||||
.style(crate::ui::color_select::list_row_style)
|
||||
.width(Fill)
|
||||
.padding([2, 4])
|
||||
};
|
||||
let popup = container(column![
|
||||
row_button("ByLayer", Message::FillTransparencyByLayer),
|
||||
row_button("ByBlock", Message::FillTransparencyByBlock),
|
||||
row_button(
|
||||
"Custom",
|
||||
Message::FillTransparencyChanged(info.value),
|
||||
),
|
||||
])
|
||||
.style(crate::ui::color_select::popup_panel_style)
|
||||
.padding(2);
|
||||
let alignment = match side {
|
||||
crate::app::config::DockSide::Left => {
|
||||
iced_aw::drop_down::Alignment::BottomEnd
|
||||
}
|
||||
crate::app::config::DockSide::Right => {
|
||||
iced_aw::drop_down::Alignment::BottomStart
|
||||
}
|
||||
};
|
||||
iced_aw::DropDown::new(more, popup, true)
|
||||
.width(Length::Fixed(110.0))
|
||||
.height(Length::Shrink)
|
||||
.alignment(alignment)
|
||||
.offset(2.0)
|
||||
.on_dismiss(Message::CloseFillTransparencyDropdown)
|
||||
.into()
|
||||
} else {
|
||||
more.into()
|
||||
};
|
||||
body = body.push(row![control, more].spacing(4).align_y(iced::Center));
|
||||
}
|
||||
}
|
||||
let body = body.padding(8);
|
||||
|
||||
container(column![header, body])
|
||||
.width(Length::Fixed(width))
|
||||
.height(Fill)
|
||||
.style(|theme: &Theme| container::Style {
|
||||
background: Some(Background::Color(theme.palette().background.base.color)),
|
||||
..Default::default()
|
||||
})
|
||||
.into()
|
||||
}
|
||||
@@ -0,0 +1,36 @@
|
||||
use crate::app::Message;
|
||||
use iced::widget::{button, column, container, mouse_area, row, text, Space};
|
||||
use iced::{Background, Border, Element, Fill, Length, Theme};
|
||||
|
||||
pub fn view() -> Element<'static, Message> {
|
||||
let content = column![
|
||||
text("Hatch settings").size(12),
|
||||
text("Additional hatch settings will be available here.").size(11),
|
||||
Space::new().height(12),
|
||||
container(
|
||||
row![button(text(crate::t!("Close")).size(11))
|
||||
.on_press(Message::HatchEditorClose)
|
||||
.style(button::secondary)
|
||||
.padding([5, 14])]
|
||||
.width(Fill),
|
||||
)
|
||||
.width(Fill)
|
||||
.align_x(iced::alignment::Horizontal::Right),
|
||||
]
|
||||
.spacing(8)
|
||||
.padding(10);
|
||||
let panel = container(content)
|
||||
.width(Length::Fixed(390.0))
|
||||
.style(|theme: &Theme| container::Style {
|
||||
background: Some(Background::Color(theme.palette().background.base.color)),
|
||||
border: Border {
|
||||
color: theme.palette().primary.base.color,
|
||||
width: 1.0,
|
||||
radius: 3.0.into(),
|
||||
},
|
||||
..Default::default()
|
||||
});
|
||||
mouse_area(panel)
|
||||
.interaction(iced::mouse::Interaction::Pointer)
|
||||
.into()
|
||||
}
|
||||
@@ -2,6 +2,9 @@ pub mod about;
|
||||
pub mod block_definition;
|
||||
pub mod block_palette;
|
||||
pub mod browser;
|
||||
pub mod graphic_attributes;
|
||||
pub mod gradient_editor;
|
||||
pub mod hatch_editor;
|
||||
pub mod layout_manager;
|
||||
pub mod layer_state_manager;
|
||||
pub mod drawing_units;
|
||||
|
||||