Add all buttons to the editor.
@@ -0,0 +1,6 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<svg width="24px" height="24px" viewBox="176 25 1750 1750" xmlns="http://www.w3.org/2000/svg">
|
||||
<g stroke="none" fill="none" fill-rule="evenodd">
|
||||
<path d="M 381.594 296.386 C 410.645 295.433 443.296 296.178 472.583 296.181 L 638.694 296.16 L 1150.39 296.157 L 1526.08 296.167 L 1645.64 296.085 C 1666.93 296.062 1688.83 295.751 1710.03 296.347 C 1724.43 297.103 1735.71 299.471 1747.4 308.203 C 1773.76 327.91 1780.11 366.464 1759.7 392.741 C 1747.15 408.905 1733.23 414.469 1713.23 416.945 C 1684.03 417.895 1650.82 417.13 1621.37 417.13 L 1448.97 417.128 L 927.915 417.121 L 561.582 417.146 L 443.244 417.156 C 425.352 417.156 396.674 418.632 379.436 416.771 C 304.588 408.689 301.485 305.408 381.594 296.386 z M 379.571 1384.39 C 385.115 1383.46 412.558 1383.87 419.226 1383.89 L 499.173 1383.97 L 805.998 1383.97 L 1412.93 1383.97 L 1612.71 1383.96 C 1645.03 1383.95 1677.61 1383.74 1709.94 1383.92 C 1722.23 1383.99 1735.87 1388.38 1745.99 1395.54 C 1759.41 1404.93 1768.46 1419.35 1771.1 1435.52 C 1773.53 1451.04 1769.61 1466.89 1760.2 1479.47 C 1748.68 1494.98 1733.92 1502.03 1715.26 1504.62 C 1692.9 1505.98 1660.84 1504.97 1637.92 1504.94 L 1499.55 1504.92 L 1060.41 1504.95 L 608.88 1504.95 L 461.022 1504.99 C 440.093 1505 419.154 1505.07 398.225 1504.99 C 378.436 1504.91 361.724 1504.83 345.676 1491.88 C 332.984 1481.82 324.858 1467.09 323.118 1450.99 C 319.354 1415.22 344.356 1387.44 379.571 1384.39 z M 986.57 659.423 C 995.068 658.388 1014.88 658.787 1023.74 658.8 L 1086.39 658.87 L 1303.27 658.845 L 1578.18 658.832 L 1664.95 658.84 C 1693.15 658.846 1726.71 654.386 1749.85 672.678 C 1762.02 682.183 1769.76 696.271 1771.24 711.644 C 1775.12 750.081 1749.6 775.831 1712.4 779.515 C 1661.76 781.02 1604.9 779.811 1553.88 779.818 L 1252.78 779.83 L 1074.15 779.813 C 1047.95 779.809 1021.73 779.871 995.533 779.687 C 978.09 779.565 961.597 777.932 948.143 765.47 C 905.851 726.299 928.093 663.606 986.57 659.423 z M 988.986 1021.45 L 1480.2 1021.37 L 1635.14 1021.36 C 1662.42 1021.36 1691.27 1020.69 1718.45 1022.38 C 1730.83 1023.15 1740.77 1027.55 1750.37 1035.58 C 1762.68 1046.06 1770.31 1061.02 1771.56 1077.14 C 1773.71 1104.41 1756.39 1131.69 1729.6 1139.19 C 1723.04 1141.02 1717.69 1141.38 1710.88 1141.91 C 1657.59 1143.22 1600.56 1142.23 1547.06 1142.22 L 1248.88 1142.24 L 1073.8 1142.24 C 1043.61 1142.24 1013.33 1142.71 983.174 1141.56 C 953.351 1140.43 928.906 1115.93 926.55 1086.45 C 925.417 1070.33 930.722 1054.42 941.301 1042.2 C 954.569 1027.15 969.769 1022.74 988.986 1021.45 z M 506.131 659.031 C 507.65 660.272 529.49 697.154 531.868 701.068 C 524.89 706.311 510.914 713.754 502.923 718.497 C 490.974 725.631 479.442 733.44 468.383 741.888 C 418.453 779.552 399.216 815.231 390.794 875.324 C 432.561 875.377 486.096 874.25 508.446 916.446 C 532.074 961.055 513.539 1013.58 470.046 1036.1 C 429.228 1050.58 369.621 1039.18 346.669 999.451 C 322.32 957.31 317.643 901.231 327.353 853.848 C 331.944 831.446 342.399 811.985 354.249 792.826 C 393.45 728.802 444.712 697.435 506.131 659.031 z M 767.337 659.978 C 772.342 661.749 788.25 693.832 794.27 701.346 C 722.576 742.192 660.121 784.83 653.102 875.137 C 680.36 875.815 701.361 873.552 728.078 882.343 C 797.957 905.338 797.13 1005.89 732.273 1035.64 C 712.255 1043.59 680.435 1042.84 659.894 1037.19 C 636.774 1030.84 618.359 1016.89 606.778 995.574 C 583.149 952.09 578.489 892.235 591.77 844.56 C 597.447 824.18 605.657 810.372 616.206 793.2 C 644.784 746.677 684.816 712.21 730.426 683.174 C 742.582 675.435 754.444 667.064 767.337 659.978 z" fill="#FFFFFF"></path>
|
||||
</g>
|
||||
</svg>
|
||||
|
After Width: | Height: | Size: 3.5 KiB |
@@ -0,0 +1,6 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<svg width="24px" height="24px" viewBox="133 -13 1739 1739" xmlns="http://www.w3.org/2000/svg">
|
||||
<g stroke="none" fill="none" fill-rule="evenodd">
|
||||
<path d="M 742.94 1298.42 C 809.544 1297.7 877.257 1298.29 943.95 1298.31 L 1312.1 1298.3 L 1543.3 1298.29 C 1583.2 1298.29 1623.13 1298.09 1663.02 1298.5 C 1679.07 1298.67 1691.49 1301.54 1704.36 1311.32 C 1716.89 1321.03 1725.05 1335.32 1727.04 1351.05 C 1729.17 1367.39 1724.59 1383.89 1714.34 1396.78 C 1702.48 1411.47 1687.01 1417 1668.87 1419.02 C 1598.6 1419.92 1526.58 1419.18 1456.18 1419.18 L 1055.12 1419.19 L 844.558 1419.2 L 777.994 1419.21 C 750.68 1419.19 725.152 1422.95 702.763 1404.3 C 690.628 1394.23 683.106 1379.66 681.92 1363.93 C 680.573 1347.85 685.749 1331.91 696.282 1319.7 C 709.256 1304.58 723.701 1299.85 742.94 1298.42 z M 749.429 332.46 L 1364.75 332.423 L 1568.33 332.378 L 1630.83 332.375 C 1645.19 332.378 1658.47 332.346 1672.9 333.315 C 1723.42 336.707 1745.39 398.751 1710.28 434.452 C 1695.5 449.489 1682.39 452.61 1661.53 453.132 L 1031.44 453.112 L 835.065 453.134 L 775.197 453.141 C 762.649 453.15 750.514 453.238 737.934 452.579 C 686.999 449.907 663.787 387.885 698.229 351.076 C 713.372 334.892 728.436 333.219 749.429 332.46 z M 733.986 816.261 C 749.227 814.965 769.146 815.524 784.756 815.546 L 866.641 815.615 L 1129.69 815.602 L 1488.8 815.595 L 1605.25 815.601 C 1626.87 815.603 1648.17 815.572 1670.01 815.925 C 1695.47 815.762 1720.81 836.711 1725.93 861.642 C 1733.41 898.058 1712.2 927.712 1676.81 935.147 C 1665.66 936.647 1645.63 936.15 1633.77 936.145 L 1563.02 936.085 L 1317.4 936.091 L 932.104 936.105 L 806.031 936.111 C 783.79 936.109 761.121 936.329 738.883 935.762 C 727.845 935.48 716.862 931.561 707.699 925.354 C 694.57 916.379 685.578 902.524 682.728 886.878 C 676.015 851.044 699.2 822.67 733.986 816.261 z M 358.803 1258.41 C 403.013 1258.04 439.178 1293.53 439.646 1337.74 C 440.113 1381.95 404.706 1418.19 360.499 1418.76 C 316.152 1419.32 279.769 1383.78 279.3 1339.43 C 278.831 1295.09 314.454 1258.78 358.803 1258.41 z M 347.536 293.423 C 391.473 286.781 432.42 317.168 438.793 361.143 C 445.166 405.119 414.529 445.88 370.515 451.984 C 326.88 458.035 286.547 427.719 280.229 384.122 C 273.911 340.526 303.979 300.008 347.536 293.423 z M 347.172 776.497 C 390.945 769.694 431.93 799.709 438.654 843.494 C 445.379 887.28 415.29 928.211 371.493 934.857 C 327.807 941.486 287.002 911.491 280.294 867.816 C 273.586 824.142 303.509 783.283 347.172 776.497 z" fill="#FFFFFF"></path>
|
||||
</g>
|
||||
</svg>
|
||||
|
After Width: | Height: | Size: 2.5 KiB |
@@ -0,0 +1,6 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<svg width="24px" height="24px" viewBox="-123 -111 2297 2297" xmlns="http://www.w3.org/2000/svg">
|
||||
<g stroke="none" fill="none" fill-rule="evenodd">
|
||||
<path d="M 246.414 1124.42 L 247.196 1124.39 C 282.826 1123.37 323.488 1124.3 359.551 1124.3 L 577.983 1124.31 L 1242.11 1124.32 L 1627.35 1124.29 L 1747.69 1124.29 C 1789.52 1124.29 1833.48 1117.53 1866.15 1148.47 C 1883.25 1164.45 1893.09 1186.7 1893.42 1210.09 C 1893.91 1233.41 1885.01 1255.95 1868.72 1272.65 C 1851.66 1290.03 1829.99 1297.96 1805.98 1298.33 L 733.059 1298.3 L 398.019 1298.35 L 291.066 1298.35 C 272.886 1298.35 253.852 1298.89 235.737 1297.91 C 193 1295.58 159.179 1259.52 156.316 1217.26 C 154.856 1194.09 162.751 1171.3 178.232 1154 C 197.759 1132.09 218.251 1126.12 246.414 1124.42 z M 234.316 777.332 C 249.087 775.668 284.999 776.616 301.118 776.633 L 426.433 776.684 L 849.622 776.682 L 1491.29 776.674 L 1699.95 776.658 C 1733.22 776.639 1778.09 775.335 1810.49 777.021 C 1827.93 777.852 1844.73 783.825 1858.78 794.187 C 1877.07 807.983 1889.18 828.442 1892.47 851.117 C 1895.97 873.724 1890.23 896.787 1876.55 915.122 C 1861.53 935.216 1841.91 946.491 1817.25 949.942 C 1784.25 951.517 1744.71 950.545 1711.39 950.543 L 1525 950.516 L 953.17 950.512 L 474.869 950.517 L 319.452 950.561 C 297.507 950.567 275.469 950.517 253.53 950.556 C 229.437 950.598 211.282 948.549 191.445 933.703 C 172.433 919.65 159.965 898.459 156.916 875.015 C 153.889 851.823 160.288 828.387 174.681 809.952 C 189.425 790.94 210.744 780.412 234.316 777.332 z M 1018.3 81.3706 C 1020.5 81.2739 1022.7 81.2119 1024.9 81.1847 C 1047.5 80.9564 1070.15 90.5762 1086.17 106.444 C 1097.96 118.359 1106.08 133.412 1109.56 149.81 C 1113.25 167.386 1111.69 214.772 1111.67 235.506 L 1111.76 393.594 C 1140.23 366.162 1167.43 336.974 1195.86 309.49 C 1219.37 286.765 1237.29 261.097 1271.2 254.796 C 1293.02 250.67 1315.58 255.569 1333.73 268.371 C 1353.26 281.967 1366.62 304.527 1370.89 327.76 C 1375.14 348.297 1369.06 380.582 1354.8 397.07 C 1329.32 426.512 1295.64 456.447 1267.85 484.095 L 1145.52 604.505 C 1126.27 623.645 1107.15 642.958 1087.76 661.966 C 1070.18 679.198 1057.94 686.04 1033.6 690.164 L 1029.63 690.255 C 984.78 691.042 967.574 668.015 937.965 638.37 L 893.696 594.239 L 737.822 441.296 C 707.441 411.839 677.893 389.949 677.519 344.308 C 677.37 319.271 687.22 295.209 704.882 277.462 C 720.509 261.874 741.727 253.192 763.799 253.355 C 806.894 253.528 832.72 288.317 860.866 316.466 L 938.015 393.7 L 938.035 236.591 C 938.004 217.146 936.811 168.142 939.679 152.066 C 942.259 137.74 948.398 124.293 957.534 112.961 C 972.736 93.7945 994.411 84.1162 1018.3 81.3706 z M 1016.54 1385.31 C 1041.51 1381.21 1069.01 1394.83 1086.33 1411.94 C 1107.15 1432.5 1127.66 1453.35 1148.47 1473.91 L 1269.49 1592.94 L 1322.94 1644.91 C 1333.39 1655.12 1348.15 1668.71 1356.19 1680.26 C 1366.27 1694.93 1371.82 1712.23 1372.15 1730.03 C 1372.69 1754.98 1363.13 1779.09 1345.64 1796.9 C 1329.71 1813.27 1310.21 1821.8 1287.35 1822.03 C 1272.28 1822.24 1257.45 1818.23 1244.53 1810.46 C 1228.56 1800.74 1202.16 1772.31 1187.97 1758.1 L 1111.71 1681.92 L 1111.71 1839.87 C 1111.73 1858.34 1112.87 1905.36 1110.3 1921.65 C 1108.25 1934.79 1103.14 1947.27 1095.41 1958.09 C 1080.18 1979.72 1059.56 1989.44 1034.2 1993.94 C 1031.48 1994.03 1028.77 1994.09 1026.05 1994.11 C 1002.68 1994.48 980.133 1985.47 963.45 1969.11 C 943.945 1949.64 937.44 1924.84 937.972 1898.13 C 938.345 1879.42 938.049 1860.25 938.052 1841.49 L 938.022 1681.73 C 910.058 1708.85 882.825 1737.5 854.951 1764.79 C 839.487 1778.54 824.628 1798.65 807.065 1809.44 C 751.216 1843.73 686.643 1804.69 678.287 1744.45 C 675.066 1720.1 681.664 1695.47 696.628 1675.99 C 703.422 1667 723.15 1648.27 732.204 1639.58 C 749.268 1623.34 766.151 1606.91 782.851 1590.29 L 905.023 1470.05 L 946.376 1428.71 C 969.891 1405.27 982.261 1390.28 1016.54 1385.31 z" fill="#FFFFFF"></path>
|
||||
</g>
|
||||
</svg>
|
||||
|
After Width: | Height: | Size: 3.9 KiB |
@@ -0,0 +1,6 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<svg width="24px" height="24px" viewBox="84 92 1143 1143" xmlns="http://www.w3.org/2000/svg">
|
||||
<g stroke="none" fill="none" fill-rule="evenodd">
|
||||
<path d="M 293.896 716.313 C 338.733 714.584 393.269 716.003 438.884 716.004 L 715.015 716.009 L 915.242 716.002 L 978.122 715.963 C 996.286 715.948 1025.38 712.618 1038.96 725.361 C 1045.69 731.801 1049.54 740.682 1049.63 749.995 C 1050.01 778.442 1024.23 787.429 1000.87 785.066 C 996.236 784.598 989.004 785.069 984.15 785.07 L 918.08 785.067 L 690.732 785.132 C 688.302 863.424 692.751 946.408 690.158 1024.87 C 731.333 986.772 768.215 942.465 809.627 904.601 C 813.46 901.095 821.49 899.153 826.544 898.953 C 836.175 898.578 845.546 902.128 852.511 908.79 C 859.594 915.632 862.094 923.971 862.303 933.691 C 862.634 949.14 850.944 959.38 840.904 969.629 L 735.156 1077.79 C 716.426 1097 697.95 1117.08 678.292 1135.35 C 670.257 1143.96 649.253 1147.38 639.508 1139.22 C 614.781 1118.52 588.637 1090.74 565.898 1068.08 L 496.066 998.586 C 482.979 985.573 469.731 972.696 457.047 959.293 C 449.342 951.152 445.641 946.012 445.241 934.544 C 444.934 924.981 448.539 915.706 455.224 908.86 C 461.529 902.462 470.134 898.857 479.116 898.851 C 496.4 898.687 504.617 910.217 515.886 921.21 C 522.22 927.389 528.423 933.735 534.718 939.957 L 621.981 1026.54 L 621.994 785.187 C 547.538 784.218 471.358 785.055 396.784 785.059 L 332.243 785.061 C 317.238 785.061 289.262 787.296 277.289 779.005 C 269.731 773.691 264.557 765.628 262.876 756.544 C 259.09 735.526 273.487 719.32 293.896 716.313 z M 652.963 188.238 C 653.657 188.186 654.352 188.15 655.047 188.13 C 665.569 187.765 674.244 192.163 681.541 199.384 C 695.029 212.73 708.259 226.506 721.484 240.104 L 788.396 308.61 L 830.526 351.574 C 842.661 364.014 862.179 379.79 862.474 398.043 C 862.594 407.397 858.958 416.408 852.379 423.059 C 845.942 429.691 837.061 433.383 827.819 433.269 C 810.451 433.009 801.991 420.668 790.695 409.406 C 783.501 402.232 776.468 394.815 769.368 387.539 L 690.212 306.766 C 691.839 323.565 690.302 356.97 690.574 375.385 C 691.376 429.848 689.398 485.908 690.813 540.271 C 766.446 541.753 846.585 540.409 922.552 540.41 L 982.742 540.385 C 994.024 540.383 1011.2 539.942 1021.91 541.447 C 1028.41 542.382 1034.44 545.376 1039.1 549.989 C 1058.42 568.75 1050.05 602.831 1022.86 608.375 C 1012.31 610.525 991.826 609.376 980.348 609.376 L 908.23 609.373 L 653.5 609.334 L 404.787 609.308 L 333.783 609.4 C 315.738 609.455 286.729 612.928 273.289 600.15 C 266.466 593.761 262.552 584.859 262.457 575.512 C 262.348 560.656 273.14 543.861 288.692 541.549 C 301.782 539.603 317.886 540.363 331.312 540.375 L 396.661 540.385 L 621.934 540.523 L 622.103 305.332 C 615.554 312.442 605.721 321.319 598.627 328.286 L 557.222 369.563 L 522.257 404.381 C 514.959 411.663 507.853 419.435 499.994 425.88 C 480.414 441.935 450.218 429.993 445.851 405.348 C 444.473 398.786 445.489 387.123 449.731 381.692 C 461.109 367.125 475.845 353.54 488.973 340.508 L 552.686 277.131 L 607.478 222.464 C 621.001 209.052 633.905 191.755 652.963 188.238 z" fill="#FFFFFF"></path>
|
||||
</g>
|
||||
</svg>
|
||||
|
After Width: | Height: | Size: 3.1 KiB |
@@ -0,0 +1,6 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<svg width="24px" height="24px" viewBox="63 76 1888 1888" xmlns="http://www.w3.org/2000/svg">
|
||||
<g stroke="none" fill="none" fill-rule="evenodd">
|
||||
<path d="M 318.798 235.434 C 320.317 235.384 321.836 235.343 323.355 235.311 C 360.334 234.51 400.282 235.272 437.443 235.277 L 663.297 235.262 L 777.422 235.243 C 791.056 235.241 823.364 234.447 835.498 235.976 C 849.33 237.846 862.209 244.072 872.266 253.751 C 884.446 265.813 891.426 282.159 891.714 299.298 C 891.911 316.782 885.028 333.603 872.632 345.935 C 864.612 354.084 854.457 359.807 843.332 362.446 C 826.258 366.338 786.752 365.053 767.719 365.045 L 653.105 365.076 C 652.336 430.967 653.063 498.526 653.064 564.541 L 653.078 956.022 L 1360.21 955.968 C 1361.01 879.736 1360.27 801.958 1360.28 725.588 L 1360.18 365.091 C 1310.44 364.258 1260.37 365.428 1210.49 365.046 C 1176.24 364.784 1151.11 364.591 1131.21 331.952 C 1116.84 308.4 1121.77 274.963 1140.74 255.507 C 1153.69 242.229 1170.91 235.065 1189.41 235.342 C 1227.73 235.225 1266.15 235.26 1304.48 235.262 L 1529.74 235.292 L 1641.93 235.235 C 1656.98 235.226 1688.88 234.361 1702.51 236.284 C 1715.67 238.14 1727.9 244.164 1737.4 253.474 C 1749.74 265.281 1756.78 281.582 1756.92 298.665 C 1757.31 332.223 1732.69 361.305 1698.75 364.096 C 1677.18 365.87 1654.41 365.05 1632.68 365.048 L 1518.92 364.985 C 1518.33 414.244 1518.87 464.68 1518.87 514.012 L 1518.87 805.05 L 1518.88 1676.85 L 1637.63 1676.82 C 1653.55 1676.8 1688.96 1675.97 1702.69 1678.12 C 1711.1 1679.47 1719.16 1682.52 1726.35 1687.09 C 1741.1 1696.3 1751.57 1710.99 1755.47 1727.93 C 1759.29 1744.75 1756.13 1762.41 1746.71 1776.85 C 1734.96 1794.88 1719.96 1801.78 1699.82 1806.16 L 1699 1806.18 C 1660.89 1807.16 1619.3 1806.37 1581 1806.37 L 1355.88 1806.36 L 1240.9 1806.35 C 1219.31 1806.35 1195.32 1807.34 1174.08 1804.78 C 1162.76 1803.41 1151.6 1796.64 1143.38 1788.76 C 1130.59 1776.57 1123.2 1759.78 1122.85 1742.11 C 1122.51 1720.14 1135.84 1697.11 1154.76 1686.23 C 1177.95 1672.9 1215.1 1676.78 1241.59 1676.8 L 1360.17 1676.8 C 1361.13 1610.79 1360.27 1542.5 1360.27 1476.34 L 1360.27 1085.87 C 1270.55 1084.81 1178.87 1085.75 1089.02 1085.74 L 653.057 1085.84 L 653.131 1676.75 C 714.06 1678.08 777.258 1674.8 838.22 1678.15 C 848.143 1678.69 863.162 1687.26 870.643 1693.94 C 883.355 1705.39 890.945 1721.46 891.716 1738.56 C 892.687 1755.82 886.628 1772.75 874.92 1785.48 C 862.461 1798.94 845.711 1806.2 827.512 1806.31 C 789.791 1806.55 751.942 1806.36 714.225 1806.34 L 499.466 1806.35 L 375.147 1806.37 C 355.515 1806.37 333.722 1806.78 314.224 1805.86 C 302.355 1805.3 285.954 1796.59 277.239 1788.2 C 265.019 1776.32 258.027 1760.07 257.807 1743.03 C 257.553 1725.38 263.59 1708.91 275.998 1696.44 C 291.121 1681.25 309.066 1676.81 329.944 1676.84 C 346.608 1676.86 363.218 1676.8 379.82 1676.8 L 494.7 1676.88 L 494.753 364.98 L 376.35 365.058 C 359.107 365.064 323.974 366.039 308.975 363.165 C 299.059 361.18 289.758 356.862 281.844 350.567 C 236.492 314.608 257.082 242.107 318.798 235.434 z" fill="#FFFFFF"></path>
|
||||
</g>
|
||||
</svg>
|
||||
|
After Width: | Height: | Size: 3.0 KiB |
@@ -0,0 +1,6 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<svg width="24px" height="24px" viewBox="38 75 905 905" xmlns="http://www.w3.org/2000/svg">
|
||||
<g stroke="none" fill="none" fill-rule="evenodd">
|
||||
<path d="M 380.003 151.159 C 402.581 149.201 418.63 160.332 423.991 182.571 C 422.424 189.292 421.273 191.821 418.503 198.068 C 362.011 204.938 402.908 168.051 400.088 159.985 C 378.904 150.249 372.018 174.985 367.439 190.05 C 340.982 277.09 328.76 370.178 313.023 459.477 C 297.334 544.758 279.36 629.602 259.117 713.918 C 247.764 762.559 224.482 896.442 165.928 904.718 C 164.643 904.794 163.358 904.856 162.072 904.905 C 150.3 905.336 142.02 902.202 133.03 894.117 C 125.208 887.082 116.783 871.185 126.1 861.964 C 132.513 855.617 141.987 856.902 150.324 857.182 C 165.835 879.355 146.949 883.348 146.479 895.12 C 150.171 899.195 160.601 897.874 165.919 897.745 C 196.777 881.18 210.355 799.431 217.296 767.312 C 227.089 721.994 233.822 674.815 241.666 629.07 C 255.879 549.391 271.999 470.064 290.014 391.157 C 297.009 359.461 304.547 327.887 312.627 296.45 C 322.692 256.188 340.116 169.99 380.003 151.159 z M 666.512 375.432 C 669.178 375.333 670.968 374.925 673.135 376.328 C 678.983 390.298 612.073 591.301 626.502 605.291 C 631.204 606.657 636.86 605.973 641.834 605.705 C 656.4 590.556 653.756 576.252 663.307 559.505 C 663.805 558.631 665.522 559.16 666.449 559.279 L 668.061 561.407 C 668.325 565.712 665.753 576.731 664.476 581.22 C 657.681 605.098 650.954 608.146 631.473 619.267 C 615.888 609.603 609.196 612.535 601.924 592.056 C 595.392 598.642 589.607 604.076 581.823 609.139 C 577.978 610.706 559.818 618.238 557.048 618.136 C 549.372 617.854 531.568 609.17 526.604 602.917 C 514.899 588.172 512.046 573.132 513.248 555.179 C 515.153 526.746 529.962 499.841 551.28 481.466 C 563.853 470.628 579.478 461.583 596.601 463.187 C 612.701 464.696 616.496 471.032 626.055 482.346 C 630.339 460.931 642.019 419.587 648.382 398.219 C 637.688 388.934 628.025 387.872 614.466 384.586 C 620.158 383.246 628.015 382.888 634.245 381.637 C 645.106 379.455 655.487 375.883 666.512 375.432 z M 589.301 470.186 C 597.059 469.829 599.689 470.264 606.811 473.64 C 616.219 477.526 622.875 495.873 620.297 505.099 C 611.445 536.777 608.617 594.747 570.478 605.85 C 566.238 606.06 551.631 607.041 548.215 605.274 C 530.743 596.11 537.992 567.059 540.549 551.584 C 545.413 522.144 558.571 481.849 589.301 470.186 z M 742.441 463.337 C 769.892 461.236 769.746 467.889 786.065 484.845 C 789.503 483.649 796.101 474.506 801.045 470.864 C 814.41 461.02 825.476 462.301 840.646 464.326 C 848.798 471.144 855.965 476.944 854.653 488.892 C 852.535 508.181 819.872 512.21 823.038 488.815 C 824.06 481.268 835.578 475.192 841.516 471.509 C 817.498 468.877 801.777 470.302 793.555 495.781 C 787.514 514.504 763.314 590.743 776.866 603.942 C 783.86 607.522 796.563 606.075 804.797 605.723 C 819.566 596.254 825.621 587.456 832.466 571.431 C 834.337 567.051 836.458 561.928 839.902 558.672 L 841.646 558.839 L 842.978 560.873 C 843.802 579.213 827.116 601.001 811.638 610.204 C 808.872 611.849 798.879 614.455 794.967 615.989 C 792.847 617.549 792.488 617.886 789.941 618.467 C 772.273 615.499 761.601 606.653 751.407 592.474 C 738.537 614.444 730.128 608.837 717.48 619.835 C 707.617 614.169 693.741 612.234 687.105 601.556 C 677.942 586.81 695.395 577.195 705.743 570.707 C 711.062 575.53 713.016 577.664 717.478 583.3 C 714.797 598.331 714.135 592.161 706.75 603.835 L 707.144 605.379 C 711.729 607.116 725.91 605.785 731.382 605.327 C 736.133 601.11 739.799 596.375 742.734 590.736 C 751.867 573.187 772.177 502.171 765.909 483.269 C 764.334 478.519 761.4 474.003 756.75 471.815 C 751.445 469.319 743.978 469.851 738.605 471.825 C 719.418 478.872 712.382 498.702 704.76 515.521 C 700.266 518.844 697.615 518.998 694.448 514.004 C 694.176 509.593 697.001 506.938 699.106 503.131 C 709.134 484.998 720.938 468.186 742.441 463.337 z" fill="#FFFFFF"></path>
|
||||
</g>
|
||||
</svg>
|
||||
|
After Width: | Height: | Size: 3.9 KiB |
@@ -0,0 +1,6 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<svg width="24px" height="24px" viewBox="247 137 1585 1585" xmlns="http://www.w3.org/2000/svg">
|
||||
<g stroke="none" fill="none" fill-rule="evenodd">
|
||||
<path d="M 553.996 1003.4 C 554.473 1003.34 554.95 1003.27 555.426 1003.21 C 650.32 996.904 732.751 1063.54 744.337 1157.43 C 754.813 1242.32 713.914 1289.59 666.38 1352.15 C 644.2 1381.34 623.392 1410.52 601.02 1438.85 L 645.347 1438.76 C 674.797 1438.73 699.893 1437.52 723.681 1458.78 C 736.341 1470.52 746.58 1492.41 746.39 1509.82 C 745.853 1559.09 708.721 1585.16 662.523 1584.88 C 605.477 1584.67 548.391 1584.89 491.345 1584.88 C 453.713 1584.88 430.944 1587.92 402.569 1560.56 C 384.971 1540.22 378.757 1515.9 386.33 1490.02 C 391.281 1473.11 421.732 1435.25 433.608 1419.41 L 546.586 1268.79 C 556.092 1256.03 565.676 1243.32 575.337 1230.67 C 582.178 1221.69 589.301 1212.82 595.088 1203.11 C 603.613 1188.81 603.623 1167.7 590.077 1156.37 C 581.904 1149.5 571.221 1146.39 560.639 1147.79 C 553.35 1148.71 546.497 1151.76 540.947 1156.58 C 518.335 1176.34 537.593 1208.93 513.039 1241.21 C 500.982 1256.91 483.154 1267.14 463.516 1269.64 C 447.323 1271.74 423.703 1265.06 411.149 1254.87 C 382.412 1231.53 380.77 1202.02 384.428 1167.93 C 388.528 1129.74 399.9 1098.23 424.596 1068.31 C 450.117 1037.39 485.208 1015.85 524.33 1007.09 C 533.214 1005.19 544.8 1004.29 553.996 1003.4 z M 894.673 1220.43 C 941.367 1218.56 998.019 1220.15 1045.45 1220.15 L 1335.58 1220.13 L 1526.25 1220.12 C 1558.96 1220.12 1593.22 1219.5 1625.88 1220.99 C 1638.56 1221.57 1652.47 1227.23 1662.74 1234.81 C 1678.53 1246.28 1689.1 1263.58 1692.09 1282.87 C 1696.92 1315.85 1677.52 1349.45 1646 1360.65 C 1637.43 1363.69 1630.82 1364.69 1621.77 1365.93 C 1567.41 1366.79 1510.94 1366.04 1456.44 1366.05 L 1138.65 1366.06 L 974.705 1366.06 C 946.014 1366.06 908.312 1367.98 880.655 1364.39 C 826.572 1357.38 800.099 1287.93 837.36 1246.45 C 854.613 1227.24 869.255 1222.6 894.673 1220.43 z M 902.461 856.439 C 954.165 855.054 1011.22 856.232 1063.24 856.236 L 1368.73 856.231 L 1535.09 856.234 C 1565.46 856.235 1597.82 855.308 1627.96 857.677 C 1643.32 858.885 1656.98 865.064 1668.6 875.343 C 1683.01 888.35 1691.7 906.525 1692.78 925.91 C 1694.47 957.318 1672.91 987.989 1643.2 997.679 C 1632.03 1001.32 1622.39 1001.59 1610.81 1002.14 L 1117.88 1002.16 L 968.64 1002.18 C 940.67 1002.19 912.534 1003.11 884.764 1000.82 C 825.995 995.979 798.68 922.748 839.35 880.147 C 858.066 860.541 876.602 857.589 902.461 856.439 z M 900.902 492.493 L 1395.88 492.462 L 1546.9 492.436 C 1568.05 492.418 1610.72 491.334 1630.18 493.836 C 1644.9 495.862 1658.66 502.34 1669.61 512.402 C 1698.83 539.523 1700.85 586.21 1672.81 614.732 C 1654.38 633.478 1637.61 637.432 1611.95 638.343 L 1114.19 638.353 L 966.494 638.354 C 939.628 638.359 910.415 639.758 884.177 636.996 C 825.364 630.803 798.789 558.303 840.046 515.479 C 858.122 496.717 876.075 493.323 900.902 492.493 z M 591.022 275.437 C 593.835 275.232 596.651 275.06 599.469 274.92 C 618.153 274.035 636.884 281.338 650.467 293.889 C 667.55 309.674 673.795 330.328 674.068 353.104 C 674.449 384.891 674.182 416.998 674.153 448.837 L 674.196 627.677 L 674.223 730.092 C 674.227 751.146 675.16 771.812 672.641 792.909 C 665.587 851.993 593.403 876.525 550.774 835.781 C 531.498 817.357 528.682 795.356 528.255 769.984 L 528.19 465.285 C 510.428 473.801 487.646 486.814 469.092 490.669 C 450.689 494.332 431.585 490.503 416.014 480.031 C 399.747 469.299 388.404 452.549 384.475 433.461 C 380.849 414.358 385.026 394.599 396.073 378.598 C 400.647 371.896 409.617 362.842 416.751 358.947 C 466.72 331.665 518.634 307.103 569.648 281.738 C 575.873 278.643 584.254 276.745 591.022 275.437 z" fill="#FFFFFF"></path>
|
||||
</g>
|
||||
</svg>
|
||||
|
After Width: | Height: | Size: 3.7 KiB |
@@ -0,0 +1,6 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<svg width="24px" height="24px" viewBox="-142 -133 2312 2312" xmlns="http://www.w3.org/2000/svg">
|
||||
<g stroke="none" fill="none" fill-rule="evenodd">
|
||||
<path d="M 415.959 374.584 C 419.668 374.165 422.098 373.545 425.65 374.665 C 428.16 381.32 431.704 402.806 433.553 411.417 C 439.85 440.749 445.306 470.532 451.937 499.802 C 447.174 500.998 441.581 501.836 436.717 502.51 C 379.678 510.527 320.189 531.138 284.336 578.795 C 255.713 616.842 252.901 655.858 253.336 701.311 C 294.789 701.297 337.53 700.753 378.883 701.602 C 380.342 807.337 378.949 916.433 378.95 1022.44 C 324.801 1023.23 268.801 1022.5 214.5 1022.51 L 51.759 1022.43 C 50.6494 965.326 51.7957 905.627 51.6441 848.114 C 52.135 737.016 44.3687 620.888 103.557 521.505 C 172.839 405.175 291.506 381.135 415.959 374.584 z M 929.338 374.58 C 932.895 374.216 936.041 373.421 939.181 374.828 C 941.18 379.93 944.45 399.951 945.994 406.593 C 953.109 437.198 958.102 469.756 965.431 500.14 C 882.808 512.473 800.712 537.307 773.724 627.069 C 765.628 653.998 766.579 673.655 766.816 701.32 C 808.211 701.303 850.814 700.773 892.115 701.595 C 893.735 807.063 892.197 916.642 892.192 1022.44 C 837.418 1023.3 780.454 1022.5 725.5 1022.51 L 565.035 1022.43 L 564.953 846.398 C 564.944 722.22 557.581 585.24 644.474 482.834 C 698.475 419.192 777.964 390.99 858.38 379.981 C 883.196 376.584 904.602 376.156 929.338 374.58 z M 1648.43 1022.49 L 1971.14 1022.42 L 1971.21 1201.43 C 1971.22 1312.01 1977.93 1421.73 1920.87 1521.27 C 1881.88 1589.28 1820.54 1629.9 1746.25 1650.93 C 1714.06 1659.81 1681.09 1665.58 1647.8 1668.17 C 1631.02 1669.56 1614.55 1669.6 1597.67 1671.32 C 1593.93 1656.74 1590.79 1639.39 1587.72 1624.5 L 1571.11 1544.77 C 1654.16 1534.83 1738.19 1504.8 1763.25 1417.09 C 1770.53 1391.62 1770.62 1370.37 1769.58 1344.39 C 1728.73 1343.61 1685.09 1344.27 1644.04 1344.26 C 1644.03 1314.99 1642.44 1027.97 1645.01 1023.65 C 1645.75 1022.42 1647.14 1022.68 1648.43 1022.49 z M 1135.12 1022.5 C 1241.94 1021.34 1350.92 1022.47 1457.92 1022.47 L 1457.97 1201.52 C 1457.97 1308.67 1463.9 1417.33 1411.07 1514.73 C 1372.02 1586.72 1312.65 1626.79 1235.76 1650.05 C 1175.39 1667.39 1143.75 1666.69 1083.46 1671.22 C 1082.32 1659.64 1076.38 1633.06 1073.84 1620.91 C 1068.75 1596.64 1064.2 1568.89 1058.26 1545.08 C 1070.92 1542.25 1085.18 1541.07 1098.06 1538.47 C 1157.11 1526.55 1213.88 1498.47 1241.17 1441.93 C 1257.17 1408.77 1258.1 1380.4 1256.87 1344.39 C 1215.83 1343.6 1172 1344.27 1130.77 1344.26 L 1130.72 1140.75 C 1130.72 1128.25 1129.67 1027.06 1132.15 1023.03 L 1135.12 1022.5 z" fill="#FFFFFF"></path>
|
||||
</g>
|
||||
</svg>
|
||||
|
After Width: | Height: | Size: 2.6 KiB |
@@ -0,0 +1,6 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<svg width="24px" height="24px" viewBox="86 99 1870 1870" xmlns="http://www.w3.org/2000/svg">
|
||||
<g stroke="none" fill="none" fill-rule="evenodd">
|
||||
<path d="M 509.56 256.583 L 510.367 256.513 C 531.625 254.813 562.258 255.614 584.232 255.621 L 707.271 255.646 L 1099.73 255.648 L 1394.75 255.627 L 1480.79 255.603 C 1508.79 255.608 1537.68 255.247 1565.36 259.735 C 1615.89 268.115 1663.15 290.186 1702.02 323.548 C 1758.6 372.099 1793.53 441.175 1799.1 515.525 C 1800.9 541.862 1800.15 572.071 1800.14 598.808 L 1800.1 731.339 L 1800.11 1145.69 L 1800.13 1421.92 L 1800.14 1497.81 C 1800.15 1524.41 1800.63 1551.86 1796.32 1578.14 C 1787.88 1629.33 1765.4 1677.18 1731.38 1716.36 C 1681.67 1773.69 1610.84 1808.44 1535.08 1812.66 C 1509.05 1813.78 1479.16 1813.1 1452.81 1813.1 L 1307.95 1813.1 L 867.692 1813.09 L 622.784 1813.1 C 576.818 1813.1 519.365 1816.17 474.995 1808.8 C 346.731 1787.51 246.354 1670.79 244.342 1540.71 C 243.908 1512.7 243.923 1484.49 243.938 1456.29 L 243.945 1313.38 L 243.949 873.185 L 243.923 634.051 C 243.919 602.679 244.624 570.722 244.048 539.419 C 241.359 393.078 360.384 262.073 509.56 256.583 z M 510.361 386.373 C 514.381 385.717 522.772 385.616 527.089 385.583 L 1231 385.662 L 1435.78 385.609 C 1462.16 385.602 1488.71 385.835 1515.08 385.644 C 1558.09 385.332 1595.57 399.671 1626.42 430.309 C 1645.56 449.263 1659.16 473.086 1665.76 499.205 C 1672.73 526.51 1670.76 573.206 1670.75 602.73 L 1670.74 731.939 L 1535.5 731.916 L 373.562 731.87 C 372.326 683.952 374.085 635.084 373.397 587.036 C 373.044 562.445 371.988 529.162 376.846 505.636 C 381.746 482.817 391.787 461.416 406.205 443.063 C 432.871 409.734 468.203 391.073 510.361 386.373 z M 849.041 1337.2 L 1195.16 1337.21 L 1195.04 1683.65 L 848.948 1683.64 L 849.041 1337.2 z M 849.068 861.378 L 1195.16 861.392 L 1195.07 1207.81 L 848.948 1207.8 L 849.068 861.378 z M 373.439 861.386 L 719.239 861.382 L 719.137 1207.81 L 373.538 1207.72 C 372.751 1169.46 373.446 1128.4 373.44 1089.94 L 373.439 861.386 z M 1325.02 861.381 C 1439.03 861.3 1556.92 859.825 1670.62 861.589 C 1671.48 899.466 1670.7 940.494 1670.7 978.574 L 1670.74 1207.82 L 1324.89 1207.8 L 1325.02 861.381 z M 1324.99 1337.2 L 1670.61 1337.32 L 1670.66 1339.65 C 1671.21 1379.62 1670.88 1419.97 1670.74 1459.95 C 1670.49 1530.77 1679.49 1583.87 1625.84 1639.36 C 1625.22 1640 1624.6 1640.63 1623.97 1641.26 C 1574.86 1689.86 1521.42 1683.67 1458.69 1683.67 L 1324.89 1683.63 L 1324.99 1337.2 z M 373.437 1337.21 L 719.24 1337.21 L 719.115 1683.65 L 590.631 1683.68 C 561.099 1683.68 513.065 1685.65 486.26 1678.59 C 460.191 1671.62 436.448 1657.83 417.467 1638.65 C 398.45 1619.68 384.929 1595.92 378.342 1569.88 C 371.325 1542.3 373.437 1494.9 373.438 1464.76 L 373.437 1337.21 z" fill="#FFFFFF"></path>
|
||||
</g>
|
||||
</svg>
|
||||
|
After Width: | Height: | Size: 2.8 KiB |
@@ -0,0 +1,6 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<svg width="24px" height="24px" viewBox="83 113 1881 1881" xmlns="http://www.w3.org/2000/svg">
|
||||
<g stroke="none" fill="none" fill-rule="evenodd">
|
||||
<path d="M 321.782 413.362 C 336.764 411.702 374.743 412.925 391.629 412.944 L 533.186 412.959 L 692.688 412.944 C 718.676 412.932 746.235 412.191 772.086 413.516 C 786.83 415.13 800.873 419.533 813.034 428.045 C 842.32 448.541 852.483 476.577 850.978 511.417 C 850.316 526.721 850.998 544.275 851.004 559.595 L 851.033 701.399 L 851.009 862.073 C 851.001 887.912 851.362 914.184 850.555 940.017 C 849.2 983.407 816.83 1015.18 774.251 1021.91 C 740.693 1023.12 703.226 1022.34 669.45 1022.34 L 484.298 1022.34 L 375.851 1022.38 C 332.465 1022.39 291.649 1028.29 261.322 990.229 C 252.951 979.725 244.708 964.358 243.498 950.805 C 241.085 923.777 242.017 895.923 242.038 868.755 L 242.079 721.589 L 242.036 567.913 C 242.034 542.331 240.533 512.933 243.105 487.691 C 247.418 445.37 282.241 418.551 321.782 413.362 z M 375.746 543.446 L 599.662 543.43 C 637.196 543.413 684.177 542.072 720.561 543.836 L 720.349 889.25 L 719.677 891.192 C 715.346 892.581 667.832 891.84 660.275 891.839 L 503.719 891.874 L 418.253 891.921 C 405.907 891.925 384.4 892.504 372.736 891.424 C 371.418 781.546 373.78 671.324 372.414 561.414 C 372.351 556.326 372.035 549.01 373.434 544.314 L 375.746 543.446 z M 777.186 1262.24 C 780.205 1262 783.229 1261.85 786.256 1261.79 C 821.038 1261.21 851.722 1291.9 850.932 1326.6 C 850.407 1349.65 843.346 1361.98 827.342 1378.01 C 757.889 1447.55 688.326 1516.96 618.836 1586.45 L 555.814 1649.6 C 534.236 1671.22 514.746 1697.18 481.781 1696.82 C 460.942 1696.59 447.268 1689.83 432.487 1675.3 C 420.364 1663.38 408.484 1651.46 396.542 1639.51 L 311.396 1554.28 L 278.655 1521.72 C 259.008 1502.17 240.482 1487.01 240.163 1456.55 C 240.085 1439.29 246.798 1422.7 258.852 1410.35 C 271.123 1397.89 287.852 1390.83 305.339 1390.73 C 327.427 1390.75 342.393 1400.29 357.345 1415.6 C 368.266 1426.78 379.666 1437.89 390.732 1448.97 L 481.16 1539.27 C 502.729 1518.91 525.406 1495.22 546.654 1473.98 L 670.317 1350.35 L 716.205 1304.37 C 737.789 1282.69 746.442 1269.26 777.186 1262.24 z M 1089.29 1501.42 C 1127.71 1499.66 1177.52 1501.21 1217.07 1501.2 L 1468.98 1501.19 L 1652.83 1501.19 C 1677.27 1501.19 1701.72 1501.09 1726.17 1501.2 C 1746.95 1501.3 1764.34 1500.77 1781.81 1513.4 C 1795.82 1523.41 1805.19 1538.66 1807.8 1555.68 C 1813.85 1597.65 1787.53 1625.81 1747.41 1631.78 C 1699.31 1632.74 1648.17 1631.91 1599.87 1631.91 L 1316.46 1631.94 L 1161.49 1631.97 C 1143.37 1631.97 1125.09 1631.8 1106.99 1631.96 C 1085.65 1632.14 1067.8 1631.58 1050.23 1617.71 C 1036.44 1606.8 1027.64 1590.79 1025.81 1573.31 C 1024.01 1555.89 1029.23 1538.46 1040.31 1524.9 C 1053.66 1508.71 1069.18 1503.28 1089.29 1501.42 z M 1082.79 979.274 C 1091.05 978.184 1106.84 978.513 1115.53 978.526 L 1167.75 978.619 L 1346.89 978.601 L 1615.2 978.575 L 1700.38 978.572 C 1727.43 978.567 1759.03 974.587 1781.93 991.18 C 1795.77 1001.07 1805.05 1016.09 1807.7 1032.89 C 1813.63 1071.75 1789.87 1102.89 1751.23 1108.67 C 1739.63 1109.69 1723.83 1109.29 1712.01 1109.27 L 1649.03 1109.21 L 1448.24 1109.23 L 1208.07 1109.24 L 1128.96 1109.25 C 1115.95 1109.24 1098.47 1109.68 1085.79 1108.89 C 1075.38 1108.29 1065.29 1105.14 1056.39 1099.7 C 1041.5 1090.44 1030.92 1075.61 1027.01 1058.52 C 1023.13 1041.18 1026.45 1023.01 1036.22 1008.18 C 1047.79 990.638 1062.94 983.38 1082.79 979.274 z M 1079.78 457.255 C 1092.16 456.115 1108.19 456.506 1120.86 456.526 L 1183.5 456.56 L 1388.89 456.548 L 1629.45 456.529 L 1705.83 456.538 C 1730.98 456.545 1755.43 453.131 1777.64 466.091 C 1808.55 484.126 1817.98 527.43 1798.81 556.857 C 1789.1 571.942 1773.67 582.432 1756.07 585.916 C 1741.64 588.192 1693.37 586.991 1675.6 586.977 L 1512.7 586.988 L 1227.61 587.009 L 1134.91 587.006 C 1110.34 587.004 1078.47 590.622 1057.18 577.809 C 1042.16 568.758 1031.37 554.1 1027.18 537.074 C 1023.12 520.246 1025.97 502.492 1035.08 487.777 C 1045.62 470.53 1060.63 461.87 1079.78 457.255 z" fill="#FFFFFF"></path>
|
||||
</g>
|
||||
</svg>
|
||||
|
After Width: | Height: | Size: 4.0 KiB |
@@ -7166,8 +7166,24 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
|
||||
"lng_polls_stop_sure" = "Stop";
|
||||
"lng_article_menu_item" = "Article";
|
||||
"lng_article_editor_title" = "Article";
|
||||
"lng_article_insert_heading" = "Heading";
|
||||
"lng_article_insert_heading1" = "Heading 1";
|
||||
"lng_article_insert_heading2" = "Heading 2";
|
||||
"lng_article_insert_heading3" = "Heading 3";
|
||||
"lng_article_insert_heading4" = "Heading 4";
|
||||
"lng_article_insert_heading5" = "Heading 5";
|
||||
"lng_article_insert_heading6" = "Heading 6";
|
||||
"lng_article_insert_blockquote" = "Blockquote";
|
||||
"lng_article_insert_math" = "Math";
|
||||
"lng_article_insert_divider" = "Divider";
|
||||
"lng_article_insert_ordered_list" = "Ordered List";
|
||||
"lng_article_insert_bullet_list" = "Bullet List";
|
||||
"lng_article_insert_task_list" = "Task List";
|
||||
"lng_article_insert_pullquote" = "Pullquote";
|
||||
"lng_article_insert_media" = "Attach Media";
|
||||
"lng_article_insert_details" = "Details";
|
||||
"lng_article_insert_table" = "Table";
|
||||
"lng_article_insert_map" = "Map";
|
||||
"lng_polls_menu_item" = "Poll";
|
||||
"lng_polls_create" = "Create poll";
|
||||
"lng_polls_create_title" = "New poll";
|
||||
|
||||
@@ -79,11 +79,30 @@ ivWrongLayoutLink: LinkButton(defaultLinkButton) {
|
||||
overColor: windowSubTextFg;
|
||||
padding: margins(5px, 5px, 5px, 5px);
|
||||
}
|
||||
ivEditorBodyPadding: margins(0px, 0px, 0px, 0px);
|
||||
ivEditorToolbarPadding: margins(12px, 24px, 12px, 24px);
|
||||
ivEditorToolbarButtonsSkip: 8px;
|
||||
ivEditorToolbarButtonMinWidth: 132px;
|
||||
ivEditorToolbarButtonHeight: 42px;
|
||||
ivEditorBodyPadding: margins(0px, 8px, 0px, 8px);
|
||||
ivEditorToolbarPadding: margins(8px, defaultVerticalListSkip, 8px, defaultVerticalListSkip);
|
||||
ivEditorToolbarButtonSize: 36px;
|
||||
ivEditorToolbarButton: IconButton(defaultIconButton) {
|
||||
width: ivEditorToolbarButtonSize;
|
||||
height: ivEditorToolbarButtonSize;
|
||||
iconPosition: point(-1px, -1px);
|
||||
rippleAreaPosition: point(0px, 0px);
|
||||
rippleAreaSize: ivEditorToolbarButtonSize;
|
||||
ripple: defaultRippleAnimationBgOver;
|
||||
}
|
||||
ivEditorToolbarButtonSkip: 6px;
|
||||
ivEditorToolbarRowSkip: 0px;
|
||||
ivEditorToolbarHeadingIcon: icon {{ "menu/rich/heading-24x24", menuIconColor }};
|
||||
ivEditorToolbarBlockquoteIcon: icon {{ "menu/rich/blockquote-24x24", menuIconColor }};
|
||||
ivEditorToolbarMathIcon: icon {{ "menu/rich/math-24x24", menuIconColor }};
|
||||
ivEditorToolbarDividerIcon: icon {{ "menu/rich/divider-24x24", menuIconColor }};
|
||||
ivEditorToolbarOrderedListIcon: icon {{ "menu/rich/ordered_list-24x24", menuIconColor }};
|
||||
ivEditorToolbarBulletListIcon: icon {{ "menu/rich/bullet_list-24x24", menuIconColor }};
|
||||
ivEditorToolbarTaskListIcon: icon {{ "menu/rich/task_list-24x24", menuIconColor }};
|
||||
ivEditorToolbarPullquoteIcon: icon {{ "menu/rich/pullquote-24x24", menuIconColor }};
|
||||
ivEditorToolbarAttachIcon: icon {{ "chat/input_attach", menuIconColor }};
|
||||
ivEditorToolbarDetailsIcon: icon {{ "menu/rich/details-24x24", menuIconColor }};
|
||||
ivEditorToolbarTableIcon: icon {{ "menu/rich/table-24x24", menuIconColor }};
|
||||
ivEditorInlineFieldMargins: margins(4px, 0px, 6px, 0px);
|
||||
ivEditorMinHeight: boxWidth;
|
||||
ivEditorInputField: InputField(defaultInputField) {
|
||||
|
||||
@@ -7,32 +7,412 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
|
||||
*/
|
||||
#include "iv/iv_editor_box.h"
|
||||
|
||||
#include "data/data_msg_id.h"
|
||||
#include <QtCore/QDir>
|
||||
#include <QtCore/QEvent>
|
||||
#include <QtCore/QFileInfo>
|
||||
#include <QtCore/QPointer>
|
||||
#include <QtNetwork/QNetworkProxy>
|
||||
|
||||
#include "base/flat_map.h"
|
||||
#include "base/const_string.h"
|
||||
#include "base/unique_qptr.h"
|
||||
#include "base/weak_ptr.h"
|
||||
#include "core/file_utilities.h"
|
||||
#include "core/mime_type.h"
|
||||
#include "core/shortcuts.h"
|
||||
#include "ui/image/image_location.h"
|
||||
#include "data/data_location.h"
|
||||
#include "data/data_types.h"
|
||||
#include "iv/iv_editor_state.h"
|
||||
#include "iv/iv_editor_widget.h"
|
||||
#include "lang/lang_keys.h"
|
||||
#include "main/main_app_config.h"
|
||||
#include "main/main_session.h"
|
||||
#include "settings.h"
|
||||
#include "ui/emoji_config.h"
|
||||
#include "storage/storage_account.h"
|
||||
#include "ui/controls/location_picker.h"
|
||||
#include "ui/layers/generic_box.h"
|
||||
#include "ui/rect_part.h"
|
||||
#include "ui/ui_utility.h"
|
||||
#include "ui/widgets/buttons.h"
|
||||
#include "ui/wrap/vertical_layout.h"
|
||||
#include "styles/style_layers.h"
|
||||
#include "styles/style_settings.h"
|
||||
|
||||
#include <QtCore/QDate>
|
||||
#include "ui/widgets/popup_menu.h"
|
||||
#include "ui/widgets/tooltip.h"
|
||||
|
||||
#include "window/window_session_controller.h"
|
||||
#include "mainwindow.h"
|
||||
#include "mainwidget.h"
|
||||
|
||||
#include <array>
|
||||
#include <memory>
|
||||
#include <optional>
|
||||
#include <vector>
|
||||
|
||||
#include "styles/style_iv.h"
|
||||
#include "styles/style_layers.h"
|
||||
#include "styles/style_menu_icons.h"
|
||||
#include "styles/style_widgets.h"
|
||||
|
||||
namespace Iv::Editor {
|
||||
namespace {
|
||||
|
||||
struct ToolbarButton {
|
||||
object_ptr<Ui::IconButton> widget;
|
||||
Fn<rpl::producer<TextWithEntities>()> tooltipFactory;
|
||||
};
|
||||
|
||||
class Toolbar final : public Ui::RpWidget {
|
||||
public:
|
||||
Toolbar(
|
||||
QWidget *parent,
|
||||
not_null<Window::SessionController*> controller,
|
||||
not_null<PeerData*> peer,
|
||||
not_null<Widget*> editor,
|
||||
QPointer<QWidget> tooltipParent);
|
||||
|
||||
int resizeGetHeight(int width) override;
|
||||
|
||||
protected:
|
||||
bool eventFilter(QObject *object, QEvent *event) override;
|
||||
|
||||
private:
|
||||
not_null<Ui::IconButton*> addButton(
|
||||
QString label,
|
||||
Fn<rpl::producer<TextWithEntities>()> tooltipFactory,
|
||||
const style::icon *icon,
|
||||
Fn<void()> callback);
|
||||
void addInsertButtons();
|
||||
void showHeadingMenu(not_null<Ui::IconButton*> button);
|
||||
void chooseMedia();
|
||||
void applyMediaResult(FileDialog::OpenResult &&result);
|
||||
void chooseMap();
|
||||
void showTooltip(not_null<Ui::IconButton*> button);
|
||||
void hideTooltip();
|
||||
void updateTooltipGeometry();
|
||||
[[nodiscard]] ToolbarButton *buttonData(not_null<Ui::IconButton*> button);
|
||||
|
||||
const not_null<Window::SessionController*> _controller;
|
||||
const not_null<PeerData*> _peer;
|
||||
const QPointer<Widget> _editor;
|
||||
const QPointer<QWidget> _tooltipParent;
|
||||
const Ui::LocationPickerConfig _mapsConfig;
|
||||
std::vector<ToolbarButton> _buttons;
|
||||
base::unique_qptr<Ui::ImportantTooltip> _tooltip;
|
||||
base::unique_qptr<Ui::PopupMenu> _menu;
|
||||
Ui::IconButton *_hovered = nullptr;
|
||||
|
||||
};
|
||||
|
||||
[[nodiscard]] Ui::LocationPickerConfig ResolveMapsConfig(
|
||||
not_null<Main::Session*> session) {
|
||||
const auto &appConfig = session->appConfig();
|
||||
auto map = appConfig.get<base::flat_map<QString, QString>>(
|
||||
u"tdesktop_config_map"_q,
|
||||
base::flat_map<QString, QString>());
|
||||
return {
|
||||
.mapsToken = map[u"maps"_q],
|
||||
.geoToken = map[u"geo"_q],
|
||||
};
|
||||
}
|
||||
|
||||
[[nodiscard]] QString HeadingLabel(int level) {
|
||||
switch (level) {
|
||||
case 1: return tr::lng_article_insert_heading1(tr::now);
|
||||
case 2: return tr::lng_article_insert_heading2(tr::now);
|
||||
case 3: return tr::lng_article_insert_heading3(tr::now);
|
||||
case 4: return tr::lng_article_insert_heading4(tr::now);
|
||||
case 5: return tr::lng_article_insert_heading5(tr::now);
|
||||
case 6: return tr::lng_article_insert_heading6(tr::now);
|
||||
}
|
||||
return tr::lng_article_insert_heading1(tr::now);
|
||||
}
|
||||
|
||||
[[nodiscard]] std::optional<State::InsertBlockType> MediaTypeForPath(
|
||||
const QString &path) {
|
||||
const auto mime = Core::MimeTypeForFile(QFileInfo(path)).name();
|
||||
if (Core::FileIsImage(path, mime)) {
|
||||
return State::InsertBlockType::Photo;
|
||||
} else if (mime.startsWith(u"video/"_q)) {
|
||||
return State::InsertBlockType::Video;
|
||||
} else if (mime.startsWith(u"audio/"_q)) {
|
||||
return State::InsertBlockType::Audio;
|
||||
}
|
||||
return std::nullopt;
|
||||
}
|
||||
|
||||
Toolbar::Toolbar(
|
||||
QWidget *parent,
|
||||
not_null<Window::SessionController*> controller,
|
||||
not_null<PeerData*> peer,
|
||||
not_null<Widget*> editor,
|
||||
QPointer<QWidget> tooltipParent)
|
||||
: Ui::RpWidget(parent)
|
||||
, _controller(controller)
|
||||
, _peer(peer)
|
||||
, _editor(editor.get())
|
||||
, _tooltipParent(std::move(tooltipParent))
|
||||
, _mapsConfig(ResolveMapsConfig(&controller->session())) {
|
||||
setMouseTracking(true);
|
||||
addInsertButtons();
|
||||
}
|
||||
|
||||
not_null<Ui::IconButton*> Toolbar::addButton(
|
||||
QString label,
|
||||
Fn<rpl::producer<TextWithEntities>()> tooltipFactory,
|
||||
const style::icon *icon,
|
||||
Fn<void()> callback) {
|
||||
auto button = object_ptr<Ui::IconButton>(this, st::ivEditorToolbarButton);
|
||||
const auto raw = button.data();
|
||||
raw->setAccessibleName(label);
|
||||
raw->setIconOverride(icon, icon);
|
||||
raw->setClickedCallback([=, callback = std::move(callback)] {
|
||||
hideTooltip();
|
||||
callback();
|
||||
});
|
||||
raw->installEventFilter(this);
|
||||
_buttons.push_back({
|
||||
std::move(button),
|
||||
std::move(tooltipFactory),
|
||||
});
|
||||
return raw;
|
||||
}
|
||||
|
||||
void Toolbar::addInsertButtons() {
|
||||
const auto insert = [=](State::InsertAction action) {
|
||||
if (_editor) {
|
||||
_editor->insertBlock(action);
|
||||
}
|
||||
};
|
||||
const auto insertType = [=](State::InsertBlockType type) {
|
||||
insert({ .type = type });
|
||||
};
|
||||
|
||||
const auto heading = addButton(
|
||||
tr::lng_article_insert_heading(tr::now),
|
||||
[] { return tr::lng_article_insert_heading(tr::marked); },
|
||||
&st::ivEditorToolbarHeadingIcon,
|
||||
[] {});
|
||||
heading->setClickedCallback([=] {
|
||||
hideTooltip();
|
||||
showHeadingMenu(heading);
|
||||
});
|
||||
addButton(
|
||||
tr::lng_article_insert_blockquote(tr::now),
|
||||
[] { return tr::lng_article_insert_blockquote(tr::marked); },
|
||||
&st::ivEditorToolbarBlockquoteIcon,
|
||||
[=] { insertType(State::InsertBlockType::Blockquote); });
|
||||
addButton(
|
||||
tr::lng_article_insert_math(tr::now),
|
||||
[] { return tr::lng_article_insert_math(tr::marked); },
|
||||
&st::ivEditorToolbarMathIcon,
|
||||
[=] { insertType(State::InsertBlockType::Math); });
|
||||
addButton(
|
||||
tr::lng_article_insert_divider(tr::now),
|
||||
[] { return tr::lng_article_insert_divider(tr::marked); },
|
||||
&st::ivEditorToolbarDividerIcon,
|
||||
[=] { insertType(State::InsertBlockType::Divider); });
|
||||
addButton(
|
||||
tr::lng_article_insert_ordered_list(tr::now),
|
||||
[] { return tr::lng_article_insert_ordered_list(tr::marked); },
|
||||
&st::ivEditorToolbarOrderedListIcon,
|
||||
[=] { insertType(State::InsertBlockType::OrderedList); });
|
||||
addButton(
|
||||
tr::lng_article_insert_bullet_list(tr::now),
|
||||
[] { return tr::lng_article_insert_bullet_list(tr::marked); },
|
||||
&st::ivEditorToolbarBulletListIcon,
|
||||
[=] { insertType(State::InsertBlockType::BulletList); });
|
||||
addButton(
|
||||
tr::lng_article_insert_task_list(tr::now),
|
||||
[] { return tr::lng_article_insert_task_list(tr::marked); },
|
||||
&st::ivEditorToolbarTaskListIcon,
|
||||
[=] { insertType(State::InsertBlockType::TaskList); });
|
||||
addButton(
|
||||
tr::lng_article_insert_pullquote(tr::now),
|
||||
[] { return tr::lng_article_insert_pullquote(tr::marked); },
|
||||
&st::ivEditorToolbarPullquoteIcon,
|
||||
[=] { insertType(State::InsertBlockType::Pullquote); });
|
||||
addButton(
|
||||
tr::lng_article_insert_media(tr::now),
|
||||
[] { return tr::lng_article_insert_media(tr::marked); },
|
||||
&st::ivEditorToolbarAttachIcon,
|
||||
[=] { chooseMedia(); });
|
||||
addButton(
|
||||
tr::lng_article_insert_details(tr::now),
|
||||
[] { return tr::lng_article_insert_details(tr::marked); },
|
||||
&st::ivEditorToolbarDetailsIcon,
|
||||
[=] { insertType(State::InsertBlockType::Details); });
|
||||
addButton(
|
||||
tr::lng_article_insert_table(tr::now),
|
||||
[] { return tr::lng_article_insert_table(tr::marked); },
|
||||
&st::ivEditorToolbarTableIcon,
|
||||
[=] { insertType(State::InsertBlockType::Table); });
|
||||
|
||||
if (Ui::LocationPicker::Available(_mapsConfig)) {
|
||||
addButton(
|
||||
tr::lng_article_insert_map(tr::now),
|
||||
[] { return tr::lng_article_insert_map(tr::marked); },
|
||||
&st::menuIconAddress,
|
||||
[=] { chooseMap(); });
|
||||
}
|
||||
}
|
||||
|
||||
void Toolbar::showHeadingMenu(not_null<Ui::IconButton*> button) {
|
||||
_menu = base::make_unique_q<Ui::PopupMenu>(this);
|
||||
for (const auto level : std::array{ 1, 2, 3, 4, 5, 6 }) {
|
||||
_menu->addAction(
|
||||
HeadingLabel(level),
|
||||
[=] {
|
||||
if (_editor) {
|
||||
_editor->insertBlock({
|
||||
.type = State::InsertBlockType::Heading,
|
||||
.headingLevel = level,
|
||||
});
|
||||
}
|
||||
});
|
||||
}
|
||||
_menu->popup(button->mapToGlobal(QPoint(0, button->height())));
|
||||
}
|
||||
|
||||
void Toolbar::chooseMedia() {
|
||||
const auto weak = QPointer<Toolbar>(this);
|
||||
FileDialog::GetOpenPath(
|
||||
QPointer<QWidget>(this),
|
||||
tr::lng_article_insert_media(tr::now),
|
||||
FileDialog::AllFilesFilter(),
|
||||
[=](FileDialog::OpenResult &&result) {
|
||||
if (weak) {
|
||||
weak->applyMediaResult(std::move(result));
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
void Toolbar::applyMediaResult(FileDialog::OpenResult &&result) {
|
||||
if (result.paths.isEmpty()) {
|
||||
return;
|
||||
}
|
||||
const auto type = MediaTypeForPath(result.paths.front());
|
||||
if (!type) {
|
||||
_controller->showToast(tr::lng_edit_media_invalid_file(tr::now));
|
||||
return;
|
||||
}
|
||||
if (_editor) {
|
||||
_editor->insertMedia(*type);
|
||||
}
|
||||
}
|
||||
|
||||
void Toolbar::chooseMap() {
|
||||
const auto weak = QPointer<Toolbar>(this);
|
||||
const auto session = &_controller->session();
|
||||
Ui::LocationPicker::Show({
|
||||
.parent = _controller->widget().get(),
|
||||
.config = _mapsConfig,
|
||||
.chooseLabel = tr::lng_maps_point_send(),
|
||||
.recipient = _peer,
|
||||
.session = session,
|
||||
.callback = [=](Data::InputVenue venue) {
|
||||
if (weak && weak->_editor) {
|
||||
weak->_editor->insertMap(venue.lat, venue.lon);
|
||||
}
|
||||
},
|
||||
.quit = [] { Shortcuts::Launch(Shortcuts::Command::Quit); },
|
||||
.storageId = session->local().resolveStorageIdBots(),
|
||||
.closeRequests = _controller->content()->death(),
|
||||
});
|
||||
}
|
||||
|
||||
int Toolbar::resizeGetHeight(int width) {
|
||||
const auto padding = st::ivEditorToolbarPadding;
|
||||
const auto buttonWidth = st::ivEditorToolbarButton.width;
|
||||
const auto buttonHeight = st::ivEditorToolbarButton.height;
|
||||
const auto right = width - padding.right();
|
||||
auto left = padding.left();
|
||||
auto top = padding.top();
|
||||
for (const auto &button : _buttons) {
|
||||
if (left > padding.left() && left + buttonWidth > right) {
|
||||
left = padding.left();
|
||||
top += buttonHeight + st::ivEditorToolbarRowSkip;
|
||||
}
|
||||
button.widget->moveToLeft(left, top, width);
|
||||
left += buttonWidth + st::ivEditorToolbarButtonSkip;
|
||||
}
|
||||
updateTooltipGeometry();
|
||||
return top + buttonHeight + padding.bottom();
|
||||
}
|
||||
|
||||
bool Toolbar::eventFilter(QObject *object, QEvent *event) {
|
||||
for (const auto &data : _buttons) {
|
||||
if (data.widget.data() != object) {
|
||||
continue;
|
||||
}
|
||||
const auto button = data.widget.data();
|
||||
if (event->type() == QEvent::Enter) {
|
||||
showTooltip(not_null<Ui::IconButton*>(button));
|
||||
} else if (event->type() == QEvent::Leave && _hovered == button) {
|
||||
hideTooltip();
|
||||
}
|
||||
break;
|
||||
}
|
||||
return Ui::RpWidget::eventFilter(object, event);
|
||||
}
|
||||
|
||||
void Toolbar::showTooltip(not_null<Ui::IconButton*> button) {
|
||||
hideTooltip();
|
||||
const auto data = buttonData(button);
|
||||
if (!data) {
|
||||
return;
|
||||
}
|
||||
_hovered = button;
|
||||
const auto tooltipParent = _tooltipParent
|
||||
? _tooltipParent.data()
|
||||
: (parentWidget() ? parentWidget() : this);
|
||||
_tooltip.reset(Ui::CreateChild<Ui::ImportantTooltip>(
|
||||
tooltipParent,
|
||||
Ui::MakeNiceTooltipLabel(
|
||||
tooltipParent,
|
||||
data->tooltipFactory(),
|
||||
st::boxWideWidth,
|
||||
st::defaultImportantTooltipLabel),
|
||||
st::defaultImportantTooltip));
|
||||
_tooltip->setAttribute(Qt::WA_TransparentForMouseEvents);
|
||||
_tooltip->toggleFast(false);
|
||||
updateTooltipGeometry();
|
||||
_tooltip->raise();
|
||||
_tooltip->toggleAnimated(true);
|
||||
}
|
||||
|
||||
void Toolbar::hideTooltip() {
|
||||
_hovered = nullptr;
|
||||
if (_tooltip) {
|
||||
_tooltip->toggleFast(false);
|
||||
_tooltip = nullptr;
|
||||
}
|
||||
}
|
||||
|
||||
void Toolbar::updateTooltipGeometry() {
|
||||
if (!_tooltip || !_hovered) {
|
||||
return;
|
||||
}
|
||||
const auto tooltipParent = _tooltip->parentWidget();
|
||||
const auto geometry = Ui::MapFrom(
|
||||
tooltipParent,
|
||||
_hovered,
|
||||
_hovered->rect());
|
||||
_tooltip->pointAt(geometry, RectPart::Top | RectPart::Center);
|
||||
}
|
||||
|
||||
ToolbarButton *Toolbar::buttonData(not_null<Ui::IconButton*> button) {
|
||||
for (auto &data : _buttons) {
|
||||
if (data.widget.data() == button.get()) {
|
||||
return &data;
|
||||
}
|
||||
}
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
void SetupBox(
|
||||
not_null<Ui::GenericBox*> box,
|
||||
not_null<Window::SessionController*> controller,
|
||||
not_null<PeerData*> peer) {
|
||||
box->setTitle(tr::lng_article_editor_title());
|
||||
box->setWidth(st::boxWideWidth);
|
||||
box->setNoContentMargin(true);
|
||||
|
||||
@@ -43,21 +423,13 @@ void SetupBox(
|
||||
peer,
|
||||
state),
|
||||
style::margins());
|
||||
const auto toolbar = box->setPinnedToTopContent(
|
||||
object_ptr<Ui::VerticalLayout>(box));
|
||||
|
||||
toolbar->add(object_ptr<Ui::SettingsButton>(
|
||||
toolbar,
|
||||
tr::lng_article_insert_heading1(),
|
||||
st::settingsButtonNoIcon))->setClickedCallback([=] {
|
||||
editor->insertHeading1();
|
||||
});
|
||||
toolbar->add(object_ptr<Ui::SettingsButton>(
|
||||
toolbar,
|
||||
tr::lng_article_insert_blockquote(),
|
||||
st::settingsButtonNoIcon))->setClickedCallback([=] {
|
||||
editor->insertBlockquote();
|
||||
});
|
||||
const auto tooltipParent = box->getDelegate()->outerContainer();
|
||||
box->setPinnedToTopContent(object_ptr<Toolbar>(
|
||||
box,
|
||||
controller,
|
||||
peer,
|
||||
editor,
|
||||
tooltipParent));
|
||||
|
||||
box->setFocusCallback([=] {
|
||||
editor->activateInitialNode();
|
||||
|
||||
@@ -11,20 +11,146 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
|
||||
#include "iv/markdown/iv_markdown_prepare.h"
|
||||
|
||||
#include <memory>
|
||||
#include <optional>
|
||||
#include <vector>
|
||||
|
||||
namespace Iv::Editor {
|
||||
|
||||
class State final {
|
||||
public:
|
||||
enum class TextSlot : uchar {
|
||||
Text,
|
||||
Caption,
|
||||
enum class FieldMode : uchar {
|
||||
Rich,
|
||||
Raw,
|
||||
};
|
||||
|
||||
enum class InsertBlockType : uchar {
|
||||
Heading,
|
||||
Blockquote,
|
||||
Math,
|
||||
Footer,
|
||||
Divider,
|
||||
Anchor,
|
||||
OrderedList,
|
||||
BulletList,
|
||||
TaskList,
|
||||
Pullquote,
|
||||
Photo,
|
||||
Video,
|
||||
Audio,
|
||||
Details,
|
||||
Table,
|
||||
Map,
|
||||
};
|
||||
|
||||
struct InsertAction {
|
||||
InsertBlockType type;
|
||||
int headingLevel = 1;
|
||||
double latitude = 0.;
|
||||
double longitude = 0.;
|
||||
};
|
||||
|
||||
enum class BlockContainerKind : uchar {
|
||||
Root,
|
||||
BlockChildren,
|
||||
ListItemChildren,
|
||||
};
|
||||
|
||||
struct BlockContainerStep {
|
||||
BlockContainerKind kind = BlockContainerKind::BlockChildren;
|
||||
int blockIndex = -1;
|
||||
int listItemIndex = -1;
|
||||
|
||||
friend inline bool operator==(
|
||||
const BlockContainerStep &a,
|
||||
const BlockContainerStep &b) {
|
||||
return (a.kind == b.kind)
|
||||
&& (a.blockIndex == b.blockIndex)
|
||||
&& (a.listItemIndex == b.listItemIndex);
|
||||
}
|
||||
};
|
||||
|
||||
struct BlockContainerPath {
|
||||
std::vector<BlockContainerStep> steps;
|
||||
|
||||
friend inline bool operator==(
|
||||
const BlockContainerPath &a,
|
||||
const BlockContainerPath &b) {
|
||||
return (a.steps == b.steps);
|
||||
}
|
||||
};
|
||||
|
||||
struct BlockPath {
|
||||
BlockContainerPath container;
|
||||
int index = -1;
|
||||
|
||||
friend inline bool operator==(
|
||||
const BlockPath &a,
|
||||
const BlockPath &b) {
|
||||
return (a.container == b.container)
|
||||
&& (a.index == b.index);
|
||||
}
|
||||
};
|
||||
|
||||
enum class LeafKind : uchar {
|
||||
BlockText,
|
||||
BlockCaption,
|
||||
ListItemText,
|
||||
TableCellText,
|
||||
MathFormula,
|
||||
};
|
||||
|
||||
struct LeafPath {
|
||||
LeafKind kind = LeafKind::BlockText;
|
||||
BlockPath block;
|
||||
int listItemIndex = -1;
|
||||
int tableRowIndex = -1;
|
||||
int tableCellIndex = -1;
|
||||
|
||||
friend inline bool operator==(
|
||||
const LeafPath &a,
|
||||
const LeafPath &b) {
|
||||
return (a.kind == b.kind)
|
||||
&& (a.block == b.block)
|
||||
&& (a.listItemIndex == b.listItemIndex)
|
||||
&& (a.tableRowIndex == b.tableRowIndex)
|
||||
&& (a.tableCellIndex == b.tableCellIndex);
|
||||
}
|
||||
};
|
||||
|
||||
struct InsertionAnchor {
|
||||
BlockContainerPath container;
|
||||
int blockIndex = -1;
|
||||
};
|
||||
|
||||
enum class RemovalKind : uchar {
|
||||
Block,
|
||||
ListItem,
|
||||
TableCell,
|
||||
};
|
||||
|
||||
struct RemovalTarget {
|
||||
RemovalKind kind = RemovalKind::Block;
|
||||
BlockPath block;
|
||||
int listItemIndex = -1;
|
||||
int tableRowIndex = -1;
|
||||
int tableCellIndex = -1;
|
||||
|
||||
friend inline bool operator==(
|
||||
const RemovalTarget &a,
|
||||
const RemovalTarget &b) {
|
||||
return (a.kind == b.kind)
|
||||
&& (a.block == b.block)
|
||||
&& (a.listItemIndex == b.listItemIndex)
|
||||
&& (a.tableRowIndex == b.tableRowIndex)
|
||||
&& (a.tableCellIndex == b.tableCellIndex);
|
||||
}
|
||||
};
|
||||
|
||||
struct TextNodeDescriptor {
|
||||
std::vector<int> blockPath;
|
||||
TextSlot slot = TextSlot::Text;
|
||||
LeafPath leaf;
|
||||
InsertionAnchor insertionAnchor;
|
||||
RemovalTarget removalTarget;
|
||||
FieldMode mode = FieldMode::Rich;
|
||||
};
|
||||
|
||||
State();
|
||||
@@ -37,41 +163,113 @@ public:
|
||||
[[nodiscard]] bool setActiveTextByOrdinal(int ordinal);
|
||||
[[nodiscard]] TextWithEntities activeText() const;
|
||||
void applyActiveText(TextWithEntities text);
|
||||
[[nodiscard]] FieldMode activeFieldMode() const;
|
||||
[[nodiscard]] QString activeRawText() const;
|
||||
void applyActiveRawText(QString text);
|
||||
[[nodiscard]] int activeTextLength() const;
|
||||
[[nodiscard]] std::optional<int> previousEditableOrdinal() const;
|
||||
[[nodiscard]] std::optional<int> nextEditableOrdinal() const;
|
||||
[[nodiscard]] bool isActiveTopLevelParagraph() const;
|
||||
[[nodiscard]] bool activeOwnerIsEmpty() const;
|
||||
[[nodiscard]] std::optional<int> removeActiveOwnerAndSelectAdjacent(
|
||||
bool forward);
|
||||
[[nodiscard]] int ensureTrailingParagraphActive();
|
||||
void insertHeading1AfterActive();
|
||||
void insertBlockquoteAfterActive();
|
||||
void insertBlockAfterActive(InsertAction action);
|
||||
|
||||
private:
|
||||
[[nodiscard]] RichPage::Block *blockFromPath(const std::vector<int> &path);
|
||||
[[nodiscard]] const RichPage::Block *blockFromPath(
|
||||
const std::vector<int> &path) const;
|
||||
[[nodiscard]] std::vector<RichPage::Block> *blockContainerFromPath(
|
||||
const std::vector<int> &path);
|
||||
[[nodiscard]] RichPage::RichText *richText(
|
||||
const TextNodeDescriptor &descriptor);
|
||||
[[nodiscard]] std::vector<RichPage::Block> *blockContainer(
|
||||
const BlockContainerPath &path);
|
||||
[[nodiscard]] const std::vector<RichPage::Block> *blockContainer(
|
||||
const BlockContainerPath &path) const;
|
||||
[[nodiscard]] RichPage::Block *block(const BlockPath &path);
|
||||
[[nodiscard]] const RichPage::Block *block(const BlockPath &path) const;
|
||||
[[nodiscard]] RichPage::ListItem *listItem(
|
||||
const BlockPath &blockPath,
|
||||
int itemIndex);
|
||||
[[nodiscard]] const RichPage::ListItem *listItem(
|
||||
const BlockPath &blockPath,
|
||||
int itemIndex) const;
|
||||
[[nodiscard]] RichPage::TableCell *tableCell(
|
||||
const BlockPath &blockPath,
|
||||
int rowIndex,
|
||||
int cellIndex);
|
||||
[[nodiscard]] const RichPage::TableCell *tableCell(
|
||||
const BlockPath &blockPath,
|
||||
int rowIndex,
|
||||
int cellIndex) const;
|
||||
[[nodiscard]] RichPage::RichText *richText(const LeafPath &path);
|
||||
[[nodiscard]] const RichPage::RichText *richText(
|
||||
const TextNodeDescriptor &descriptor) const;
|
||||
const LeafPath &path) const;
|
||||
[[nodiscard]] QString *rawText(const LeafPath &path);
|
||||
[[nodiscard]] const QString *rawText(const LeafPath &path) const;
|
||||
[[nodiscard]] const TextNodeDescriptor *textNode(int ordinal) const;
|
||||
[[nodiscard]] int textNodeOrdinal(
|
||||
const std::vector<int> &blockPath,
|
||||
TextSlot slot) const;
|
||||
[[nodiscard]] int textNodeOrdinal(const LeafPath &path) const;
|
||||
|
||||
void rebuild();
|
||||
void rebuildTextNodes();
|
||||
void rebuildTextNodes(
|
||||
const std::vector<RichPage::Block> &blocks,
|
||||
const BlockContainerPath &container);
|
||||
void appendBlockTextNode(
|
||||
const BlockPath &path,
|
||||
LeafKind kind,
|
||||
FieldMode mode = FieldMode::Rich,
|
||||
std::optional<InsertionAnchor> insertionAnchor = std::nullopt);
|
||||
void appendListItemTextNode(const BlockPath &path, int itemIndex);
|
||||
void appendTableCellTextNode(
|
||||
const BlockPath &path,
|
||||
int rowIndex,
|
||||
int cellIndex);
|
||||
void ensureActiveTextOrdinal();
|
||||
void insertBlockAfterActive(RichPage::Block block, TextSlot slotToFocus);
|
||||
void ensureEditableNodes();
|
||||
void focusInsertedBlock(const BlockPath &path);
|
||||
[[nodiscard]] std::optional<int> adjacentEditableOrdinal(
|
||||
bool forward) const;
|
||||
[[nodiscard]] bool descriptorBelongsToBlock(
|
||||
const TextNodeDescriptor &descriptor,
|
||||
const BlockPath &path) const;
|
||||
[[nodiscard]] bool removalTargetIsEmpty(
|
||||
const RemovalTarget &target) const;
|
||||
[[nodiscard]] bool removeTarget(const RemovalTarget &target);
|
||||
[[nodiscard]] bool anchorIdExists(const QString &id) const;
|
||||
[[nodiscard]] bool anchorIdExists(
|
||||
const std::vector<RichPage::Block> &blocks,
|
||||
const QString &id) const;
|
||||
[[nodiscard]] QString nextAnchorId() const;
|
||||
[[nodiscard]] RichPage::Block makeBlock(InsertAction action) const;
|
||||
|
||||
[[nodiscard]] static RichPage::Block makeParagraphBlock();
|
||||
[[nodiscard]] static RichPage::Block makeHeading1Block();
|
||||
[[nodiscard]] static RichPage::Block makeBlockquoteBlock();
|
||||
[[nodiscard]] static bool stripWrapperEntityInEditMode(EntityType type);
|
||||
[[nodiscard]] static TextWithEntities stripEditModeWrapperEntities(
|
||||
[[nodiscard]] static TextWithEntities MakeText(QString text);
|
||||
[[nodiscard]] static RichPage::Block MakeParagraphBlock();
|
||||
[[nodiscard]] static RichPage::Block MakeHeadingBlock(int level);
|
||||
[[nodiscard]] static RichPage::Block MakeQuoteBlock(bool pullquote);
|
||||
[[nodiscard]] static RichPage::Block MakeMathBlock();
|
||||
[[nodiscard]] static RichPage::Block MakeDividerBlock();
|
||||
[[nodiscard]] static RichPage::Block MakeAnchorBlock(QString anchorId);
|
||||
[[nodiscard]] static RichPage::Block MakeListBlock(
|
||||
RichPage::ListKind kind,
|
||||
RichPage::TaskState taskState = RichPage::TaskState::None);
|
||||
[[nodiscard]] static RichPage::Block MakeDetailsBlock();
|
||||
[[nodiscard]] static RichPage::Block MakeTableBlock();
|
||||
[[nodiscard]] static RichPage::Block MakeMediaBlock(
|
||||
RichPage::BlockKind kind);
|
||||
[[nodiscard]] static RichPage::Block MakeMapBlock(
|
||||
double latitude,
|
||||
double longitude);
|
||||
[[nodiscard]] static bool RichTextIsEmpty(const RichPage::RichText &text);
|
||||
[[nodiscard]] static bool ListItemIsEmpty(
|
||||
const RichPage::ListItem &item);
|
||||
[[nodiscard]] static bool BlockIsEmpty(const RichPage::Block &block);
|
||||
[[nodiscard]] static bool StripWrapperEntityInEditMode(EntityType type);
|
||||
[[nodiscard]] static TextWithEntities StripEditModeWrapperEntities(
|
||||
TextWithEntities text);
|
||||
|
||||
std::shared_ptr<RichPage> _richPage;
|
||||
Markdown::MarkdownArticleContent _prepared;
|
||||
std::vector<TextNodeDescriptor> _textNodes;
|
||||
int _activeTextOrdinal = -1;
|
||||
|
||||
};
|
||||
|
||||
} // namespace Iv::Editor
|
||||
|
||||
@@ -23,8 +23,10 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
|
||||
#include "styles/style_iv.h"
|
||||
|
||||
#include <QtCore/QDate>
|
||||
#include <QtCore/QEvent>
|
||||
#include <QtCore/QPointer>
|
||||
#include <QtGui/QFocusEvent>
|
||||
#include <QtGui/QKeyEvent>
|
||||
#include <QtGui/QMouseEvent>
|
||||
#include <QtGui/QResizeEvent>
|
||||
#include <QtGui/QTextCursor>
|
||||
@@ -161,8 +163,8 @@ void Widget::activateInitialNode() {
|
||||
? _activeOrdinal
|
||||
: _state->activeTextOrdinal();
|
||||
if (ordinal < 0) {
|
||||
const auto first = _article->firstTextSegmentIndex();
|
||||
const auto fallback = textOrdinalForSegment(first);
|
||||
const auto first = _article->firstEditableSegmentIndex();
|
||||
const auto fallback = editableOrdinalForSegment(first);
|
||||
if (fallback < 0) {
|
||||
return;
|
||||
}
|
||||
@@ -173,7 +175,7 @@ void Widget::activateInitialNode() {
|
||||
}
|
||||
|
||||
void Widget::activateSegment(int segmentIndex, int cursorOffset) {
|
||||
const auto ordinal = textOrdinalForSegment(segmentIndex);
|
||||
const auto ordinal = editableOrdinalForSegment(segmentIndex);
|
||||
if (ordinal < 0) {
|
||||
return;
|
||||
}
|
||||
@@ -184,18 +186,22 @@ void Widget::commitInlineField() {
|
||||
applyFieldTextToState();
|
||||
}
|
||||
|
||||
void Widget::acceptInlineField() {
|
||||
void Widget::hideInlineFieldAndRefresh() {
|
||||
if (_field->isHidden()) {
|
||||
return;
|
||||
}
|
||||
commitInlineField();
|
||||
_pendingOrdinal = -1;
|
||||
_pendingCursorOffset = 0;
|
||||
_field->hide();
|
||||
hideInlineField();
|
||||
_article->clearTextLeafHeightOverride();
|
||||
refreshPreparedContent();
|
||||
}
|
||||
|
||||
void Widget::acceptInlineField() {
|
||||
hideInlineFieldAndRefresh();
|
||||
}
|
||||
|
||||
void Widget::refreshPreparedContent() {
|
||||
setDocument(_state->prepared());
|
||||
const auto width = std::max(
|
||||
@@ -212,18 +218,42 @@ void Widget::syncInlineFieldGeometry() {
|
||||
syncInlineFieldGeometry(widthNoMargins());
|
||||
}
|
||||
|
||||
void Widget::insertHeading1() {
|
||||
void Widget::insertBlock(State::InsertAction action) {
|
||||
commitInlineField();
|
||||
_state->insertHeading1AfterActive();
|
||||
_state->insertBlockAfterActive(action);
|
||||
refreshPreparedContent();
|
||||
activateTextOrdinal(_state->activeTextOrdinal(), 0);
|
||||
}
|
||||
|
||||
void Widget::insertMedia(State::InsertBlockType type) {
|
||||
switch (type) {
|
||||
case State::InsertBlockType::Photo:
|
||||
case State::InsertBlockType::Video:
|
||||
case State::InsertBlockType::Audio:
|
||||
insertBlock({ .type = type });
|
||||
break;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
void Widget::insertMap(double latitude, double longitude) {
|
||||
insertBlock({
|
||||
.type = State::InsertBlockType::Map,
|
||||
.latitude = latitude,
|
||||
.longitude = longitude,
|
||||
});
|
||||
}
|
||||
|
||||
void Widget::insertHeading1() {
|
||||
insertBlock({
|
||||
.type = State::InsertBlockType::Heading,
|
||||
.headingLevel = 1,
|
||||
});
|
||||
}
|
||||
|
||||
void Widget::insertBlockquote() {
|
||||
commitInlineField();
|
||||
_state->insertBlockquoteAfterActive();
|
||||
refreshPreparedContent();
|
||||
activateTextOrdinal(_state->activeTextOrdinal(), 0);
|
||||
insertBlock({ .type = State::InsertBlockType::Blockquote });
|
||||
}
|
||||
|
||||
int Widget::resizeGetHeight(int newWidth) {
|
||||
@@ -245,9 +275,19 @@ int Widget::resizeGetHeight(int newWidth) {
|
||||
st::ivEditorMinHeight);
|
||||
}
|
||||
|
||||
bool Widget::eventFilter(QObject *object, QEvent *event) {
|
||||
if (_field
|
||||
&& object == _field->rawTextEdit().get()
|
||||
&& event->type() == QEvent::KeyPress
|
||||
&& handleFieldKey(static_cast<QKeyEvent*>(event))) {
|
||||
return true;
|
||||
}
|
||||
return Ui::RpWidget::eventFilter(object, event);
|
||||
}
|
||||
|
||||
void Widget::focusInEvent(QFocusEvent *e) {
|
||||
Ui::RpWidget::focusInEvent(e);
|
||||
if (!_field->isHidden()) {
|
||||
if (!_settingField && !_field->isHidden()) {
|
||||
_field->setFocusFast();
|
||||
}
|
||||
}
|
||||
@@ -321,6 +361,11 @@ void Widget::mouseReleaseEvent(QMouseEvent *e) {
|
||||
&& hit.direct
|
||||
&& _article->segmentIsText(hit.segmentIndex);
|
||||
};
|
||||
const auto directEditableHit = [&] {
|
||||
return hit.valid()
|
||||
&& hit.direct
|
||||
&& _article->segmentIsEditable(hit.segmentIndex);
|
||||
};
|
||||
const auto commitVisibleInlineField = [&] {
|
||||
if (_field->isHidden()) {
|
||||
return;
|
||||
@@ -328,7 +373,7 @@ void Widget::mouseReleaseEvent(QMouseEvent *e) {
|
||||
commitInlineField();
|
||||
_pendingOrdinal = -1;
|
||||
_pendingCursorOffset = 0;
|
||||
_field->hide();
|
||||
hideInlineField();
|
||||
_article->clearTextLeafHeightOverride();
|
||||
refreshPreparedContent();
|
||||
};
|
||||
@@ -346,7 +391,7 @@ void Widget::mouseReleaseEvent(QMouseEvent *e) {
|
||||
&& (selection.from.segment == selection.to.segment)
|
||||
&& _article->segmentIsText(selection.from.segment);
|
||||
const auto selectionOrdinal = sameSegmentSelection
|
||||
? textOrdinalForSegment(selection.from.segment)
|
||||
? editableOrdinalForSegment(selection.from.segment)
|
||||
: -1;
|
||||
if (selectionOrdinal >= 0) {
|
||||
const auto selectionFrom = selection.from.offset;
|
||||
@@ -360,7 +405,8 @@ void Widget::mouseReleaseEvent(QMouseEvent *e) {
|
||||
e->accept();
|
||||
return;
|
||||
} else if (selection.empty() && directTextHit()) {
|
||||
const auto targetOrdinal = textOrdinalForSegment(hit.segmentIndex);
|
||||
const auto targetOrdinal = editableOrdinalForSegment(
|
||||
hit.segmentIndex);
|
||||
const auto targetOffset = _article->selectionOffsetFromHit(
|
||||
hit,
|
||||
TextSelectType::Letters);
|
||||
@@ -381,8 +427,8 @@ void Widget::mouseReleaseEvent(QMouseEvent *e) {
|
||||
e->accept();
|
||||
return;
|
||||
}
|
||||
if (directTextHit()) {
|
||||
const auto targetOrdinal = textOrdinalForSegment(hit.segmentIndex);
|
||||
if (directEditableHit()) {
|
||||
const auto targetOrdinal = editableOrdinalForSegment(hit.segmentIndex);
|
||||
const auto offset = _article->selectionOffsetFromHit(
|
||||
hit,
|
||||
TextSelectType::Letters);
|
||||
@@ -433,7 +479,7 @@ void Widget::setDocument(const Markdown::MarkdownArticleContent &prepared) {
|
||||
Markdown::MarkdownArticleTextLeafStyle Widget::inlineFieldStyleForSegment(
|
||||
int segmentIndex) const {
|
||||
return _article
|
||||
? _article->textLeafStyleForSegment(segmentIndex)
|
||||
? _article->editableStyleForSegment(segmentIndex)
|
||||
: Markdown::MarkdownArticleTextLeafStyle();
|
||||
}
|
||||
|
||||
@@ -453,6 +499,9 @@ const Widget::CachedInlineFieldStyle &Widget::inlineFieldStyleFor(
|
||||
auto fieldStyle = std::make_shared<style::InputField>(
|
||||
st::ivEditorInputField);
|
||||
fieldStyle->style = *data.textStyle;
|
||||
fieldStyle->style.font = data.italic
|
||||
? data.textStyle->font->italic()
|
||||
: data.textStyle->font;
|
||||
fieldStyle->style.lineHeight = data.lineHeight;
|
||||
fieldStyle->textFg = data.textFg;
|
||||
fieldStyle->textAlign = data.align;
|
||||
@@ -477,6 +526,7 @@ Widget::InlineFieldStyleData Widget::normalizedInlineFieldStyle(
|
||||
.lineHeight = lineHeight,
|
||||
.textFg = valid ? leafStyle.textColor : st::messageMarkdown.textColor,
|
||||
.align = valid ? leafStyle.align : style::al_left,
|
||||
.italic = valid ? leafStyle.italic : false,
|
||||
};
|
||||
}
|
||||
|
||||
@@ -486,46 +536,65 @@ Widget::InlineFieldStyleKey Widget::inlineFieldStyleKey(
|
||||
? data.textStyle
|
||||
: &st::messageMarkdown.body;
|
||||
return {
|
||||
.font = textStyle->font,
|
||||
.font = data.italic
|
||||
? textStyle->font->italic()
|
||||
: textStyle->font,
|
||||
.lineHeight = data.lineHeight,
|
||||
.textFg = data.textFg,
|
||||
.align = data.align,
|
||||
};
|
||||
}
|
||||
|
||||
void Widget::ensureInlineFieldStyleForSegment(int segmentIndex) {
|
||||
void Widget::ensureInlineFieldForSegment(int segmentIndex) {
|
||||
const auto data = normalizedInlineFieldStyle(
|
||||
inlineFieldStyleForSegment(segmentIndex));
|
||||
const auto key = inlineFieldStyleKey(data);
|
||||
if (_activeFieldStyleKey && *_activeFieldStyleKey == key) {
|
||||
const auto mode = _state->activeFieldMode();
|
||||
if (_activeFieldStyleKey
|
||||
&& *_activeFieldStyleKey == key
|
||||
&& _fieldMode == mode) {
|
||||
return;
|
||||
}
|
||||
const auto &cached = inlineFieldStyleFor(data);
|
||||
_activeFieldStyleKey = key;
|
||||
_fieldMode = mode;
|
||||
recreateInlineField(*cached.style);
|
||||
}
|
||||
|
||||
void Widget::setupInlineField() {
|
||||
const auto allowPremiumEmoji = [peer = _peer](
|
||||
not_null<DocumentData*> emoji) {
|
||||
return Data::AllowEmojiWithoutPremium(peer, emoji);
|
||||
};
|
||||
InitMessageFieldHandlers({
|
||||
.session = &_controller->session(),
|
||||
.show = _controller->uiShow(),
|
||||
.field = _field.get(),
|
||||
.customEmojiPaused = [=] {
|
||||
return _controller->isGifPausedAtLeastFor(
|
||||
Window::GifPauseReason::Layer);
|
||||
},
|
||||
.allowPremiumEmoji = allowPremiumEmoji,
|
||||
.fieldStyle = &_field->st(),
|
||||
});
|
||||
_field->setMimeDataHook(WrappedMessageFieldMimeHook(
|
||||
Ui::InputField::MimeDataHook(),
|
||||
_field.get()));
|
||||
if (_fieldMode == State::FieldMode::Rich) {
|
||||
const auto allowPremiumEmoji = [peer = _peer](
|
||||
not_null<DocumentData*> emoji) {
|
||||
return Data::AllowEmojiWithoutPremium(peer, emoji);
|
||||
};
|
||||
InitMessageFieldHandlers({
|
||||
.session = &_controller->session(),
|
||||
.show = _controller->uiShow(),
|
||||
.field = _field.get(),
|
||||
.customEmojiPaused = [=] {
|
||||
return _controller->isGifPausedAtLeastFor(
|
||||
Window::GifPauseReason::Layer);
|
||||
},
|
||||
.allowPremiumEmoji = allowPremiumEmoji,
|
||||
.fieldStyle = &_field->st(),
|
||||
});
|
||||
_field->setMimeDataHook(WrappedMessageFieldMimeHook(
|
||||
Ui::InputField::MimeDataHook(),
|
||||
_field.get()));
|
||||
} else {
|
||||
_field->setInstantReplacesEnabled(
|
||||
rpl::single(false),
|
||||
rpl::single(false));
|
||||
_field->setMarkdownReplacesEnabled(
|
||||
rpl::single(Ui::MarkdownEnabledState{
|
||||
Ui::MarkdownDisabled()
|
||||
}));
|
||||
}
|
||||
_field->setDocumentMargin(0.);
|
||||
_field->setAdditionalMargins({});
|
||||
_field->setSubmitSettings(Ui::InputField::SubmitSettings::None);
|
||||
_field->setMaxHeight(std::numeric_limits<int>::max());
|
||||
_field->rawTextEdit()->installEventFilter(this);
|
||||
|
||||
_field->heightChanges(
|
||||
) | rpl::on_next([=] {
|
||||
@@ -539,7 +608,7 @@ void Widget::setupInlineField() {
|
||||
}
|
||||
}, _field->lifetime());
|
||||
|
||||
_field->hide();
|
||||
hideInlineField();
|
||||
}
|
||||
|
||||
void Widget::recreateInlineField(const style::InputField &st) {
|
||||
@@ -592,25 +661,32 @@ void Widget::activateTextOrdinal(
|
||||
_pendingOrdinal = -1;
|
||||
_pendingCursorOffset = 0;
|
||||
|
||||
const auto segmentIndex = segmentIndexForTextOrdinal(ordinal);
|
||||
const auto segmentIndex = segmentIndexForEditableOrdinal(ordinal);
|
||||
if (segmentIndex < 0) {
|
||||
_activeSegmentIndex = -1;
|
||||
_pendingOrdinal = ordinal;
|
||||
_pendingCursorOffset = selectionTo;
|
||||
_field->hide();
|
||||
hideInlineField();
|
||||
return;
|
||||
}
|
||||
|
||||
_activeSegmentIndex = segmentIndex;
|
||||
ensureInlineFieldStyleForSegment(segmentIndex);
|
||||
const auto activeText = _state->activeText();
|
||||
ensureInlineFieldForSegment(segmentIndex);
|
||||
_settingField = true;
|
||||
_field->setTextWithTags(
|
||||
{
|
||||
activeText.text,
|
||||
TextUtilities::ConvertEntitiesToTextTags(activeText.entities),
|
||||
},
|
||||
Ui::InputField::HistoryAction::Clear);
|
||||
if (_state->activeFieldMode() == State::FieldMode::Raw) {
|
||||
_field->setTextWithTags(
|
||||
{ _state->activeRawText(), {} },
|
||||
Ui::InputField::HistoryAction::Clear);
|
||||
_article->clearTextLeafHeightOverride();
|
||||
} else {
|
||||
const auto activeText = _state->activeText();
|
||||
_field->setTextWithTags(
|
||||
{
|
||||
activeText.text,
|
||||
TextUtilities::ConvertEntitiesToTextTags(activeText.entities),
|
||||
},
|
||||
Ui::InputField::HistoryAction::Clear);
|
||||
}
|
||||
auto cursor = _field->textCursor();
|
||||
const auto size = _field->getLastText().size();
|
||||
const auto from = std::clamp(selectionFrom, 0, size);
|
||||
@@ -639,6 +715,10 @@ void Widget::applyFieldTextToState() {
|
||||
if (_settingField || _field->isHidden()) {
|
||||
return;
|
||||
}
|
||||
if (_state->activeFieldMode() == State::FieldMode::Raw) {
|
||||
_state->applyActiveRawText(_field->getLastText());
|
||||
return;
|
||||
}
|
||||
const auto text = _field->getTextWithAppliedMarkdown();
|
||||
_state->applyActiveText({
|
||||
.text = text.text,
|
||||
@@ -646,11 +726,144 @@ void Widget::applyFieldTextToState() {
|
||||
});
|
||||
}
|
||||
|
||||
void Widget::hideInlineField() {
|
||||
if (_field->isHidden()) {
|
||||
return;
|
||||
}
|
||||
const auto wasSettingField = _settingField;
|
||||
_settingField = true;
|
||||
const auto guard = gsl::finally([&] {
|
||||
_settingField = wasSettingField;
|
||||
});
|
||||
_field->hide();
|
||||
}
|
||||
|
||||
void Widget::activateTextOrdinalAtEnd(int ordinal) {
|
||||
if (!_state->setActiveTextByOrdinal(ordinal)) {
|
||||
return;
|
||||
}
|
||||
activateTextOrdinal(ordinal, _state->activeTextLength());
|
||||
}
|
||||
|
||||
bool Widget::handleFieldKey(QKeyEvent *e) {
|
||||
if (_field->isHidden()) {
|
||||
return false;
|
||||
}
|
||||
const auto key = e->key();
|
||||
if (key == Qt::Key_Escape) {
|
||||
hideInlineFieldAndRefresh();
|
||||
e->accept();
|
||||
return true;
|
||||
}
|
||||
const auto modifiers = e->modifiers()
|
||||
& ~(Qt::KeypadModifier | Qt::GroupSwitchModifier);
|
||||
if (modifiers != Qt::NoModifier) {
|
||||
return false;
|
||||
}
|
||||
const auto cursor = _field->textCursor();
|
||||
if (cursor.hasSelection()) {
|
||||
return false;
|
||||
}
|
||||
const auto position = cursor.position();
|
||||
const auto length = _field->getLastText().size();
|
||||
auto handled = false;
|
||||
if (position <= 0
|
||||
&& (key == Qt::Key_Left
|
||||
|| key == Qt::Key_Up
|
||||
|| key == Qt::Key_PageUp)) {
|
||||
handled = moveBoundary(false, false);
|
||||
} else if (position >= length
|
||||
&& (key == Qt::Key_Right
|
||||
|| key == Qt::Key_Down
|
||||
|| key == Qt::Key_PageDown)) {
|
||||
handled = moveBoundary(true, true);
|
||||
} else if (position <= 0 && key == Qt::Key_Backspace) {
|
||||
handled = removeBoundaryOwner(false);
|
||||
} else if (position >= length && key == Qt::Key_Delete) {
|
||||
handled = removeBoundaryOwner(true);
|
||||
}
|
||||
if (handled) {
|
||||
e->accept();
|
||||
}
|
||||
return handled;
|
||||
}
|
||||
|
||||
bool Widget::moveBoundary(bool forward, bool allowTrailing) {
|
||||
const auto target = forward
|
||||
? _state->nextEditableOrdinal()
|
||||
: _state->previousEditableOrdinal();
|
||||
const auto addTrailing = forward
|
||||
&& allowTrailing
|
||||
&& !target
|
||||
&& !_state->isActiveTopLevelParagraph();
|
||||
if (!target && !addTrailing) {
|
||||
return false;
|
||||
}
|
||||
commitInlineField();
|
||||
if (target) {
|
||||
refreshPreparedContent();
|
||||
if (forward) {
|
||||
activateTextOrdinal(*target, 0);
|
||||
} else {
|
||||
activateTextOrdinalAtEnd(*target);
|
||||
}
|
||||
return true;
|
||||
}
|
||||
const auto ordinal = _state->ensureTrailingParagraphActive();
|
||||
refreshPreparedContent();
|
||||
activateTextOrdinal(ordinal, 0);
|
||||
return true;
|
||||
}
|
||||
|
||||
bool Widget::moveBoundaryAfterCommit(bool forward, bool allowTrailing) {
|
||||
const auto target = forward
|
||||
? _state->nextEditableOrdinal()
|
||||
: _state->previousEditableOrdinal();
|
||||
if (target) {
|
||||
refreshPreparedContent();
|
||||
if (forward) {
|
||||
activateTextOrdinal(*target, 0);
|
||||
} else {
|
||||
activateTextOrdinalAtEnd(*target);
|
||||
}
|
||||
return true;
|
||||
}
|
||||
if (forward && allowTrailing && !_state->isActiveTopLevelParagraph()) {
|
||||
const auto ordinal = _state->ensureTrailingParagraphActive();
|
||||
refreshPreparedContent();
|
||||
activateTextOrdinal(ordinal, 0);
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
bool Widget::removeBoundaryOwner(bool forward) {
|
||||
commitInlineField();
|
||||
if (_state->activeOwnerIsEmpty()) {
|
||||
const auto target = _state->removeActiveOwnerAndSelectAdjacent(
|
||||
forward);
|
||||
hideInlineField();
|
||||
_article->clearTextLeafHeightOverride();
|
||||
refreshPreparedContent();
|
||||
if (target) {
|
||||
if (forward) {
|
||||
activateTextOrdinal(*target, 0);
|
||||
} else {
|
||||
activateTextOrdinalAtEnd(*target);
|
||||
}
|
||||
} else {
|
||||
activateInitialNode();
|
||||
}
|
||||
return true;
|
||||
}
|
||||
return moveBoundaryAfterCommit(forward, forward);
|
||||
}
|
||||
|
||||
void Widget::ensurePendingActivation() {
|
||||
if (_pendingOrdinal < 0) {
|
||||
_activeSegmentIndex = (_activeOrdinal >= 0)
|
||||
? segmentIndexForTextOrdinal(_activeOrdinal)
|
||||
: _article->firstTextSegmentIndex();
|
||||
? segmentIndexForEditableOrdinal(_activeOrdinal)
|
||||
: _article->firstEditableSegmentIndex();
|
||||
return;
|
||||
}
|
||||
const auto ordinal = _pendingOrdinal;
|
||||
@@ -670,11 +883,17 @@ void Widget::updateInlineFieldHeightOverride() {
|
||||
_pendingHeightOverrideUpdate = true;
|
||||
return;
|
||||
}
|
||||
const auto segmentRect = outerTextSegmentRect(_activeSegmentIndex);
|
||||
const auto textLeafIndex = _article->textLeafIndexForSegment(
|
||||
_activeSegmentIndex);
|
||||
if (textLeafIndex < 0) {
|
||||
_article->clearTextLeafHeightOverride();
|
||||
return;
|
||||
}
|
||||
const auto segmentRect = outerEditableSegmentRect(_activeSegmentIndex);
|
||||
const auto height = segmentRect.isEmpty()
|
||||
? _field->height()
|
||||
: std::max(_field->geometry().bottom() + 1 - segmentRect.y(), 1);
|
||||
_article->setTextLeafHeightOverride(_activeOrdinal, height);
|
||||
_article->setTextLeafHeightOverride(textLeafIndex, height);
|
||||
resizeToWidth(std::max(widthNoMargins(), 1));
|
||||
update();
|
||||
}
|
||||
@@ -684,24 +903,27 @@ void Widget::syncInlineFieldGeometry(int width) {
|
||||
return;
|
||||
}
|
||||
if (_activeSegmentIndex >= 0) {
|
||||
ensureInlineFieldStyleForSegment(_activeSegmentIndex);
|
||||
ensureInlineFieldForSegment(_activeSegmentIndex);
|
||||
}
|
||||
const auto segmentRect = outerTextSegmentRect(_activeSegmentIndex);
|
||||
const auto segmentRect = outerEditableSegmentRect(_activeSegmentIndex);
|
||||
if (segmentRect.isEmpty()) {
|
||||
_pendingOrdinal = _activeOrdinal;
|
||||
_pendingCursorOffset = _field->textCursor().position();
|
||||
_field->hide();
|
||||
hideInlineField();
|
||||
_article->clearTextLeafHeightOverride();
|
||||
return;
|
||||
}
|
||||
const auto margins = _field->fullTextMargins();
|
||||
const auto left = segmentRect.x() - margins.left();
|
||||
const auto top = segmentRect.y() - margins.top();
|
||||
const auto fieldWidth = std::max(
|
||||
std::min(segmentRect.width(), width - segmentRect.x()),
|
||||
std::min(
|
||||
segmentRect.width() + margins.left() + margins.right(),
|
||||
width - left),
|
||||
1);
|
||||
_syncingInlineFieldGeometry = true;
|
||||
_field->resizeToWidth(fieldWidth);
|
||||
const auto fieldHeight = FieldNaturalHeight(_field.get());
|
||||
const auto left = segmentRect.x();
|
||||
const auto top = segmentRect.y();
|
||||
_field->setGeometryToLeft(left, top, fieldWidth, fieldHeight, width);
|
||||
_field->raise();
|
||||
_syncingInlineFieldGeometry = false;
|
||||
@@ -766,12 +988,12 @@ void Widget::updateTextSelection(
|
||||
}
|
||||
}
|
||||
|
||||
int Widget::textOrdinalForSegment(int segmentIndex) const {
|
||||
return _article->textLeafIndexForSegment(segmentIndex);
|
||||
int Widget::editableOrdinalForSegment(int segmentIndex) const {
|
||||
return _article->editableIndexForSegment(segmentIndex);
|
||||
}
|
||||
|
||||
int Widget::segmentIndexForTextOrdinal(int ordinal) const {
|
||||
return _article->segmentIndexForTextLeafIndex(ordinal);
|
||||
int Widget::segmentIndexForEditableOrdinal(int ordinal) const {
|
||||
return _article->segmentIndexForEditableIndex(ordinal);
|
||||
}
|
||||
|
||||
QPoint Widget::articleTopLeft() const {
|
||||
@@ -786,8 +1008,8 @@ int Widget::articleWidth(int outerWidth) const {
|
||||
1);
|
||||
}
|
||||
|
||||
QRect Widget::outerTextSegmentRect(int segmentIndex) const {
|
||||
const auto rect = _article->textSegmentRect(segmentIndex);
|
||||
QRect Widget::outerEditableSegmentRect(int segmentIndex) const {
|
||||
const auto rect = _article->segmentRect(segmentIndex);
|
||||
return rect.isEmpty() ? rect : rect.translated(articleTopLeft());
|
||||
}
|
||||
|
||||
@@ -823,9 +1045,11 @@ Markdown::MarkdownArticlePaintContext Widget::textPaintContext(QRect clip) {
|
||||
});
|
||||
},
|
||||
};
|
||||
context.hiddenTextSegmentIndex = _field->isHidden()
|
||||
const auto hiddenSegmentIndex = _field->isHidden()
|
||||
? -1
|
||||
: _activeSegmentIndex;
|
||||
context.hiddenTextSegmentIndex = hiddenSegmentIndex;
|
||||
context.hiddenSegmentIndex = hiddenSegmentIndex;
|
||||
context.selectionState.selection = _selection;
|
||||
context.selectionState.endpoints = &_selectionEndpoints;
|
||||
return context;
|
||||
|
||||
@@ -17,6 +17,10 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
|
||||
#include <optional>
|
||||
#include <vector>
|
||||
|
||||
class QEvent;
|
||||
class QKeyEvent;
|
||||
class QObject;
|
||||
|
||||
namespace Ui {
|
||||
class ChatStyle;
|
||||
class ChatTheme;
|
||||
@@ -48,12 +52,16 @@ public:
|
||||
void commitInlineField();
|
||||
void refreshPreparedContent();
|
||||
void syncInlineFieldGeometry();
|
||||
void insertBlock(State::InsertAction action);
|
||||
void insertMedia(State::InsertBlockType type);
|
||||
void insertMap(double latitude, double longitude);
|
||||
void insertHeading1();
|
||||
void insertBlockquote();
|
||||
|
||||
int resizeGetHeight(int newWidth) override;
|
||||
|
||||
protected:
|
||||
bool eventFilter(QObject *object, QEvent *event) override;
|
||||
void focusInEvent(QFocusEvent *e) override;
|
||||
void mouseMoveEvent(QMouseEvent *e) override;
|
||||
void mousePressEvent(QMouseEvent *e) override;
|
||||
@@ -67,6 +75,7 @@ private:
|
||||
int lineHeight = 0;
|
||||
style::color textFg;
|
||||
style::align align = style::al_left;
|
||||
bool italic = false;
|
||||
};
|
||||
|
||||
struct InlineFieldStyleKey {
|
||||
@@ -109,22 +118,31 @@ private:
|
||||
const Markdown::MarkdownArticleTextLeafStyle &style) const;
|
||||
[[nodiscard]] InlineFieldStyleKey inlineFieldStyleKey(
|
||||
const InlineFieldStyleData &data) const;
|
||||
void ensureInlineFieldStyleForSegment(int segmentIndex);
|
||||
void ensureInlineFieldForSegment(int segmentIndex);
|
||||
void setupInlineField();
|
||||
void recreateInlineField(const style::InputField &st);
|
||||
void activateTrailingParagraph();
|
||||
void applyFieldTextToState();
|
||||
void hideInlineField();
|
||||
void acceptInlineField();
|
||||
void hideInlineFieldAndRefresh();
|
||||
void activateTextOrdinalAtEnd(int ordinal);
|
||||
[[nodiscard]] bool handleFieldKey(QKeyEvent *e);
|
||||
[[nodiscard]] bool moveBoundary(bool forward, bool allowTrailing);
|
||||
[[nodiscard]] bool moveBoundaryAfterCommit(
|
||||
bool forward,
|
||||
bool allowTrailing);
|
||||
[[nodiscard]] bool removeBoundaryOwner(bool forward);
|
||||
void ensurePendingActivation();
|
||||
void updateInlineFieldHeightOverride();
|
||||
void syncInlineFieldGeometry(int width);
|
||||
void clearTextSelection();
|
||||
void updateTextSelection(const Markdown::MarkdownArticleHitTestResult &hit);
|
||||
[[nodiscard]] int textOrdinalForSegment(int segmentIndex) const;
|
||||
[[nodiscard]] int segmentIndexForTextOrdinal(int ordinal) const;
|
||||
[[nodiscard]] int editableOrdinalForSegment(int segmentIndex) const;
|
||||
[[nodiscard]] int segmentIndexForEditableOrdinal(int ordinal) const;
|
||||
[[nodiscard]] QPoint articleTopLeft() const;
|
||||
[[nodiscard]] int articleWidth(int outerWidth) const;
|
||||
[[nodiscard]] QRect outerTextSegmentRect(int segmentIndex) const;
|
||||
[[nodiscard]] QRect outerEditableSegmentRect(int segmentIndex) const;
|
||||
[[nodiscard]] Markdown::MarkdownArticlePaintContext textPaintContext(
|
||||
QRect clip);
|
||||
|
||||
@@ -138,6 +156,7 @@ private:
|
||||
std::vector<Ui::Text::SpecialColor> _highlightColors;
|
||||
std::vector<CachedInlineFieldStyle> _fieldStyles;
|
||||
std::optional<InlineFieldStyleKey> _activeFieldStyleKey;
|
||||
State::FieldMode _fieldMode = State::FieldMode::Rich;
|
||||
int _articleHeight = 0;
|
||||
int _activeOrdinal = -1;
|
||||
int _activeSegmentIndex = -1;
|
||||
|
||||
@@ -119,6 +119,14 @@ void RestoreRelatedArticleImageStates(
|
||||
}
|
||||
}
|
||||
|
||||
[[nodiscard]] bool IsDisplayMathSegment(const SelectableSegment &segment) {
|
||||
return (segment.kind == SelectableSegmentKind::DisplayMath);
|
||||
}
|
||||
|
||||
[[nodiscard]] bool IsEditableSegment(const SelectableSegment &segment) {
|
||||
return segment.isTextLeaf() || IsDisplayMathSegment(segment);
|
||||
}
|
||||
|
||||
void CollectPlaceholderIds(
|
||||
const std::vector<LaidOutBlock> &blocks,
|
||||
std::unordered_set<uint64> *result) {
|
||||
@@ -747,19 +755,34 @@ public:
|
||||
|
||||
[[nodiscard]] bool segmentIsText(int index) const;
|
||||
|
||||
[[nodiscard]] bool segmentIsDisplayMath(int index) const;
|
||||
|
||||
[[nodiscard]] bool segmentIsEditable(int index) const;
|
||||
|
||||
[[nodiscard]] int segmentLength(int index) const;
|
||||
|
||||
[[nodiscard]] int firstTextSegmentIndex() const;
|
||||
|
||||
[[nodiscard]] int firstEditableSegmentIndex() const;
|
||||
|
||||
[[nodiscard]] int textLeafIndexForSegment(int segmentIndex) const;
|
||||
|
||||
[[nodiscard]] int segmentIndexForTextLeafIndex(int textLeafIndex) const;
|
||||
|
||||
[[nodiscard]] int editableIndexForSegment(int segmentIndex) const;
|
||||
|
||||
[[nodiscard]] int segmentIndexForEditableIndex(int editableIndex) const;
|
||||
|
||||
[[nodiscard]] QRect textSegmentRect(int segmentIndex) const;
|
||||
|
||||
[[nodiscard]] QRect segmentRect(int segmentIndex) const;
|
||||
|
||||
[[nodiscard]] MarkdownArticleTextLeafStyle textLeafStyleForSegment(
|
||||
int segmentIndex) const;
|
||||
|
||||
[[nodiscard]] MarkdownArticleTextLeafStyle editableStyleForSegment(
|
||||
int segmentIndex) const;
|
||||
|
||||
[[nodiscard]] int selectionOffsetFromHit(
|
||||
const MarkdownArticleHitTestResult &result,
|
||||
TextSelectType selectionType) const;
|
||||
@@ -1087,6 +1110,16 @@ bool MarkdownArticle::Impl::segmentIsText(int index) const {
|
||||
return segment && segment->isTextLeaf();
|
||||
}
|
||||
|
||||
bool MarkdownArticle::Impl::segmentIsDisplayMath(int index) const {
|
||||
const auto segment = FindSegment(&_segments, index);
|
||||
return segment && IsDisplayMathSegment(*segment);
|
||||
}
|
||||
|
||||
bool MarkdownArticle::Impl::segmentIsEditable(int index) const {
|
||||
const auto segment = FindSegment(&_segments, index);
|
||||
return segment && IsEditableSegment(*segment);
|
||||
}
|
||||
|
||||
int MarkdownArticle::Impl::segmentLength(int index) const {
|
||||
const auto segment = FindSegment(&_segments, index);
|
||||
return segment ? SegmentLength(*segment) : 0;
|
||||
@@ -1101,6 +1134,15 @@ int MarkdownArticle::Impl::firstTextSegmentIndex() const {
|
||||
return -1;
|
||||
}
|
||||
|
||||
int MarkdownArticle::Impl::firstEditableSegmentIndex() const {
|
||||
for (const auto &segment : _segments) {
|
||||
if (IsEditableSegment(segment)) {
|
||||
return segment.index;
|
||||
}
|
||||
}
|
||||
return -1;
|
||||
}
|
||||
|
||||
int MarkdownArticle::Impl::textLeafIndexForSegment(int segmentIndex) const {
|
||||
auto textLeafIndex = 0;
|
||||
for (const auto &segment : _segments) {
|
||||
@@ -1131,11 +1173,56 @@ int MarkdownArticle::Impl::segmentIndexForTextLeafIndex(
|
||||
return -1;
|
||||
}
|
||||
|
||||
int MarkdownArticle::Impl::editableIndexForSegment(int segmentIndex) const {
|
||||
auto editableIndex = 0;
|
||||
for (const auto &segment : _segments) {
|
||||
if (!IsEditableSegment(segment)) {
|
||||
continue;
|
||||
} else if (segment.index == segmentIndex) {
|
||||
return editableIndex;
|
||||
}
|
||||
++editableIndex;
|
||||
}
|
||||
return -1;
|
||||
}
|
||||
|
||||
int MarkdownArticle::Impl::segmentIndexForEditableIndex(
|
||||
int editableIndex) const {
|
||||
if (editableIndex < 0) {
|
||||
return -1;
|
||||
}
|
||||
auto current = 0;
|
||||
for (const auto &segment : _segments) {
|
||||
if (!IsEditableSegment(segment)) {
|
||||
continue;
|
||||
} else if (current == editableIndex) {
|
||||
return segment.index;
|
||||
}
|
||||
++current;
|
||||
}
|
||||
return -1;
|
||||
}
|
||||
|
||||
QRect MarkdownArticle::Impl::textSegmentRect(int segmentIndex) const {
|
||||
const auto segment = FindSegment(&_segments, segmentIndex);
|
||||
return (segment && segment->isTextLeaf()) ? segment->textRect : QRect();
|
||||
}
|
||||
|
||||
QRect MarkdownArticle::Impl::segmentRect(int segmentIndex) const {
|
||||
const auto segment = FindSegment(&_segments, segmentIndex);
|
||||
if (!segment) {
|
||||
return QRect();
|
||||
} else if (segment->isTextLeaf()) {
|
||||
return segment->textRect;
|
||||
} else if (IsDisplayMathSegment(*segment)) {
|
||||
if (!segment->outerRect.isEmpty()) {
|
||||
return segment->outerRect;
|
||||
}
|
||||
return segment->block ? segment->block->formulaRect : QRect();
|
||||
}
|
||||
return QRect();
|
||||
}
|
||||
|
||||
MarkdownArticleTextLeafStyle MarkdownArticle::Impl::textLeafStyleForSegment(
|
||||
int segmentIndex) const {
|
||||
const auto segment = FindSegment(&_segments, segmentIndex);
|
||||
@@ -1149,6 +1236,26 @@ MarkdownArticleTextLeafStyle MarkdownArticle::Impl::textLeafStyleForSegment(
|
||||
.textColor = TextColorForSegment(*segment, st),
|
||||
.lineHeight = TextLineHeight(textStyle),
|
||||
.align = segment->align,
|
||||
.italic = segment->block && segment->block->pullquote,
|
||||
};
|
||||
}
|
||||
|
||||
MarkdownArticleTextLeafStyle MarkdownArticle::Impl::editableStyleForSegment(
|
||||
int segmentIndex) const {
|
||||
const auto segment = FindSegment(&_segments, segmentIndex);
|
||||
if (!segment) {
|
||||
return {};
|
||||
} else if (segment->isTextLeaf()) {
|
||||
return textLeafStyleForSegment(segmentIndex);
|
||||
} else if (!IsDisplayMathSegment(*segment)) {
|
||||
return {};
|
||||
}
|
||||
const auto &st = layoutStyle();
|
||||
return {
|
||||
.textStyle = &st.displayMath.fallbackStyle,
|
||||
.textColor = st.displayMath.fg,
|
||||
.lineHeight = TextLineHeight(st.displayMath.fallbackStyle),
|
||||
.align = segment->align,
|
||||
};
|
||||
}
|
||||
|
||||
@@ -1788,6 +1895,14 @@ bool MarkdownArticle::segmentIsText(int index) const {
|
||||
return _impl->segmentIsText(index);
|
||||
}
|
||||
|
||||
bool MarkdownArticle::segmentIsDisplayMath(int index) const {
|
||||
return _impl->segmentIsDisplayMath(index);
|
||||
}
|
||||
|
||||
bool MarkdownArticle::segmentIsEditable(int index) const {
|
||||
return _impl->segmentIsEditable(index);
|
||||
}
|
||||
|
||||
int MarkdownArticle::segmentLength(int index) const {
|
||||
return _impl->segmentLength(index);
|
||||
}
|
||||
@@ -1796,6 +1911,10 @@ int MarkdownArticle::firstTextSegmentIndex() const {
|
||||
return _impl->firstTextSegmentIndex();
|
||||
}
|
||||
|
||||
int MarkdownArticle::firstEditableSegmentIndex() const {
|
||||
return _impl->firstEditableSegmentIndex();
|
||||
}
|
||||
|
||||
int MarkdownArticle::textLeafIndexForSegment(int segmentIndex) const {
|
||||
return _impl->textLeafIndexForSegment(segmentIndex);
|
||||
}
|
||||
@@ -1804,15 +1923,32 @@ int MarkdownArticle::segmentIndexForTextLeafIndex(int textLeafIndex) const {
|
||||
return _impl->segmentIndexForTextLeafIndex(textLeafIndex);
|
||||
}
|
||||
|
||||
int MarkdownArticle::editableIndexForSegment(int segmentIndex) const {
|
||||
return _impl->editableIndexForSegment(segmentIndex);
|
||||
}
|
||||
|
||||
int MarkdownArticle::segmentIndexForEditableIndex(int editableIndex) const {
|
||||
return _impl->segmentIndexForEditableIndex(editableIndex);
|
||||
}
|
||||
|
||||
QRect MarkdownArticle::textSegmentRect(int segmentIndex) const {
|
||||
return _impl->textSegmentRect(segmentIndex);
|
||||
}
|
||||
|
||||
QRect MarkdownArticle::segmentRect(int segmentIndex) const {
|
||||
return _impl->segmentRect(segmentIndex);
|
||||
}
|
||||
|
||||
MarkdownArticleTextLeafStyle MarkdownArticle::textLeafStyleForSegment(
|
||||
int segmentIndex) const {
|
||||
return _impl->textLeafStyleForSegment(segmentIndex);
|
||||
}
|
||||
|
||||
MarkdownArticleTextLeafStyle MarkdownArticle::editableStyleForSegment(
|
||||
int segmentIndex) const {
|
||||
return _impl->editableStyleForSegment(segmentIndex);
|
||||
}
|
||||
|
||||
int MarkdownArticle::selectionOffsetFromHit(
|
||||
const MarkdownArticleHitTestResult &result,
|
||||
TextSelectType selectionType) const {
|
||||
|
||||
@@ -157,6 +157,7 @@ struct MarkdownArticlePaintContext final : Ui::ChatPaintContext {
|
||||
PaintSelectionState selectionState;
|
||||
MarkdownArticleRevealPaintState *reveal = nullptr;
|
||||
int hiddenTextSegmentIndex = -1;
|
||||
int hiddenSegmentIndex = -1;
|
||||
bool debugBlockGeometry = false;
|
||||
|
||||
[[nodiscard]] MarkdownArticlePaintContext translated(int x, int y) const {
|
||||
@@ -192,6 +193,7 @@ struct MarkdownArticleTextLeafStyle {
|
||||
style::color textColor;
|
||||
int lineHeight = 0;
|
||||
style::align align = style::al_left;
|
||||
bool italic = false;
|
||||
|
||||
[[nodiscard]] bool valid() const {
|
||||
return textStyle && textColor;
|
||||
@@ -237,13 +239,21 @@ public:
|
||||
const QString &anchorId);
|
||||
[[nodiscard]] bool toggleDetails(const QString &anchorId);
|
||||
[[nodiscard]] bool segmentIsText(int index) const;
|
||||
[[nodiscard]] bool segmentIsDisplayMath(int index) const;
|
||||
[[nodiscard]] bool segmentIsEditable(int index) const;
|
||||
[[nodiscard]] int segmentLength(int index) const;
|
||||
[[nodiscard]] int firstTextSegmentIndex() const;
|
||||
[[nodiscard]] int firstEditableSegmentIndex() const;
|
||||
[[nodiscard]] int textLeafIndexForSegment(int segmentIndex) const;
|
||||
[[nodiscard]] int segmentIndexForTextLeafIndex(int textLeafIndex) const;
|
||||
[[nodiscard]] int editableIndexForSegment(int segmentIndex) const;
|
||||
[[nodiscard]] int segmentIndexForEditableIndex(int editableIndex) const;
|
||||
[[nodiscard]] QRect textSegmentRect(int segmentIndex) const;
|
||||
[[nodiscard]] QRect segmentRect(int segmentIndex) const;
|
||||
[[nodiscard]] MarkdownArticleTextLeafStyle textLeafStyleForSegment(
|
||||
int segmentIndex) const;
|
||||
[[nodiscard]] MarkdownArticleTextLeafStyle editableStyleForSegment(
|
||||
int segmentIndex) const;
|
||||
[[nodiscard]] int selectionOffsetFromHit(
|
||||
const MarkdownArticleHitTestResult &result,
|
||||
TextSelectType selectionType) const;
|
||||
|
||||
@@ -418,7 +418,7 @@ void LayoutMediaCaption(
|
||||
int skip,
|
||||
int *bottom,
|
||||
LayoutContext context) {
|
||||
if (prepared.text.text.isEmpty()) {
|
||||
if (prepared.text.text.isEmpty() && !prepared.forceTextSegment) {
|
||||
return;
|
||||
}
|
||||
block->supplementary = prepared.supplementary;
|
||||
|
||||
@@ -687,7 +687,8 @@ void PaintSelectableTextLeaf(
|
||||
std::optional<TextSelection> selection = std::nullopt,
|
||||
int elisionLines = 0) {
|
||||
if ((segmentIndex >= 0)
|
||||
&& (context.hiddenTextSegmentIndex == segmentIndex)) {
|
||||
&& ((context.hiddenTextSegmentIndex == segmentIndex)
|
||||
|| (context.hiddenSegmentIndex == segmentIndex))) {
|
||||
if (context.reveal && !elisionLines) {
|
||||
context.reveal->nextLine += CountTextRevealLines(leaf, rect, width);
|
||||
}
|
||||
@@ -1362,6 +1363,10 @@ void PaintDisplayMathBlock(
|
||||
context,
|
||||
block.visibleFormulaRect,
|
||||
[&](Painter &p, const MarkdownArticlePaintContext &formulaContext) {
|
||||
if (block.segmentIndex >= 0
|
||||
&& (formulaContext.hiddenSegmentIndex == block.segmentIndex)) {
|
||||
return;
|
||||
}
|
||||
const auto &paintSt = PaintStyle(formulaContext, st);
|
||||
const auto formula = PreparedFormulaFor(formulas, block.formulaIndex);
|
||||
p.setPen(paintSt.textColor->c);
|
||||
|
||||
@@ -405,13 +405,14 @@ void CollectSelectableSegments(
|
||||
segment.kind = SelectableSegmentKind::DisplayMath;
|
||||
segment.block = █
|
||||
segment.outerRect = block.visibleFormulaRect;
|
||||
segment.align = block.formulaAlign;
|
||||
segment.length = 1;
|
||||
block.segmentIndex = AddSelectableSegment(
|
||||
segments,
|
||||
std::move(segment));
|
||||
} break;
|
||||
case PreparedBlockKind::Table: {
|
||||
if (!block.textRect.isEmpty() && !block.leaf.isEmpty()) {
|
||||
if (!block.textRect.isEmpty()) {
|
||||
auto textSegment = SelectableSegment();
|
||||
textSegment.kind = SelectableSegmentKind::TextLeaf;
|
||||
textSegment.leaf = &block.leaf;
|
||||
@@ -477,7 +478,7 @@ void CollectSelectableSegments(
|
||||
block.segmentIndex = AddSelectableSegment(
|
||||
segments,
|
||||
std::move(segment));
|
||||
if (!block.textRect.isEmpty() && !block.leaf.isEmpty()) {
|
||||
if (!block.textRect.isEmpty()) {
|
||||
auto textSegment = SelectableSegment();
|
||||
textSegment.kind = SelectableSegmentKind::TextLeaf;
|
||||
textSegment.leaf = &block.leaf;
|
||||
|
||||
@@ -253,6 +253,7 @@ struct PreparedBlock {
|
||||
bool tight = false;
|
||||
bool supplementary = false;
|
||||
bool pullquote = false;
|
||||
bool forceTextSegment = false;
|
||||
};
|
||||
|
||||
struct PreparedRenderDocument {
|
||||
|
||||
@@ -144,47 +144,6 @@ void SortPreparedIvRichText(PreparedIvRichText *text) {
|
||||
return block;
|
||||
}
|
||||
|
||||
[[nodiscard]] bool PrepareNativeIvMathBlock(
|
||||
const MTPDpageBlockMath &data,
|
||||
std::vector<PreparedBlock> *result,
|
||||
NativeIvPrepareState *state) {
|
||||
const auto source = qs(data.vsource());
|
||||
if (source.trimmed().isEmpty()) {
|
||||
return true;
|
||||
}
|
||||
auto block = PreparedBlock();
|
||||
block.kind = PreparedBlockKind::DisplayMath;
|
||||
block.formulaTex = source;
|
||||
block.mathKind = MathKind::Display;
|
||||
block.formulaIndex = state->rememberFormula(block);
|
||||
result->push_back(std::move(block));
|
||||
return true;
|
||||
}
|
||||
|
||||
[[nodiscard]] bool AppendNativeIvFlowBlock(
|
||||
std::vector<PreparedBlock> *result,
|
||||
PreparedBlockKind kind,
|
||||
int headingLevel,
|
||||
const MTPRichText &text,
|
||||
NativeIvPrepareState *state,
|
||||
bool allowEmpty = false) {
|
||||
auto prepared = PreparedIvRichText();
|
||||
auto anchorId = QString();
|
||||
const auto context = NativeIvRichTextContextForTextSize(
|
||||
NativeIvFlowTextSize(kind, headingLevel, state->dimensions),
|
||||
state->dimensions);
|
||||
if (!PrepareNativeIvRichText(text, &prepared, &anchorId, state, context)) {
|
||||
return false;
|
||||
}
|
||||
return AppendPreparedIvRichBlock(
|
||||
result,
|
||||
kind,
|
||||
headingLevel,
|
||||
std::move(prepared),
|
||||
std::move(anchorId),
|
||||
allowEmpty || state->editMode);
|
||||
}
|
||||
|
||||
void WrapPreparedIvRichTextItalic(PreparedIvRichText *prepared) {
|
||||
if (!prepared || prepared->text.text.isEmpty()) {
|
||||
return;
|
||||
@@ -217,6 +176,7 @@ bool AppendPreparedQuoteParagraph(
|
||||
}
|
||||
if (pullquote && (result->size() > count)) {
|
||||
result->back().flowAlignment = TableAlignment::Center;
|
||||
result->back().pullquote = true;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
@@ -513,10 +473,20 @@ void MarkNativeIvTableSlots(
|
||||
state->editMode)) {
|
||||
return false;
|
||||
}
|
||||
if (!data.blocks.empty()
|
||||
&& !PrepareNativeIvBlocks(
|
||||
RichPage{
|
||||
.blocks = data.blocks,
|
||||
},
|
||||
&block.children,
|
||||
state)) {
|
||||
return false;
|
||||
}
|
||||
if (block.children.empty()) {
|
||||
block.children.push_back(EmptyParagraphBlock());
|
||||
if (data.pullquote) {
|
||||
block.children.back().flowAlignment = TableAlignment::Center;
|
||||
block.children.back().pullquote = true;
|
||||
}
|
||||
}
|
||||
result->push_back(std::move(block));
|
||||
@@ -578,7 +548,9 @@ void MarkNativeIvTableSlots(
|
||||
state->editMode)) {
|
||||
return false;
|
||||
}
|
||||
} else if (!PrepareNativeIvBlocks(
|
||||
}
|
||||
if (!item.blocks.empty()
|
||||
&& !PrepareNativeIvBlocks(
|
||||
RichPage{
|
||||
.blocks = item.blocks,
|
||||
},
|
||||
@@ -767,7 +739,7 @@ void MarkNativeIvTableSlots(
|
||||
auto block = PreparedBlock();
|
||||
block.kind = PreparedBlockKind::Details;
|
||||
block.anchorId = std::move(anchorId);
|
||||
block.collapsed = !data.open;
|
||||
block.collapsed = state->editMode ? false : !data.open;
|
||||
block.text = std::move(summary.text);
|
||||
block.links = std::move(summary.links);
|
||||
block.anchorIds = std::move(summary.anchorIds);
|
||||
|
||||
@@ -292,6 +292,35 @@ void AppendCanonicalNativeIvRichText(
|
||||
state);
|
||||
}
|
||||
|
||||
[[nodiscard]] bool PrepareNativeIvCanonicalPlaceholderBlock(
|
||||
QString label,
|
||||
const Iv::RichPage::RichText &caption,
|
||||
QString anchorId,
|
||||
std::vector<PreparedBlock> *result,
|
||||
NativeIvPrepareState *state) {
|
||||
auto prepared = PreparedIvRichText();
|
||||
if (!PrepareNativeIvCaption(caption, &prepared, &anchorId, state)) {
|
||||
return state->result.failure.failed()
|
||||
? false
|
||||
: PrepareNativeIvPlainPlaceholderBlock(std::move(label), result);
|
||||
}
|
||||
SortPreparedIvRichText(&prepared);
|
||||
auto block = PreparedBlock();
|
||||
block.kind = PreparedBlockKind::Placeholder;
|
||||
block.text = std::move(prepared.text);
|
||||
block.links = std::move(prepared.links);
|
||||
block.anchorId = std::move(anchorId);
|
||||
block.anchorIds = std::move(prepared.anchorIds);
|
||||
block.supplementary = true;
|
||||
block.forceTextSegment = state->editMode;
|
||||
block.placeholder.label = std::move(label);
|
||||
block.placeholder.copyText = block.text.text.isEmpty()
|
||||
? block.placeholder.label
|
||||
: (block.placeholder.label + u"\n"_q + block.text.text);
|
||||
result->push_back(std::move(block));
|
||||
return true;
|
||||
}
|
||||
|
||||
} // namespace
|
||||
|
||||
bool PrepareNativeIvRichText(
|
||||
@@ -351,7 +380,14 @@ bool PrepareNativeIvPhotoBlock(
|
||||
if (!CanonicalPhotoId(data)
|
||||
|| CanonicalWidth(data) <= 0
|
||||
|| CanonicalHeight(data) <= 0) {
|
||||
return true;
|
||||
return state->editMode
|
||||
? PrepareNativeIvCanonicalPlaceholderBlock(
|
||||
u"Photo"_q,
|
||||
data.caption,
|
||||
data.anchorId,
|
||||
result,
|
||||
state)
|
||||
: true;
|
||||
}
|
||||
auto caption = PreparedIvRichText();
|
||||
auto anchorId = QString();
|
||||
@@ -366,6 +402,7 @@ bool PrepareNativeIvPhotoBlock(
|
||||
block.anchorId = data.anchorId.isEmpty() ? std::move(anchorId) : data.anchorId;
|
||||
block.anchorIds = std::move(caption.anchorIds);
|
||||
block.supplementary = true;
|
||||
block.forceTextSegment = state->editMode;
|
||||
block.photo.id = GeneratePreparedMediaBlockId(state);
|
||||
block.photo.photoId = CanonicalPhotoId(data);
|
||||
block.photo.width = CanonicalWidth(data);
|
||||
@@ -384,7 +421,14 @@ bool PrepareNativeIvVideoBlock(
|
||||
if (!CanonicalDocumentId(data)
|
||||
|| CanonicalWidth(data) <= 0
|
||||
|| CanonicalHeight(data) <= 0) {
|
||||
return true;
|
||||
return state->editMode
|
||||
? PrepareNativeIvCanonicalPlaceholderBlock(
|
||||
u"Video"_q,
|
||||
data.caption,
|
||||
data.anchorId,
|
||||
result,
|
||||
state)
|
||||
: true;
|
||||
}
|
||||
auto caption = PreparedIvRichText();
|
||||
auto anchorId = QString();
|
||||
@@ -399,6 +443,7 @@ bool PrepareNativeIvVideoBlock(
|
||||
block.anchorId = data.anchorId.isEmpty() ? std::move(anchorId) : data.anchorId;
|
||||
block.anchorIds = std::move(caption.anchorIds);
|
||||
block.supplementary = true;
|
||||
block.forceTextSegment = state->editMode;
|
||||
block.video.id = GeneratePreparedMediaBlockId(state);
|
||||
block.video.media.kind = PreparedMediaItemKind::Document;
|
||||
block.video.media.id = CanonicalDocumentId(data);
|
||||
@@ -414,7 +459,14 @@ bool PrepareNativeIvAudioBlock(
|
||||
std::vector<PreparedBlock> *result,
|
||||
NativeIvPrepareState *state) {
|
||||
if (!CanonicalDocumentId(data)) {
|
||||
return true;
|
||||
return state->editMode
|
||||
? PrepareNativeIvCanonicalPlaceholderBlock(
|
||||
u"Audio"_q,
|
||||
data.caption,
|
||||
data.anchorId,
|
||||
result,
|
||||
state)
|
||||
: true;
|
||||
}
|
||||
auto caption = PreparedIvRichText();
|
||||
auto anchorId = QString();
|
||||
@@ -429,6 +481,7 @@ bool PrepareNativeIvAudioBlock(
|
||||
block.anchorId = data.anchorId.isEmpty() ? std::move(anchorId) : data.anchorId;
|
||||
block.anchorIds = std::move(caption.anchorIds);
|
||||
block.supplementary = true;
|
||||
block.forceTextSegment = state->editMode;
|
||||
block.audio.id = GeneratePreparedMediaBlockId(state);
|
||||
block.audio.documentId = CanonicalDocumentId(data);
|
||||
block.audio.title = data.audioTitle;
|
||||
@@ -444,7 +497,14 @@ bool PrepareNativeIvMapBlock(
|
||||
std::vector<PreparedBlock> *result,
|
||||
NativeIvPrepareState *state) {
|
||||
if (!data.accessHash || data.width <= 0 || data.height <= 0) {
|
||||
return true;
|
||||
return state->editMode
|
||||
? PrepareNativeIvCanonicalPlaceholderBlock(
|
||||
u"Map"_q,
|
||||
data.caption,
|
||||
data.anchorId,
|
||||
result,
|
||||
state)
|
||||
: true;
|
||||
}
|
||||
auto caption = PreparedIvRichText();
|
||||
auto anchorId = QString();
|
||||
@@ -459,6 +519,7 @@ bool PrepareNativeIvMapBlock(
|
||||
block.anchorId = data.anchorId.isEmpty() ? std::move(anchorId) : data.anchorId;
|
||||
block.anchorIds = std::move(caption.anchorIds);
|
||||
block.supplementary = true;
|
||||
block.forceTextSegment = state->editMode;
|
||||
block.map.id = GeneratePreparedMediaBlockId(state);
|
||||
block.map.latitude = data.latitude;
|
||||
block.map.longitude = data.longitude;
|
||||
|
||||