new installer work in progress

master
Jaka Perovšek 2023-12-15 21:12:33 +01:00
parent fa6e4a7316
commit ca878de953
49 changed files with 2145 additions and 401 deletions

View File

@ -0,0 +1 @@
<svg xmlns="http://www.w3.org/2000/svg" height="24" viewBox="0 0 24 24" width="24"><path d="M0 0h24v24H0z" fill="none"/><path d="M22 11V3h-7v3H9V3H2v8h7V8h2v10h4v3h7v-8h-7v3h-2V8h2v3z"/></svg>

After

Width:  |  Height:  |  Size: 192 B

View File

@ -0,0 +1,59 @@
<?xml version="1.0" encoding="UTF-8" standalone="no"?>
<svg
xmlns:dc="http://purl.org/dc/elements/1.1/"
xmlns:cc="http://creativecommons.org/ns#"
xmlns:rdf="http://www.w3.org/1999/02/22-rdf-syntax-ns#"
xmlns:svg="http://www.w3.org/2000/svg"
xmlns="http://www.w3.org/2000/svg"
xmlns:sodipodi="http://sodipodi.sourceforge.net/DTD/sodipodi-0.dtd"
xmlns:inkscape="http://www.inkscape.org/namespaces/inkscape"
height="24"
viewBox="0 0 24 24"
width="24"
version="1.1"
id="svg6"
sodipodi:docname="ic_arrow_left_alt_24px.svg"
inkscape:version="0.92.5 (2060ec1f9f, 2020-04-08)">
<metadata
id="metadata12">
<rdf:RDF>
<cc:Work
rdf:about="">
<dc:format>image/svg+xml</dc:format>
<dc:type
rdf:resource="http://purl.org/dc/dcmitype/StillImage" />
<dc:title></dc:title>
</cc:Work>
</rdf:RDF>
</metadata>
<defs
id="defs10" />
<sodipodi:namedview
pagecolor="#ffffff"
bordercolor="#666666"
borderopacity="1"
objecttolerance="10"
gridtolerance="10"
guidetolerance="10"
inkscape:pageopacity="0"
inkscape:pageshadow="2"
inkscape:window-width="734"
inkscape:window-height="480"
id="namedview8"
showgrid="false"
inkscape:zoom="9.8333333"
inkscape:cx="12"
inkscape:cy="12"
inkscape:window-x="0"
inkscape:window-y="32"
inkscape:window-maximized="0"
inkscape:current-layer="svg6" />
<path
d="M0 0h24v24H0z"
fill="none"
id="path2" />
<path
d="M 7.99,13 H 20 V 11 H 7.99 V 8 L 4,12 7.99,16 Z"
id="path4"
inkscape:connector-curvature="0" />
</svg>

After

Width:  |  Height:  |  Size: 1.6 KiB

View File

@ -0,0 +1 @@
<svg xmlns="http://www.w3.org/2000/svg" height="24" viewBox="0 0 24 24" width="24"><path d="M0 0h24v24H0z" fill="none"/><path d="M16.01 11H4v2h12.01v3L20 12l-3.99-4z"/></svg>

After

Width:  |  Height:  |  Size: 174 B

View File

@ -0,0 +1 @@
<svg xmlns="http://www.w3.org/2000/svg" height="24" viewBox="0 0 24 24" width="24"><path d="M0 0h24v24H0z" fill="none"/><path d="M12 6v3l4-4-4-4v3c-4.42 0-8 3.58-8 8 0 1.57.46 3.03 1.24 4.26L6.7 14.8c-.45-.83-.7-1.79-.7-2.8 0-3.31 2.69-6 6-6zm6.76 1.74L17.3 9.2c.44.84.7 1.79.7 2.8 0 3.31-2.69 6-6 6v-3l-4 4 4 4v-3c4.42 0 8-3.58 8-8 0-1.57-.46-3.03-1.24-4.26z"/></svg>

After

Width:  |  Height:  |  Size: 368 B

View File

@ -0,0 +1 @@
<svg xmlns="http://www.w3.org/2000/svg" height="24" viewBox="0 0 24 24" width="24"><path clip-rule="evenodd" d="M0 0h24v24H0z" fill="none"/><path d="M22.7 19l-9.1-9.1c.9-2.3.4-5-1.5-6.9-2-2-5-2.4-7.4-1.3L9 6 6 9 1.6 4.7C.4 7.1.9 10.1 2.9 12.1c1.9 1.9 4.6 2.4 6.9 1.5l9.1 9.1c.4.4 1 .4 1.4 0l2.3-2.3c.5-.4.5-1.1.1-1.4z"/></svg>

After

Width:  |  Height:  |  Size: 326 B

View File

@ -0,0 +1 @@
<svg xmlns="http://www.w3.org/2000/svg" width="48" height="48" viewBox="0 0 48 48"><path d="M38 6H10c-2.21 0-4 1.79-4 4v28c0 2.21 1.79 4 4 4h28c2.21 0 4-1.79 4-4V10c0-2.21-1.79-4-4-4zM20 34L10 24l2.83-2.83L20 28.34l15.17-15.17L38 16 20 34z"/></svg>

After

Width:  |  Height:  |  Size: 248 B

View File

@ -0,0 +1 @@
<svg xmlns="http://www.w3.org/2000/svg" width="48" height="48" viewBox="0 0 48 48"><path d="M38 10v28H10V10h28m0-4H10c-2.21 0-4 1.79-4 4v28c0 2.21 1.79 4 4 4h28c2.21 0 4-1.79 4-4V10c0-2.21-1.79-4-4-4z"/></svg>

After

Width:  |  Height:  |  Size: 209 B

View File

@ -0,0 +1 @@
<svg xmlns="http://www.w3.org/2000/svg" height="24" viewBox="0 0 24 24" width="24"><path d="M0 0h24v24H0z" fill="none"/><path d="M19 6.41L17.59 5 12 10.59 6.41 5 5 6.41 10.59 12 5 17.59 6.41 19 12 13.41 17.59 19 19 17.59 13.41 12z"/></svg>

After

Width:  |  Height:  |  Size: 239 B

View File

@ -0,0 +1 @@
<svg xmlns="http://www.w3.org/2000/svg" width="48" height="48" viewBox="0 0 48 48"><path d="M38 12.83L35.17 10 24 21.17 12.83 10 10 12.83 21.17 24 10 35.17 12.83 38 24 26.83 35.17 38 38 35.17 26.83 24z"/></svg>

After

Width:  |  Height:  |  Size: 210 B

View File

@ -0,0 +1 @@
<svg xmlns="http://www.w3.org/2000/svg" height="24" viewBox="0 0 24 24" width="24"><path d="M0 0h24v24H0z" fill="none"/><path d="M19.35 10.04C18.67 6.59 15.64 4 12 4 9.11 4 6.6 5.64 5.35 8.04 2.34 8.36 0 10.91 0 14c0 3.31 2.69 6 6 6h13c2.76 0 5-2.24 5-5 0-2.64-2.05-4.78-4.65-4.96zM14 13v4h-4v-4H7l5-5 5 5h-3z"/></svg>

After

Width:  |  Height:  |  Size: 318 B

View File

@ -0,0 +1 @@
<svg xmlns="http://www.w3.org/2000/svg" width="48" height="48" viewBox="0 0 48 48"><path d="M6 34.5V42h7.5l22.13-22.13-7.5-7.5L6 34.5zm35.41-20.41c.78-.78.78-2.05 0-2.83l-4.67-4.67c-.78-.78-2.05-.78-2.83 0l-3.66 3.66 7.5 7.5 3.66-3.66z"/></svg>

After

Width:  |  Height:  |  Size: 244 B

View File

@ -0,0 +1 @@
<svg xmlns="http://www.w3.org/2000/svg" width="48" height="48" viewBox="0 0 48 48"><path d="M12 38c0 2.21 1.79 4 4 4h16c2.21 0 4-1.79 4-4V14H12v24zM38 8h-7l-2-2H19l-2 2h-7v4h28V8z"/></svg>

After

Width:  |  Height:  |  Size: 188 B

View File

@ -0,0 +1 @@
<svg xmlns="http://www.w3.org/2000/svg" height="24" viewBox="0 0 24 24" width="24"><path d="M0 0h24v24H0z" fill="none"/><path d="M14 2H6c-1.1 0-1.99.9-1.99 2L4 20c0 1.1.89 2 1.99 2H18c1.1 0 2-.9 2-2V8l-6-6zm2 16H8v-2h8v2zm0-4H8v-2h8v2zm-3-5V3.5L18.5 9H13z"/></svg>

After

Width:  |  Height:  |  Size: 264 B

View File

@ -0,0 +1 @@
<svg xmlns="http://www.w3.org/2000/svg" width="48" height="48" viewBox="0 0 48 48"><path d="M18 32.34L9.66 24l-2.83 2.83L18 38l24-24-2.83-2.83z"/></svg>

After

Width:  |  Height:  |  Size: 152 B

View File

@ -0,0 +1 @@
<svg xmlns="http://www.w3.org/2000/svg" width="48" height="48" viewBox="0 0 48 48"><path d="M6 34.5V42h7.5l22.13-22.13-7.5-7.5L6 34.5zm35.41-20.41c.78-.78.78-2.05 0-2.83l-4.67-4.67c-.78-.78-2.05-.78-2.83 0l-3.66 3.66 7.5 7.5 3.66-3.66z"/></svg>

After

Width:  |  Height:  |  Size: 244 B

View File

@ -0,0 +1 @@
<svg xmlns="http://www.w3.org/2000/svg" width="48" height="48" viewBox="0 0 48 48"><path d="M24 4C12.96 4 4 12.95 4 24s8.96 20 20 20 20-8.95 20-20S35.04 4 24 4zm2 30h-4v-4h4v4zm0-8h-4V14h4v12z"/></svg>

After

Width:  |  Height:  |  Size: 201 B

View File

@ -0,0 +1 @@
<svg xmlns="http://www.w3.org/2000/svg" height="24" viewBox="0 0 24 24" width="24"><path d="M0 0h24v24H0z" fill="none"/><path d="M20.5 11H19V7c0-1.1-.9-2-2-2h-4V3.5C13 2.12 11.88 1 10.5 1S8 2.12 8 3.5V5H4c-1.1 0-1.99.9-1.99 2v3.8H3.5c1.49 0 2.7 1.21 2.7 2.7s-1.21 2.7-2.7 2.7H2V20c0 1.1.9 2 2 2h3.8v-1.5c0-1.49 1.21-2.7 2.7-2.7 1.49 0 2.7 1.21 2.7 2.7V22H17c1.1 0 2-.9 2-2v-4h1.5c1.38 0 2.5-1.12 2.5-2.5S21.88 11 20.5 11z"/></svg>

After

Width:  |  Height:  |  Size: 430 B

View File

@ -0,0 +1 @@
<svg xmlns="http://www.w3.org/2000/svg" height="24" viewBox="0 0 24 24" width="24"><path d="M0 0h24v24H0z" fill="none"/><path d="M16 1H4c-1.1 0-2 .9-2 2v14h2V3h12V1zm-1 4l6 6v10c0 1.1-.9 2-2 2H7.99C6.89 23 6 22.1 6 21l.01-14c0-1.1.89-2 1.99-2h7zm-1 7h5.5L14 6.5V12z"/></svg>

After

Width:  |  Height:  |  Size: 274 B

View File

@ -0,0 +1 @@
<svg xmlns="http://www.w3.org/2000/svg" height="24" viewBox="0 0 24 24" width="24"><path d="M0 0h24v24H0z" fill="none"/><path d="M10 4H4c-1.1 0-1.99.9-1.99 2L2 18c0 1.1.9 2 2 2h16c1.1 0 2-.9 2-2V8c0-1.1-.9-2-2-2h-8l-2-2z"/></svg>

After

Width:  |  Height:  |  Size: 229 B

View File

@ -0,0 +1 @@
<svg xmlns="http://www.w3.org/2000/svg" width="48" height="48" viewBox="0 0 48 48"><path d="M40 12H24l-4-4H8c-2.21 0-3.98 1.79-3.98 4L4 36c0 2.21 1.79 4 4 4h32c2.21 0 4-1.79 4-4V16c0-2.21-1.79-4-4-4zm0 24H8V16h32v20z"/></svg>

After

Width:  |  Height:  |  Size: 225 B

View File

@ -0,0 +1 @@
<svg xmlns="http://www.w3.org/2000/svg" height="24" viewBox="0 0 24 24" width="24"><path d="M0 0h24v24H0z" fill="none"/><path d="M21 19V5c0-1.1-.9-2-2-2H5c-1.1 0-2 .9-2 2v14c0 1.1.9 2 2 2h14c1.1 0 2-.9 2-2zM8.5 13.5l2.5 3.01L14.5 12l4.5 6H5l3.5-4.5z"/></svg>

After

Width:  |  Height:  |  Size: 258 B

View File

@ -0,0 +1 @@
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24" width="24" height="24"><path d="M19 3H5c-1.1 0-2 .9-2 2v14c0 1.1.9 2 2 2h14c1.1 0 2-.9 2-2V5c0-1.1-.9-2-2-2zm-2 10H7v-2h10v2z"/></svg>

After

Width:  |  Height:  |  Size: 194 B

View File

@ -0,0 +1 @@
<svg xmlns="http://www.w3.org/2000/svg" enable-background="new 0 0 24 24" height="24" viewBox="0 0 24 24" width="24"><g><rect fill="none" height="24" width="24"/></g><g><g><polygon points="17.59,18 19,16.59 14.42,12 19,7.41 17.59,6 11.59,12"/><polygon points="11,18 12.41,16.59 7.83,12 12.41,7.41 11,6 5,12"/></g></g></svg>

After

Width:  |  Height:  |  Size: 323 B

View File

@ -0,0 +1 @@
<svg xmlns="http://www.w3.org/2000/svg" enable-background="new 0 0 24 24" height="24" viewBox="0 0 24 24" width="24"><g><rect fill="none" height="24" width="24"/></g><g><g><polygon points="6.41,6 5,7.41 9.58,12 5,16.59 6.41,18 12.41,12"/><polygon points="13,6 11.59,7.41 16.17,12 11.59,16.59 13,18 19,12"/></g></g></svg>

After

Width:  |  Height:  |  Size: 320 B

View File

@ -0,0 +1 @@
<svg xmlns="http://www.w3.org/2000/svg" height="24" viewBox="0 0 24 24" width="24"><path d="M0 0h24v24H0V0z" fill="none"/><path d="M3 3h18v2H3z"/></svg>

After

Width:  |  Height:  |  Size: 152 B

View File

@ -0,0 +1 @@
<svg xmlns="http://www.w3.org/2000/svg" height="24" viewBox="0 0 24 24" width="24"><path d="M0 0h24v24H0V0z" fill="none"/><path d="M6 19h12v2H6z"/></svg>

After

Width:  |  Height:  |  Size: 153 B

View File

@ -0,0 +1 @@
<svg xmlns="http://www.w3.org/2000/svg" width="48" height="48" viewBox="0 0 48 48"><path d="M30.83 14.83L28 12 16 24l12 12 2.83-2.83L21.66 24z"/></svg>

After

Width:  |  Height:  |  Size: 151 B

View File

@ -0,0 +1 @@
<svg xmlns="http://www.w3.org/2000/svg" width="48" height="48" viewBox="0 0 48 48"><path d="M20 12l-2.83 2.83L26.34 24l-9.17 9.17L20 36l12-12z"/></svg>

After

Width:  |  Height:  |  Size: 151 B

View File

@ -0,0 +1 @@
<svg xmlns="http://www.w3.org/2000/svg" height="24" viewBox="0 0 24 24" width="24"><path d="M0 0h24v24H0z" fill="none"/><path d="M19 4H5c-1.11 0-2 .9-2 2v12c0 1.1.89 2 2 2h4v-2H5V8h14v10h-4v2h4c1.1 0 2-.9 2-2V6c0-1.1-.89-2-2-2zm-7 6l-4 4h3v6h2v-6h3l-4-4z"/></svg>

After

Width:  |  Height:  |  Size: 263 B

View File

@ -0,0 +1 @@
<svg xmlns="http://www.w3.org/2000/svg" height="24" viewBox="0 0 24 24" width="24"><path d="M0 0h24v24H0z" fill="none"/><path d="M19 19H5V5h7V3H5c-1.11 0-2 .9-2 2v14c0 1.1.89 2 2 2h14c1.1 0 2-.9 2-2v-7h-2v7zM14 3v2h3.59l-9.83 9.83 1.41 1.41L19 6.41V10h2V3h-7z"/></svg>

After

Width:  |  Height:  |  Size: 268 B

View File

@ -0,0 +1 @@
<svg xmlns="http://www.w3.org/2000/svg" enable-background="new 0 0 24 24" height="24" viewBox="0 0 24 24" width="24"><rect fill="none" height="24" width="24" x="0" y="0"/><path d="M12,2C6.48,2,2,6.48,2,12c0,5.52,4.48,10,10,10s10-4.48,10-10C22,6.48,17.52,2,12,2z M13.88,11.54l-4.96,4.96l-1.41-1.41 l4.96-4.96L10.34,8l5.65,0.01L16,13.66L13.88,11.54z"/></svg>

After

Width:  |  Height:  |  Size: 356 B

View File

@ -0,0 +1 @@
<svg xmlns="http://www.w3.org/2000/svg" width="48" height="48" viewBox="0 0 48 48"><path d="M28 20H4v4h24v-4zm0-8H4v4h24v-4zm8 16v-8h-4v8h-8v4h8v8h4v-8h8v-4h-8zM4 32h16v-4H4v4z"/></svg>

After

Width:  |  Height:  |  Size: 185 B

View File

@ -0,0 +1 @@
<svg xmlns="http://www.w3.org/2000/svg" enable-background="new 0 0 24 24" height="24" viewBox="0 0 24 24" width="24"><g><rect fill="none" height="24" width="24"/></g><g><g/><g><path d="M17,19.22H5V7h7V5H5C3.9,5,3,5.9,3,7v12c0,1.1,0.9,2,2,2h12c1.1,0,2-0.9,2-2v-7h-2V19.22z"/><path d="M19,2h-2v3h-3c0.01,0.01,0,2,0,2h3v2.99c0.01,0.01,2,0,2,0V7h3V5h-3V2z"/><rect height="2" width="8" x="7" y="9"/><polygon points="7,12 7,14 15,14 15,12 12,12"/><rect height="2" width="8" x="7" y="15"/></g></g></svg>

After

Width:  |  Height:  |  Size: 496 B

View File

@ -0,0 +1 @@
<svg xmlns="http://www.w3.org/2000/svg" enable-background="new 0 0 24 24" height="24" viewBox="0 0 24 24" width="24"><g><rect fill="none" height="24" width="24"/></g><g><path d="M16,9V4l1,0c0.55,0,1-0.45,1-1v0c0-0.55-0.45-1-1-1H7C6.45,2,6,2.45,6,3v0 c0,0.55,0.45,1,1,1l1,0v5c0,1.66-1.34,3-3,3h0v2h5.97v7l1,1l1-1v-7H19v-2h0C17.34,12,16,10.66,16,9z" fill-rule="evenodd"/></g></svg>

After

Width:  |  Height:  |  Size: 379 B

View File

@ -0,0 +1 @@
<svg xmlns="http://www.w3.org/2000/svg" width="48" height="48" viewBox="0 0 48 48"><path d="M36.79 21.2C33.11 17.97 28.29 16 23 16c-9.3 0-17.17 6.06-19.92 14.44L7.81 32c2.1-6.39 8.1-11 15.19-11 3.91 0 7.46 1.44 10.23 3.77L26 32h18V14l-7.21 7.2z"/></svg>

After

Width:  |  Height:  |  Size: 253 B

View File

@ -0,0 +1 @@
<svg xmlns="http://www.w3.org/2000/svg" height="24" viewBox="0 0 24 24" width="24"><path d="M0 0h24v24H0z" fill="none"/><path d="M17.65 6.35C16.2 4.9 14.21 4 12 4c-4.42 0-7.99 3.58-7.99 8s3.57 8 7.99 8c3.73 0 6.84-2.55 7.73-6h-2.08c-.82 2.33-3.04 4-5.65 4-3.31 0-6-2.69-6-6s2.69-6 6-6c1.66 0 3.14.69 4.22 1.78L13 11h7V4l-2.35 2.35z"/></svg>

After

Width:  |  Height:  |  Size: 340 B

View File

@ -0,0 +1 @@
<svg xmlns="http://www.w3.org/2000/svg" height="24" viewBox="0 0 24 24" width="24"><path d="M0 0h24v24H0z" fill="none"/><path d="M17 3H5c-1.11 0-2 .9-2 2v14c0 1.1.89 2 2 2h14c1.1 0 2-.9 2-2V7l-4-4zm-5 16c-1.66 0-3-1.34-3-3s1.34-3 3-3 3 1.34 3 3-1.34 3-3 3zm3-10H5V5h10v4z"/></svg>

After

Width:  |  Height:  |  Size: 280 B

View File

@ -0,0 +1 @@
<svg xmlns="http://www.w3.org/2000/svg" width="48" height="48" viewBox="0 0 48 48"><path d="M6 10h4V6c-2.21 0-4 1.79-4 4zm0 16h4v-4H6v4zm8 16h4v-4h-4v4zM6 18h4v-4H6v4zM26 6h-4v4h4V6zm12 0v4h4c0-2.21-1.79-4-4-4zM10 42v-4H6c0 2.21 1.79 4 4 4zm-4-8h4v-4H6v4zM18 6h-4v4h4V6zm4 36h4v-4h-4v4zm16-16h4v-4h-4v4zm0 16c2.21 0 4-1.79 4-4h-4v4zm0-24h4v-4h-4v4zm0 16h4v-4h-4v4zm-8 8h4v-4h-4v4zm0-32h4V6h-4v4zM14 34h20V14H14v20zm4-16h12v12H18V18z"/></svg>

After

Width:  |  Height:  |  Size: 441 B

View File

@ -0,0 +1 @@
<svg xmlns="http://www.w3.org/2000/svg" enable-background="new 0 0 24 24" height="24" viewBox="0 0 24 24" width="24"><g><rect fill="none" height="24" width="24"/><path d="M20.41,8.41l-4.83-4.83C15.21,3.21,14.7,3,14.17,3H5C3.9,3,3,3.9,3,5v14c0,1.1,0.9,2,2,2h14c1.1,0,2-0.9,2-2V9.83 C21,9.3,20.79,8.79,20.41,8.41z M7,7h7v2H7V7z M17,17H7v-2h10V17z M17,13H7v-2h10V13z"/></g></svg>

After

Width:  |  Height:  |  Size: 376 B

View File

@ -0,0 +1 @@
<svg xmlns="http://www.w3.org/2000/svg" width="48" height="48" viewBox="0 0 48 48"><path d="M25 16c-5.29 0-10.11 1.97-13.8 5.2L4 14v18h18l-7.23-7.23C17.54 22.44 21.09 21 25 21c7.09 0 13.09 4.61 15.19 11l4.73-1.56C42.17 22.06 34.3 16 25 16z"/></svg>

After

Width:  |  Height:  |  Size: 248 B

View File

@ -0,0 +1 @@
<svg xmlns="http://www.w3.org/2000/svg" width="48" height="48" viewBox="0 0 48 48"><path d="M24 9C14 9 5.46 15.22 2 24c3.46 8.78 12 15 22 15 10.01 0 18.54-6.22 22-15-3.46-8.78-11.99-15-22-15zm0 25c-5.52 0-10-4.48-10-10s4.48-10 10-10 10 4.48 10 10-4.48 10-10 10zm0-16c-3.31 0-6 2.69-6 6s2.69 6 6 6 6-2.69 6-6-2.69-6-6-6z"/></svg>

After

Width:  |  Height:  |  Size: 328 B

View File

@ -0,0 +1 @@
<svg xmlns="http://www.w3.org/2000/svg" height="24" viewBox="0 0 24 24" width="24"><path d="M0 0h24v24H0z" fill="none"/><path d="M13 13v8h8v-8h-8zM3 21h8v-8H3v8zM3 3v8h8V3H3zm13.66-1.31L11 7.34 16.66 13l5.66-5.66-5.66-5.65z"/></svg>

After

Width:  |  Height:  |  Size: 232 B

View File

@ -0,0 +1,58 @@
<?xml version="1.0" encoding="UTF-8" standalone="no"?>
<svg
xmlns:dc="http://purl.org/dc/elements/1.1/"
xmlns:cc="http://creativecommons.org/ns#"
xmlns:rdf="http://www.w3.org/1999/02/22-rdf-syntax-ns#"
xmlns:svg="http://www.w3.org/2000/svg"
xmlns="http://www.w3.org/2000/svg"
xmlns:sodipodi="http://sodipodi.sourceforge.net/DTD/sodipodi-0.dtd"
xmlns:inkscape="http://www.inkscape.org/namespaces/inkscape"
height="24"
viewBox="0 0 24 24"
width="24"
version="1.1"
id="svg6"
sodipodi:docname="ic_widgets_grey_24px.svg"
inkscape:version="0.92.5 (2060ec1f9f, 2020-04-08)">
<metadata
id="metadata12">
<rdf:RDF>
<cc:Work
rdf:about="">
<dc:format>image/svg+xml</dc:format>
<dc:type
rdf:resource="http://purl.org/dc/dcmitype/StillImage" />
</cc:Work>
</rdf:RDF>
</metadata>
<defs
id="defs10" />
<sodipodi:namedview
pagecolor="#ffffff"
bordercolor="#666666"
borderopacity="1"
objecttolerance="10"
gridtolerance="10"
guidetolerance="10"
inkscape:pageopacity="0"
inkscape:pageshadow="2"
inkscape:window-width="2560"
inkscape:window-height="1379"
id="namedview8"
showgrid="false"
inkscape:zoom="9.8333333"
inkscape:cx="12"
inkscape:cy="12"
inkscape:window-x="0"
inkscape:window-y="32"
inkscape:window-maximized="1"
inkscape:current-layer="svg6" />
<path
d="M0 0h24v24H0z"
fill="none"
id="path2" />
<path
d="M13 13v8h8v-8h-8zM3 21h8v-8H3v8zM3 3v8h8V3H3zm13.66-1.31L11 7.34 16.66 13l5.66-5.66-5.66-5.65z"
id="path4"
style="fill:#808080" />
</svg>

After

Width:  |  Height:  |  Size: 1.6 KiB

View File

@ -0,0 +1,58 @@
<?xml version="1.0" encoding="UTF-8" standalone="no"?>
<svg
xmlns:dc="http://purl.org/dc/elements/1.1/"
xmlns:cc="http://creativecommons.org/ns#"
xmlns:rdf="http://www.w3.org/1999/02/22-rdf-syntax-ns#"
xmlns:svg="http://www.w3.org/2000/svg"
xmlns="http://www.w3.org/2000/svg"
xmlns:sodipodi="http://sodipodi.sourceforge.net/DTD/sodipodi-0.dtd"
xmlns:inkscape="http://www.inkscape.org/namespaces/inkscape"
height="24"
viewBox="0 0 24 24"
width="24"
version="1.1"
id="svg6"
sodipodi:docname="ic_widgets_red_24px.svg"
inkscape:version="0.92.5 (2060ec1f9f, 2020-04-08)">
<metadata
id="metadata12">
<rdf:RDF>
<cc:Work
rdf:about="">
<dc:format>image/svg+xml</dc:format>
<dc:type
rdf:resource="http://purl.org/dc/dcmitype/StillImage" />
</cc:Work>
</rdf:RDF>
</metadata>
<defs
id="defs10" />
<sodipodi:namedview
pagecolor="#ffffff"
bordercolor="#666666"
borderopacity="1"
objecttolerance="10"
gridtolerance="10"
guidetolerance="10"
inkscape:pageopacity="0"
inkscape:pageshadow="2"
inkscape:window-width="2560"
inkscape:window-height="1411"
id="namedview8"
showgrid="false"
inkscape:zoom="9.8333333"
inkscape:cx="-4.0677967"
inkscape:cy="12"
inkscape:window-x="2560"
inkscape:window-y="0"
inkscape:window-maximized="1"
inkscape:current-layer="svg6" />
<path
d="M0 0h24v24H0z"
fill="none"
id="path2" />
<path
d="M13 13v8h8v-8h-8zM3 21h8v-8H3v8zM3 3v8h8V3H3zm13.66-1.31L11 7.34 16.66 13l5.66-5.66-5.66-5.65z"
id="path4"
style="fill:#ff0000" />
</svg>

After

Width:  |  Height:  |  Size: 1.6 KiB

711
gui/installer.py 100644
View File

@ -0,0 +1,711 @@
import sys
import os
from PySide6.QtCore import QSize, Qt, Slot, QRect, QFileSystemWatcher
from PySide6.QtGui import QIcon, QAction, QShortcut, QKeySequence, QPainter, QColor, QTextFormat, QPixmap
from PySide6.QtWidgets import QApplication, QMainWindow, QPushButton, QWidget, QLabel, QVBoxLayout, QHBoxLayout, \
QGroupBox, QLineEdit, QCheckBox, QSpinBox, QPlainTextEdit, QSizePolicy, QGridLayout, QTextEdit, QScrollArea, QFrame, \
QDoubleSpinBox
from widgets import *
from pathlib import Path
import yaml
def change_font_size(label: QLabel | QLineEdit, factor: float) -> QLabel:
f = label.font()
f.setPointSizeF(f.pointSizeF() * factor)
label.setFont(f)
return label
class OptionsWidget(QWidget):
def __init__(self):
super().__init__()
options = [("Buffs", "Some songs affect the characters with strengthen\nand haste afflictions."),
("De-buffs", "Some songs affect the characters with psychosis \naffliction."),
("Walkmans", "Include personal music players (walkmans)"),
("Durability", "Playing and throwing cassettes damages them."),
("Repair", "Crafting mechanics to repair broken tapes by \nduplicating them with error correction.")]
# Name options
self.name_line_edit = QLineEdit()
self.name_hbox = QHBoxLayout()
self.name_hbox.addWidget(self.name_line_edit)
self.name_gbox = QGroupBox("Local mod name")
self.name_gbox.setCheckable(True)
self.name_gbox.setLayout(self.name_hbox)
# resolution
self.width_spinbox = QSpinBox()
self.width_spinbox.setRange(100, 100000)
self.width_spinbox.setPrefix("Width: ")
self.width_spinbox.setSuffix(" px")
self.width_spinbox.setValue(1920)
self.height_spinbox = QSpinBox()
self.height_spinbox.setRange(100, 100000)
self.height_spinbox.setPrefix("Height: ")
self.height_spinbox.setSuffix(" px")
self.height_spinbox.setValue(1080)
self.detect_pushbutton = QPushButton("Detect screen resolution")
self.resolution_hbox = QVBoxLayout()
self.resolution_hbox.addWidget(self.width_spinbox)
self.resolution_hbox.addWidget(self.height_spinbox)
self.resolution_hbox.addWidget(self.detect_pushbutton)
self.tape_covers_gbox = QGroupBox("Tape covers")
self.tape_covers_gbox.setCheckable(True)
self.tape_covers_gbox.setLayout(self.resolution_hbox)
# Checkbox options
self.cbox_buffs = TelegraphingCheckbox(options[0][0], options[0][1])
self.cbox_debuffs = TelegraphingCheckbox(options[1][0], options[1][1])
self.cbox_walkmans = TelegraphingCheckbox(options[2][0], options[2][1])
self.cbox_durability = TelegraphingCheckbox(options[3][0], options[3][1])
self.cbox_repair = TelegraphingCheckbox(options[4][0], options[4][1])
self.label_options_description = QLabel()
self.label_options_description.setSizePolicy(QSizePolicy.Policy.Expanding, QSizePolicy.Policy.Preferred)
self.label_options_description.setAlignment(Qt.AlignmentFlag.AlignLeft)
self.label_options_description.setAlignment(Qt.AlignmentFlag.AlignTop)
self.options_vbox = QVBoxLayout()
for cbox in [self.cbox_buffs,
self.cbox_debuffs,
self.cbox_walkmans,
self.cbox_durability,
self.cbox_repair]:
self.options_vbox.addWidget(cbox)
cbox.hover.connect(self.label_options_description.setText)
cbox.toggle()
self.options_hbox = QHBoxLayout()
self.options_hbox.addLayout(self.options_vbox)
self.options_hbox.addWidget(self.label_options_description)
self.options_gbox = QGroupBox("Other options")
self.options_gbox.setLayout(self.options_hbox)
# Install widget
self.install = InstallWidget()
layout = QVBoxLayout()
layout.addWidget(self.name_gbox)
layout.addWidget(self.tape_covers_gbox)
layout.addWidget(self.options_gbox)
layout.addWidget(self.install)
layout.addStretch()
self.setLayout(layout)
self.setSizePolicy(QSizePolicy.Policy.Maximum, QSizePolicy.Policy.Minimum)
# def update_label_description(self):
# TODO: implement random local mod name
class EditorWidget(QWidget):
def __init__(self):
super().__init__()
self.tapes_yaml = Path("../source/tapes.yaml")
self.filename_string = f"<tt style=\"font-family: consolas;\"><b>{self.tapes_yaml.name}</b></tt>"
self.tapes_file_watcher = QFileSystemWatcher()
self.tapes_file_watcher.addPath(self.tapes_yaml.as_posix())
self.tapes_file_watcher.fileChanged.connect(self.external_file_changed)
self.external_editor_pushbutton = QPushButton()
self.external_editor_pushbutton.setIcon(QIcon("./icons/ic_open_in_new_24px.svg"))
self.external_editor_pushbutton.setToolTip("Edit in external editor")
self.external_editor_pushbutton.clicked.connect(self.open_file)
self.reload_code_pushbutton = QPushButton()
self.reload_code_pushbutton.setIcon(QIcon("./icons/ic_refresh_24px.svg"))
self.reload_code_pushbutton.setToolTip("Reload the external file")
self.reload_code_pushbutton.clicked.connect(self.load_file)
self.save_code_pushbutton = QPushButton()
self.save_code_pushbutton.setIcon(QIcon("./icons/ic_save_24px.svg"))
self.save_code_pushbutton.setToolTip("Save setup")
self.transfer_to_validator_pushbutton = QPushButton()
self.transfer_to_validator_pushbutton.setIcon(QIcon("./icons/ic_keyboard_double_arrow_right_24px.svg"))
self.transfer_to_validator_pushbutton.setToolTip("Apply the code.")
self.status_same = f"<b>Status:</b> yaml setup matches {self.filename_string} file"
self.status_external_file_changed = f"<b>Status:</b> {self.filename_string} file was edited externally"
self.status_editor_code_changed = "<b>Status:</b> yaml setup not yet saved"
self.status_label = QLabel(self.status_same)
self.editor = CodeEditor()
self.editor.setFont("Consolas")
self.editor.setLineWrapMode(QPlainTextEdit.LineWrapMode.NoWrap)
self.editor.setMinimumWidth(400)
self.editor.textChanged.connect(self.yaml_setup_changed)
self.button_hbox = QHBoxLayout()
self.button_hbox.addWidget(self.external_editor_pushbutton)
self.button_hbox.addWidget(self.reload_code_pushbutton)
self.button_hbox.addWidget(self.save_code_pushbutton)
self.button_hbox.addWidget(self.status_label)
self.button_hbox.addStretch()
self.button_hbox.addWidget(self.transfer_to_validator_pushbutton)
#self.button_hbox.addStretch()
self.editor_vbox = QVBoxLayout()
self.editor_vbox.addLayout(self.button_hbox)
self.editor_vbox.addWidget(self.editor)
self.editor_gbox = QGroupBox("tapes.yaml editor")
self.editor_gbox.setLayout(self.editor_vbox)
layout = QVBoxLayout(self)
layout.addWidget(self.editor_gbox)
self.load_file()
def load_file(self):
with open(self.tapes_yaml, encoding="utf-8") as fp:
text = fp.read()
self.editor.setPlainText(text)
def open_file(self):
os.startfile(self.tapes_yaml)
def check_if_setups_match(self):
with open(self.tapes_yaml, encoding="utf-8") as fp:
external_text = fp.read()
contents_match = external_text == self.editor.toPlainText()
self.save_code_pushbutton.setDisabled(contents_match)
self.reload_code_pushbutton.setDisabled(contents_match)
if contents_match:
self.status_label.setText(self.status_same)
return True
else:
return False
def external_file_changed(self):
if not self.check_if_setups_match():
self.status_label.setText(self.status_external_file_changed)
def yaml_setup_changed(self):
if not self.check_if_setups_match():
self.status_label.setText(self.status_editor_code_changed)
class ProcessEditWidget(QWidget):
# TODO: move process to another part in yaml
def __init__(self, tape: dict):
super().__init__()
self.tape = tape
self.cbox_clip_start = QCheckBox("Clip at start")
self.cbox_clip_end = QCheckBox("Clip at end")
self.cbox_volume_factor = QCheckBox("Volume factor")
self.clip_start = TimeEditWithoutWheel()
self.clip_start.setDisplayFormat("hh:mm:ss.z")
self.clip_end = TimeEditWithoutWheel()
self.clip_end.setDisplayFormat("hh:mm:ss.z")
self.volume_factor = SpinBoxWithoutWheel()
self.volume_factor.setSuffix(" dB")
self.volume_factor.setMinimum(-30)
self.volume_factor.setMaximum(30)
layout = QGridLayout(self)
layout.setContentsMargins(5, 0, 5, 0)
layout.setColumnStretch(3, 1)
layout.addWidget(self.cbox_clip_start, 0, 0)
layout.addWidget(self.cbox_clip_end, 1, 0)
layout.addWidget(self.cbox_volume_factor, 2, 0)
layout.addWidget(self.clip_start, 0, 1)
layout.addWidget(self.clip_end, 1, 1)
layout.addWidget(self.volume_factor, 2, 1)
self.load()
self.clip_start.timeChanged.connect(self.save)
def load(self):
clip_start, clip_end = self.tape["process"]["start"], self.tape["process"]["end"]
start, end = QTime(), QTime()
start_time = datetime.datetime.strptime(clip_start, "%H:%M:%S.%f")
end_time = datetime.datetime.strptime(clip_end, "%H:%M:%S.%f")
start.setHMS(start_time.hour, start_time.minute, start_time.second, ms=start_time.microsecond * 1e-3)
end.setHMS(end_time.hour, end_time.minute, end_time.second, ms=end_time.microsecond * 1e-3)
self.clip_start.setTime(start)
self.clip_end.setTime(end)
self.cbox_clip_start.setChecked(clip_start != "00:00:00.0")
self.cbox_clip_end.setChecked(clip_end != "00:00:00.0")
volume_factor = self.tape["process"]["volume"]
self.volume_factor.setValue(volume_factor)
self.cbox_volume_factor.setChecked(volume_factor != 0)
def save(self):
start = self.clip_start.time()
end = self.clip_end.time()
self.tape["process"]["start"] = f"{start.hour():02}:{start.minute():02}:{start.second():02}.{start.msec():03}"
self.tape["process"]["end"] = f"{end.hour():02}:{end.minute():02}:{end.second():02}.{end.msec():03}"
self.tape["process"]["volume"] = self.volume_factor.value()
class EconomyEditWidget(QWidget):
def __init__(self, tape: dict):
super().__init__()
self.tape = tape
self.spinbox_price = SpinBoxWithoutWheel()
self.spinbox_price.setRange(10, 100000)
self.spinbox_price.setSuffix(" mk")
self.spinbox_price.setSizePolicy(QSizePolicy.Policy.Maximum, QSizePolicy.Policy.Maximum)
layout = QGridLayout(self)
layout.setContentsMargins(5, 0, 5, 0)
layout.addWidget(self.spinbox_price, 1, 0)
layout.setColumnStretch(4, 1)
for i, text in enumerate(["Price", "Sold in", "Factor", " Local price"]):
layout.addWidget(change_font_size(QLabel(text), 0.8), 0, i)
self.economy = {"outpost": {},
"city": {},
"research": {},
"military": {},
"mine": {}}
for i, (location, editors) in enumerate(self.economy.items()):
editors["cbox"] = QCheckBox(location.capitalize())
editors["float"] = DoubleSpinBoxWithoutWheel()
editors["float"].setSingleStep(0.05)
editors["label"] = QLabel()
editors["label"].setFont("consolas")
layout.addWidget(editors["cbox"], int(i) + 1, 1)
layout.addWidget(editors["float"], int(i) + 1, 2)
layout.addWidget(editors["label"], int(i) + 1, 3)
self.load()
self.update_labels()
self.spinbox_price.valueChanged.connect(self.save)
for editors in self.economy.values():
editors["cbox"].toggled.connect(self.save)
editors["float"].valueChanged.connect(self.save)
def load(self):
self.spinbox_price.setValue(self.tape["price"])
for locale in self.tape["economy"]:
self.economy[locale["location"]]["cbox"].setChecked(locale["sold"])
self.economy[locale["location"]]["float"].setValue(locale["factor"])
def save(self):
self.tape["price"] = self.spinbox_price.value()
self.tape["economy"] = [{"location": location,
"factor": editors["float"].value(),
"sold": editors["cbox"].isChecked()} for location, editors in self.economy.items()]
self.update_labels()
def update_labels(self):
for editors in self.economy.values():
editors["label"].setText(f" {self.tape['price'] * editors['float'].value():4.0f} mk")
class SpawnEditWidget(QWidget):
def __init__(self, tape: dict):
super().__init__()
self.tape = tape
layout = QGridLayout(self)
layout.setContentsMargins(5, 0, 5, 0)
layout.setColumnStretch(2, 1)
for i, text in enumerate(["Place", "Probability"]):
layout.addWidget(change_font_size(QLabel(text), 0.8), 0, i)
self.spawn = {"outpostcrewcabinet": {"label": QLabel("Outpost Crew Cabinet"),
"float": ProbabilitySpinBox()},
"wreckstorage": {"label": QLabel("Wreck Storage"),
"float": ProbabilitySpinBox()},
"abandonedcrewcab": {"label": QLabel("Abandoned Crew Cabinet"),
"float": ProbabilitySpinBox()},
"abandonedstoragecab": {"label": QLabel("Abandoned Storage Cabinet"),
"float": ProbabilitySpinBox()}}
for i, (location, editors) in enumerate(self.spawn.items()):
layout.addWidget(editors["label"], int(i) + 1, 0)
layout.addWidget(editors["float"], int(i) + 1, 1)
self.load()
for i, (location, editors) in enumerate(self.spawn.items()):
editors["float"].valueChanged.connect(self.save)
def load(self):
for locale in self.tape["spawn"]:
self.spawn[locale["location"]]["float"].setValue(locale["probability"])
def save(self):
self.tape["spawn"] = [{"location": location,
"probability": editors["float"].value()} for location, editors in self.spawn.items() if
editors["float"].value() > 0]
# TODO: afflictions with shorter range for walkman songs.
class CraftingEditWidget(QWidget):
def __init__(self, tape: dict):
super().__init__()
self.tape = tape
self.int_uses = SpinBoxWithoutWheel()
self.int_uses.setMinimum(1)
self.int_uses.setMaximum(1000)
self.description = QPlainTextEdit("One use is duration of the tape")
self.description.setEnabled(False)
self.description.setMaximumHeight(QFontMetrics(self.description.font()).height() * 1.7)
layout = QHBoxLayout(self)
layout.addWidget(QLabel("Number of uses:"))
layout.addWidget(self.int_uses)
layout.addWidget(self.description)
layout.addStretch()
layout.setContentsMargins(5, 0, 5, 0)
self.load()
self.int_uses.valueChanged.connect(self.save)
def load(self):
self.int_uses.setValue(self.tape["no_of_uses"])
def save(self):
self.tape["no_of_uses"] = self.int_uses.value()
class AfflictionsEditWidget(QWidget):
def __init__(self, tape: dict):
super().__init__()
self.tape = tape
self.description = QPlainTextEdit("Factor 1.0 means that the affliction will reach the "
"highest level after being exposed to the sound of the "
"tape for its play duration.")
self.description.setEnabled(False)
self.description.setMaximumWidth(212)
layout = QGridLayout(self)
layout.setContentsMargins(5, 0, 5, 0)
layout.addWidget(change_font_size(QLabel("Affliction"), 0.8), 0, 0)
layout.addWidget(change_font_size(QLabel("Factor"), 0.8), 0, 1)
layout.addWidget(self.description, 1, 2, 3, 1)
layout.setColumnStretch(3, 1)
self.afflictions = {"strengthen": {},
"haste": {},
"psychosis": {}}
for i, (affliction, editors) in enumerate(self.afflictions.items()):
editors["cbox"] = QCheckBox(affliction.capitalize())
editors["float"] = DoubleSpinBoxWithoutWheel()
editors["float"].setSingleStep(0.01)
layout.addWidget(editors["cbox"], int(i) + 1, 0)
layout.addWidget(editors["float"], int(i) + 1, 1)
self.load()
for editors in self.afflictions.values():
editors["cbox"].toggled.connect(self.save)
editors["float"].valueChanged.connect(self.save)
def load(self):
if self.tape["afflictions"]:
for affliction in self.tape["afflictions"]:
self.afflictions[affliction["name"]]["cbox"].setChecked(True)
self.afflictions[affliction["name"]]["float"].setValue(affliction["factor"])
def save(self):
self.tape["afflictions"] = [{"name": affliction, "factor": editors["float"].value()} for affliction, editors in
self.afflictions.items() if editors["cbox"].isChecked()]
class TapeEditWidget(QWidget):
def __init__(self, tape: dict, number: int | None = None):
super().__init__()
self.tape = tape
self.label_nr = QLabel(f"#{number}")
self.label_nr.setAlignment(Qt.AlignmentFlag.AlignCenter)
self.label_nr.setStyleSheet("background-color: #cfd8dc;")
self.edit_identifier = QLineEdit()
self.edit_identifier.setFont("Consolas")
self.edit_identifier.setStyleSheet("font-weight: bold; background-color: #cfd8dc;")
self.edit_identifier.setTextMargins(2, 0, 2, 0)
self.edit_identifier.setFrame(QFrame.Shape.NoFrame)
self.edit_identifier.setText(self.tape["identifier"])
change_font_size(self.edit_identifier, 1.41)
change_font_size(self.label_nr, 1.41)
pix = QPixmap(f"../source/images/{self.tape['identifier']}.png")
self.pixmap = pix.scaled(350, 350,
Qt.AspectRatioMode.KeepAspectRatio,
Qt.TransformationMode.SmoothTransformation)
self.cover = QLabel()
self.cover.resize(350, 0)
self.cover.setPixmap(self.pixmap)
self.edit_name = QLineEdit()
self.edit_name.setText(self.tape["name"])
source = self.tape["source"]
self.edit_source = UrlWidget(source)
self.process = ProcessEditWidget(self.tape)
self.economy = EconomyEditWidget(self.tape)
self.spawn = SpawnEditWidget(self.tape)
self.afflictions = AfflictionsEditWidget(self.tape)
self.crafting = CraftingEditWidget(self.tape)
layout = QGridLayout(self)
layout.setContentsMargins(0, 0, 0, 5)
for i, (text, widget) in enumerate([(f"", self.edit_identifier),
("Cover", self.cover),
("Name", self.edit_name),
("Source", self.edit_source),
("Process", self.process),
("Economy", self.economy),
("Spawn", self.spawn),
("Crafting", self.crafting),
("Afflictions", self.afflictions),
("Note", QPlainTextEdit(self.tape["note"]))]):
label = self.label_nr if i == 0 else QLabel(text)
if i == 0:
layout.addWidget(label, i, 0)
else:
label_layout = QVBoxLayout()
label_layout.addWidget(label)
label_layout.addStretch()
layout.addLayout(label_layout, i, 0)
layout.addWidget(widget, i, 1)
class TapeListWidget(QWidget):
def __init__(self):
super().__init__()
self.layout = QVBoxLayout(self)
self.layout.setContentsMargins(1, 0, 0, 0)
def append(self, widget):
self.layout.addWidget(widget)
class ValidationWidget(QWidget):
def __init__(self):
super().__init__()
self.tapes = self.load_tapes()
self.transfer_to_code_pushbutton = QPushButton()
self.transfer_to_code_pushbutton.setIcon(QIcon("./icons/ic_keyboard_double_arrow_left_24px.svg"))
self.transfer_to_code_pushbutton.setToolTip("Update the code")
self.jump_spinbox = QSpinBox()
#self.search_lineedit = QLineEdit()
#self.search_lineedit.setPlaceholderText("Search ...")
self.buttons_hbox = QHBoxLayout()
self.buttons_hbox.addWidget(self.transfer_to_code_pushbutton)
self.buttons_hbox.addWidget(QLabel("Navigate to"))
self.buttons_hbox.addWidget(self.jump_spinbox)
self.buttons_hbox.addStretch()
#self.buttons_hbox.addWidget(self.search_lineedit)
self.tape_list_widget = TapeListWidget()
self.scroll = QScrollArea()
self.scroll.setWidget(self.tape_list_widget)
self.scroll.setWidgetResizable(True)
self.scroll.setFrameStyle(QFrame.Shape.NoFrame)
self.scroll.setHorizontalScrollBarPolicy(Qt.ScrollBarPolicy.ScrollBarAlwaysOff)
self.validation_vbox = QVBoxLayout()
self.validation_vbox.addLayout(self.buttons_hbox)
self.validation_vbox.addWidget(self.scroll)
self.validation_gbox = QGroupBox("Data validation")
self.validation_gbox.setLayout(self.validation_vbox)
for i, tape in enumerate(self.tapes):
self.tape_list_widget.append(TapeEditWidget(tape, i))
self.jump_spinbox.setMaximum(i)
self.jump_spinbox.valueChanged.connect(self.jump_to_tape)
layout = QVBoxLayout(self)
layout.addWidget(self.validation_gbox)
self.setMinimumWidth(470)
self.setMaximumWidth(470)
def load_tapes(self):
with open("../source/tapes.yaml", encoding="utf-8") as fp:
tapes = yaml.safe_load(fp)
return tapes
def jump_to_tape(self, tape_number):
scrollbar = self.scroll.verticalScrollBar()
division = (scrollbar.maximum() + scrollbar.pageStep()) / len(self.tapes)
overflow_ratio = (scrollbar.pageStep() + 5) / division
step = scrollbar.maximum() / (len(self.tapes) - overflow_ratio)
scrollbar.setValue(tape_number * step)
class InstallWidget(QWidget):
def __init__(self):
super().__init__()
self.install_grid = QGridLayout()
hr_html_string = "<hr style=\"background-color:gray;\">"
self.ffmpeg_checkbox = QCheckBox()
self.ffmpeg_checkbox.setDisabled(True)
self.ffmpeg_label = QLabel("Download and unpack ffmpeg")
self.ffmpeg_label_link = QLabel("<a href=\"https://www.gyan.dev/ffmpeg/builds/ffmpeg-release-full.7z\">"
"gyan.dev/ffmpeg/builds/ffmpeg-release-full.7z</a>")
self.ffmpeg_label_link.setTextInteractionFlags(Qt.TextInteractionFlag.TextBrowserInteraction)
self.ffmpeg_label_link.setTextInteractionFlags(Qt.TextInteractionFlag.LinksAccessibleByMouse)
self.ffmpeg_label_link.setOpenExternalLinks(True)
self.ffmpeg_dir = DirWidget("./utils")
self.ffmpeg_pushbutton = QPushButton("Download")
self.install_grid.addWidget(self.ffmpeg_checkbox, 0, 0)
self.install_grid.addWidget(self.ffmpeg_label, 0, 1)
self.install_grid.addWidget(self.ffmpeg_label_link, 1, 1)
self.install_grid.addWidget(self.ffmpeg_dir, 2, 1)
self.install_grid.addWidget(self.ffmpeg_pushbutton, 0, 2)
self.install_grid.addWidget(QLabel(hr_html_string), 3, 0, 1, 3)
self.git_update_checkbox = QCheckBox()
self.git_update_checkbox.setDisabled(True)
self.git_update_label = QLabel("Download update from git")
self.git_update_label_link = QLabel("<a href=\"https://git.kompot.si/jaka/barotrauma-sunken-tapes\">"
"git.kompot.si/jaka/barotrauma-sunken-tapes</a>")
self.git_update_label_link.setTextInteractionFlags(Qt.TextInteractionFlag.TextBrowserInteraction)
self.git_update_label_link.setTextInteractionFlags(Qt.TextInteractionFlag.LinksAccessibleByMouse)
self.git_update_label_link.setOpenExternalLinks(True)
self.git_update_pushbutton = QPushButton("Update")
self.install_grid.addWidget(self.git_update_checkbox, 10, 0)
self.install_grid.addWidget(self.git_update_label, 10, 1)
self.install_grid.addWidget(self.git_update_label_link, 11, 1)
self.install_grid.addWidget(self.git_update_pushbutton, 10, 2)
self.install_grid.addWidget(QLabel(hr_html_string), 12, 0, 1, 3)
self.songs_download_checkbox = QCheckBox()
self.songs_download_checkbox.setDisabled(True)
self.songs_download_label = QLabel("Download songs and process them")
self.songs_download_dir = DirWidget("./build/music")
self.songs_download_pushbutton = QPushButton("Download")
self.install_grid.addWidget(self.songs_download_checkbox, 20, 0)
self.install_grid.addWidget(self.songs_download_label, 20, 1)
self.install_grid.addWidget(self.songs_download_pushbutton, 20, 2)
self.install_grid.addWidget(self.songs_download_dir, 21, 1)
self.install_grid.addWidget(QLabel(hr_html_string), 22, 0, 1, 3)
self.compile_checkbox = QCheckBox()
self.compile_checkbox.setDisabled(True)
self.compile_label = QLabel("Compile mod according to the settings")
self.compile_dir = DirWidget("./build")
self.compile_pushbutton = QPushButton("Compile")
self.install_grid.addWidget(self.compile_checkbox, 30, 0)
self.install_grid.addWidget(self.compile_label, 30, 1)
self.install_grid.addWidget(self.compile_pushbutton, 30, 2)
self.install_grid.addWidget(self.compile_dir, 31, 1)
self.install_grid.addWidget(QLabel(hr_html_string), 32, 0, 1, 3)
self.install_checkbox = QCheckBox()
self.install_checkbox.setDisabled(True)
self.install_label = QLabel("Copy the files to the install directory")
self.install_dir = DirWidget()
self.install_pushbutton = QPushButton("Install")
self.install_grid.addWidget(self.install_checkbox, 40, 0)
self.install_grid.addWidget(self.install_label, 40, 1)
self.install_grid.addWidget(self.install_pushbutton, 40, 2)
self.install_grid.addWidget(self.install_dir, 41, 1)
self.install_grid.addWidget(QLabel(hr_html_string), 42, 0, 1, 3)
self.install_grid.setRowStretch(50, 20)
self.install_gbox = QGroupBox("Install steps")
self.install_gbox.setLayout(self.install_grid)
layout = QHBoxLayout()
layout.addWidget(self.install_gbox)
layout.setContentsMargins(0, 0, 0, 0)
self.setLayout(layout)
class MainWidget(QWidget):
def __init__(self):
super().__init__()
layout = QHBoxLayout()
options = OptionsWidget()
editor = EditorWidget()
validation = ValidationWidget()
# install = InstallWidget()
layout.addWidget(options)
layout.addWidget(editor)
layout.addWidget(validation)
# layout.addWidget(install)
self.setLayout(layout)
class MainWindow(QMainWindow):
def __init__(self):
super().__init__()
self.setWindowTitle("Sunken Tapes installer")
self.quit_shortcut = QShortcut(QKeySequence('Ctrl+Q'), self)
self.quit_shortcut.activated.connect(QApplication.instance().quit)
self.setCentralWidget(MainWidget())
def main():
app = QApplication(sys.argv)
window = MainWindow()
window.show()
sys.exit(app.exec())
if __name__ == "__main__":
main()

226
gui/widgets.py 100644
View File

@ -0,0 +1,226 @@
from PySide6.QtCore import QSize, Qt, Slot, QRect, QFileSystemWatcher, Signal, QTime
from PySide6.QtGui import QIcon, QAction, QShortcut, QKeySequence, QPainter, QColor, QTextFormat, QPixmap, QFontMetrics
from PySide6.QtWidgets import QApplication, QMainWindow, QPushButton, QWidget, QLabel, QVBoxLayout, QHBoxLayout, \
QGroupBox, QLineEdit, QCheckBox, QSpinBox, QPlainTextEdit, QSizePolicy, QGridLayout, QTextEdit, QScrollArea, QFrame, \
QDoubleSpinBox, QFormLayout, QTimeEdit
import webbrowser
import validators
import datetime
from pathlib import Path
class LineNumberArea(QWidget):
def __init__(self, editor):
QWidget.__init__(self, editor)
self._code_editor = editor
def sizeHint(self):
return QSize(self._code_editor.line_number_area_width(), 0)
def paintEvent(self, event):
self._code_editor.line_number_area_paint_event(event)
class CodeEditor(QPlainTextEdit):
def __init__(self):
super().__init__()
self.line_number_area = LineNumberArea(self)
self.blockCountChanged[int].connect(self.update_line_number_area_width)
self.updateRequest[QRect, int].connect(self.update_line_number_area)
self.cursorPositionChanged.connect(self.highlight_current_line)
def line_number_area_width(self):
digits = len(str(self.blockCount()))
space = 3 + self.fontMetrics().horizontalAdvance('9') * digits
return space
def resizeEvent(self, e):
super().resizeEvent(e)
cr = self.contentsRect()
self.line_number_area.setGeometry(QRect(cr.left(),
cr.top(),
self.line_number_area_width(),
cr.height()))
def line_number_area_paint_event(self, event):
with QPainter(self.line_number_area) as painter:
painter.fillRect(event.rect(), Qt.white)
painter.setFont("Consolas")
block = self.firstVisibleBlock()
block_number = block.blockNumber()
top = self.blockBoundingGeometry(block).translated(self.contentOffset()).top()
bottom = top + self.blockBoundingRect(block).height()
while block.isValid() and top <= event.rect().bottom():
if block.isVisible() and bottom >= event.rect().top():
painter.setPen(Qt.gray)
painter.drawText(-2, top,
self.line_number_area.width(),
self.fontMetrics().height(),
Qt.AlignmentFlag.AlignRight,
f"{block_number + 1}")
block = block.next()
top = bottom
bottom = top + self.blockBoundingRect(block).height()
block_number += 1
def update_line_number_area_width(self):
self.setViewportMargins(self.line_number_area_width(), 0, 0, 0)
def update_line_number_area(self, rect, dy):
if dy:
self.line_number_area.scroll(0, dy)
else:
self.line_number_area.update(0, rect.y(), self.line_number_area.width(), rect.height())
if rect.contains(self.viewport().rect()):
self.update_line_number_area_width()
def highlight_current_line(self):
extra_selections = []
if not self.isReadOnly():
selection = QTextEdit.ExtraSelection()
selection.format.setBackground(QColor(Qt.lightGray))
selection.format.setProperty(QTextFormat.FullWidthSelection, True)
selection.cursor = self.textCursor()
selection.cursor.clearSelection()
extra_selections.append(selection)
self.setExtraSelections(extra_selections)
class SpinBoxWithoutWheel(QSpinBox):
def wheelEvent(self, event):
event.ignore()
class DoubleSpinBoxWithoutWheel(QDoubleSpinBox):
def wheelEvent(self, event):
event.ignore()
class ProbabilitySpinBox(DoubleSpinBoxWithoutWheel):
def __init__(self):
super().__init__()
self.setSingleStep(0.01)
self.valueChanged.connect(self.update_color)
self.update_color()
def update_color(self):
if self.value() > 0:
self.setStyleSheet("color: black;")
else:
self.setStyleSheet("color: gray;")
class TimeEditWithoutWheel(QTimeEdit):
def wheelEvent(self, event):
event.ignore()
class DirWidget(QWidget):
def __init__(self, default_dir: Path | str | None = None):
super().__init__()
layout = QHBoxLayout()
layout.setSpacing(2)
layout.setContentsMargins(0, 0, 0, 0)
self.dir_lineedit = QLineEdit()
self.dir_lineedit.setFont("Consolas")
self.dir_lineedit.setText(default_dir)
self.dir_set_btn = QPushButton()
self.dir_set_btn.setIcon(QIcon("./icons/ic_folder_open_48px.svg"))
self.dir_set_btn.setIconSize(QSize(16, 16))
self.dir_set_btn.setToolTip("Set directory with selection dialog")
self.dir_open_btn = QPushButton()
self.dir_open_btn.setIcon(QIcon("./icons/ic_open_in_new_24px.svg"))
self.dir_open_btn.setIconSize(QSize(16, 16))
self.dir_open_btn.setToolTip("Open directory")
layout.addWidget(self.dir_set_btn)
layout.addWidget(self.dir_lineedit)
layout.addWidget(self.dir_open_btn)
self.setLayout(layout)
class UrlLineEdit(QLineEdit):
def __init__(self, initial_text: str | None = None):
super().__init__()
self.setFont("Consolas")
self.textChanged.connect(self.color_text)
self.setText(initial_text)
def mousePressEvent(self, event):
modifiers = QApplication.keyboardModifiers()
if modifiers == Qt.ControlModifier:
self.open_url()
def open_url(self):
if validators.url(self.text()):
webbrowser.open(self.text())
def color_text(self):
if validators.url(self.text()):
self.setStyleSheet("color: blue;")
else:
self.setStyleSheet("color: black;")
class UrlWidget(QWidget):
def __init__(self, initial_url: str | None = None):
super().__init__()
layout = QHBoxLayout()
layout.setSpacing(2)
layout.setContentsMargins(0, 0, 0, 0)
self.url_lineedit = UrlLineEdit(initial_url)
self.url_lineedit.textChanged.connect(self.toggle_url_open_btn)
self.url_open_btn = QPushButton()
self.url_open_btn.setIcon(QIcon("./icons/ic_open_in_new_24px.svg"))
self.url_open_btn.setIconSize(QSize(16, 16))
self.url_open_btn.setToolTip("Open in browser")
self.url_open_btn.clicked.connect(self.url_lineedit.open_url)
self.toggle_url_open_btn()
layout.addWidget(self.url_lineedit)
layout.addWidget(self.url_open_btn)
self.setLayout(layout)
def toggle_url_open_btn(self):
url_valid = bool(validators.url(self.url_lineedit.text()))
self.url_open_btn.setEnabled(url_valid)
class TelegraphingCheckbox(QCheckBox):
hover = Signal(str)
def __init__(self, text: str, description_text: str | None = None):
super().__init__(text)
self.description_text = description_text
def enterEvent(self, event):
self.hover.emit(self.description_text)
def leaveEvent(self, event):
self.hover.emit("")

16
pyproject.toml 100644
View File

@ -0,0 +1,16 @@
[tool.poetry]
name = "barotrauma-sunken-tapes"
version = "0.1.0"
description = ""
authors = ["Jaka <jaka@kompot.si>"]
readme = "README.md"
[tool.poetry.dependencies]
python = "^3.10"
pyyaml = "^6.0.1"
validators = "^0.22.0"
[build-system]
requires = ["poetry-core"]
build-backend = "poetry.core.masonry.api"

0
requirements.txt 100644
View File

File diff suppressed because it is too large Load Diff