Compare commits
1438 Commits
Author | SHA1 | Date |
---|---|---|
|
fecbab8d69 | |
|
fea45b2d5b | |
|
ab9fcbf6ee | |
|
f3982618e7 | |
|
ce8c2dc22c | |
|
34d1d0ac48 | |
|
4ee9c47ddc | |
|
6b124b6f55 | |
|
54567bd4f6 | |
|
a0a980712e | |
|
9e8bda22a7 | |
|
e8a4562744 | |
|
bd420fed73 | |
|
1b05846892 | |
|
17486fd175 | |
|
ee3dbefcf6 | |
|
73742a0210 | |
|
28755bdbc0 | |
|
6247f94a74 | |
|
ab58d0053c | |
|
88a3267cdd | |
|
425c920952 | |
|
c9ae167d90 | |
|
1761f99e81 | |
|
7fb9cfae21 | |
|
50f019abef | |
|
d62e767056 | |
|
6d1e1d7f26 | |
|
e8daaf659e | |
|
9c164a2c5c | |
|
73e4e58c80 | |
|
0b319032b6 | |
|
fbdd84ac86 | |
|
d08ab7d260 | |
|
6e915321cc | |
|
52f07b0533 | |
|
acc0586fd6 | |
|
3f815718b1 | |
|
3037d7b87f | |
|
69bfbbf19c | |
|
102693e1be | |
|
36594e3759 | |
|
5492d34bce | |
|
a0ef6f6ca7 | |
|
c0c9a3ae92 | |
|
9af82be7f7 | |
|
ac26cf7521 | |
|
c2eb806992 | |
|
9b483d972d | |
|
0969f43196 | |
|
b85c5c542a | |
|
851bbfe74f | |
|
93383cd45a | |
|
46459dc8b2 | |
|
31a91d6fc0 | |
|
8b908aedc1 | |
|
2dcdbe02c9 | |
|
87bdb4e736 | |
|
30a363baf1 | |
|
9dc7a2bc7f | |
|
85544275d0 | |
|
dffdb63f01 | |
|
9660c15fd1 | |
|
b2c44efd7f | |
|
d53d68226d | |
|
3813e520b5 | |
|
b6ba770c12 | |
|
9fad02d689 | |
|
f7d6d2db89 | |
|
ef87e00543 | |
|
8b9fa82616 | |
|
cebfec8c2c | |
|
e33612e1c9 | |
|
4e3a69c5f3 | |
|
c2f79d98c5 | |
|
9f1b19eb93 | |
|
a0ce3f1793 | |
|
3129cd7b46 | |
|
416f4b4f70 | |
|
a564d66304 | |
|
d8781c961f | |
|
08abc9972c | |
|
16849b0a57 | |
|
8634048aba | |
|
f6022bdcc0 | |
|
e98a50b51c | |
|
1372d0d128 | |
|
92f0163563 | |
|
574971c58d | |
|
b2d3d7f29d | |
|
9c62325367 | |
|
2ee12f0bcb | |
|
e66257a2b5 | |
|
6b2453ab4d | |
|
6f26d869f2 | |
|
6e688b6476 | |
|
5452f8258f | |
|
60687e6aff | |
|
efd7e8d07d | |
|
23a0a041f0 | |
|
f7c1515ae1 | |
|
5ded8398ba | |
|
29b9e820da | |
|
d994dcb0b0 | |
|
4ec1a642a2 | |
|
58cdf151a5 | |
|
98e814f013 | |
|
e9792a2216 | |
|
21591e4f6f | |
|
651d502624 | |
|
d3c553c424 | |
|
a8f643c09c | |
|
40100a17d1 | |
|
1500c750b6 | |
|
21d4ad54b6 | |
|
26449f444f | |
|
31adbb419e | |
|
6ba124ba83 | |
|
8d90ae1dc9 | |
|
08096e13a4 | |
|
c1a0e2e3b6 | |
|
f49253cbf8 | |
|
7e30a2afc1 | |
|
2584e82191 | |
|
0c410022bb | |
|
988ca49e41 | |
|
963f230196 | |
|
20409b7599 | |
|
fec8042cfa | |
|
58753f8478 | |
|
2c73c4f175 | |
|
318bc1437d | |
|
8be1b9603a | |
|
494962b330 | |
|
7692103f7f | |
|
f929365b6e | |
|
a9fb246d74 | |
|
7082146f9e | |
|
aa0753ffc7 | |
|
47948df5de | |
|
15ffbeaa8a | |
|
77193006b9 | |
|
dbed420d32 | |
|
ef7a341ec7 | |
|
cd2d5ea78e | |
|
7a108adc4a | |
|
ef7b698205 | |
|
5ec9a2fbc0 | |
|
a6b48771d5 | |
|
04964839f2 | |
|
15856e15da | |
|
91e7473d00 | |
|
57b11c7ce8 | |
|
31970127b6 | |
|
8a090bd90b | |
|
994a37e336 | |
|
7e83ba9ffe | |
|
07dfea4a4f | |
|
d280aa40cb | |
|
b480050550 | |
|
7d22d84584 | |
|
f23ba94d70 | |
|
d6c30352ee | |
|
60df014315 | |
|
9889477a25 | |
|
e1abd02ed2 | |
|
4afebf4d8e | |
|
466f542bd3 | |
|
30a8cbf8b1 | |
|
514b8ecbc6 | |
|
0da0240875 | |
|
ce3fa7485f | |
|
68901322d4 | |
|
6e0a8915af | |
|
9f814dd47f | |
|
af03601fde | |
|
5ad39011ce | |
|
7dfc11f645 | |
|
0ad8c85de0 | |
|
151ee9a52e | |
|
8586ca35cb | |
|
2aea38b446 | |
|
0b6b68c6a1 | |
|
54e90aaafd | |
|
bca7dacd31 | |
|
07f04f1cc8 | |
|
f7907f38c6 | |
|
ff3d39213e | |
|
35750c41fe | |
|
5c0f542222 | |
|
31ca852cef | |
|
629074d269 | |
|
223972f87b | |
|
5c8dd4e7c3 | |
|
4023e1bba3 | |
|
257536103f | |
|
7ea8c33547 | |
|
53c2fe9d00 | |
|
d62775ad02 | |
|
81e25f76f2 | |
|
70fe716d06 | |
|
922dca4f1e | |
|
65b97b6836 | |
|
e1d6f35ef0 | |
|
99f2b8f5ad | |
|
f6ef61cdee | |
|
8b37a98170 | |
|
6a5fedf3f4 | |
|
f65caac1ce | |
|
beba7470f4 | |
|
b3232cc50c | |
|
77f3b41940 | |
|
c940b85b84 | |
|
fec83e72bf | |
|
0d9cfa0f68 | |
|
0b87ee9f93 | |
|
9527bb73ca | |
|
1b9250e41e | |
|
87f2be6849 | |
|
c449ab7da9 | |
|
3457082b1b | |
|
467a9102ec | |
|
c1dfc0ab5d | |
|
2be9a4f3a3 | |
|
5e557d61b3 | |
|
dff8ec0f22 | |
|
cdd997b6f6 | |
|
1deacb55aa | |
|
253ce9a3cc | |
|
035139a1e9 | |
|
a440b174f7 | |
|
7063ed6e32 | |
|
1a9b0881bc | |
|
355b5d05d3 | |
|
f8543fc641 | |
|
751ce562bb | |
|
65a0c5958e | |
|
39d4542de3 | |
|
853d97aecb | |
|
e753ae9b57 | |
|
7ce5070eeb | |
|
e8db7f9bf9 | |
|
d305440fa0 | |
|
f1d9e36a0e | |
|
a03754512f | |
|
04891a99ab | |
|
dabf2e6b93 | |
|
b95dd8fab5 | |
|
c65865d5c2 | |
|
acc0e3e5ea | |
|
000f699ba6 | |
|
0d9e3c4b67 | |
|
38e5d23891 | |
|
97609af771 | |
|
6490cdadb7 | |
|
ae8a3939ae | |
|
df5d144dbd | |
|
c25831ec66 | |
|
a5fb198558 | |
|
7138276ccf | |
|
3d4b959632 | |
|
d9a851169e | |
|
447dc24651 | |
|
c49151a4dc | |
|
b82d1b0a3b | |
|
9e6baf0a61 | |
|
d15dfa06a8 | |
|
836e0dc2c7 | |
|
71ddf6ce91 | |
|
24e8649c87 | |
|
7e34fac79d | |
|
83ba0b1a41 | |
|
fcec8693ca | |
|
38723aeff9 | |
|
ba9c42e5bb | |
|
760a7c09f5 | |
|
359c08aa11 | |
|
c216d8953d | |
|
ddd6afbf36 | |
|
79d1d58af2 | |
|
1d94e95e83 | |
|
9d31dddd13 | |
|
fc2ef2c904 | |
|
a5aa4624df | |
|
87d3fc5064 | |
|
6a22c61cd4 | |
|
d082b4efa4 | |
|
91fe6e7c47 | |
|
97b11e027d | |
|
fa4f5d78cb | |
|
30ee36f8d2 | |
|
30d4129a38 | |
|
186a92f6e9 | |
|
6891017cbe | |
|
e2222e414d | |
|
7268f2c78f | |
|
2606235a74 | |
|
db9977a4ee | |
|
869992010c | |
|
e098cb5585 | |
|
f065abedd7 | |
|
abeb0144d4 | |
|
9f89071db3 | |
|
dcf6a2c178 | |
|
c552c4bcae | |
|
1692d5d465 | |
|
76edc665b7 | |
|
6a513038f4 | |
|
2872e65db0 | |
|
419c584523 | |
|
9120d6b93f | |
|
5d959e5cbc | |
|
52bd45bb09 | |
|
24df7bc3f2 | |
|
ad1641f9ab | |
|
ea1fde52ad | |
|
b3de605d50 | |
|
ecf9efd6d7 | |
|
1f4d7dc374 | |
|
c33ea32e0f | |
|
2df2d565f9 | |
|
075d5efb61 | |
|
1a8e5555da | |
|
f47e1423cf | |
|
1c3d7be227 | |
|
9202aeee2a | |
|
dee35216ac | |
|
cd38f77490 | |
|
976279ef33 | |
|
68dade3e81 | |
|
c0742d3de0 | |
|
e0ca00e55b | |
|
e7305349f3 | |
|
4eaf01f840 | |
|
b60953ed1c | |
|
3c44d332da | |
|
6812cab625 | |
|
a37fdadbd7 | |
|
095e63d522 | |
|
1700c0ccea | |
|
5e2b8cb6ae | |
|
ce0168e1a5 | |
|
37bd4fc5a1 | |
|
bd197684aa | |
|
5d9928028f | |
|
bd685f24f9 | |
|
4ed6e3ec8a | |
|
f3882d6124 | |
|
acb40964f3 | |
|
fb67c429d8 | |
|
72c7c05f04 | |
|
4dbf1994a6 | |
|
9b340ed40c | |
|
8f4598f641 | |
|
74ec21f4c7 | |
|
9a79eed2ac | |
|
ac27e24add | |
|
888bb450b3 | |
|
7d17df7121 | |
|
d0f7cf81c5 | |
|
c2000ea54d | |
|
4dc7f4c2de | |
|
5854b0f638 | |
|
ccb7005dc6 | |
|
f180423096 | |
|
5ae45a1fcb | |
|
ec07b12295 | |
|
df59eee783 | |
|
bff9d0e311 | |
|
25a443f4f2 | |
|
ff4b424ab0 | |
|
4c107997d2 | |
|
1edb406045 | |
|
d81b365396 | |
|
51f98d1f7b | |
|
799c613a6f | |
|
6d0b276d6d | |
|
2fb3676a8f | |
|
db2f3352c4 | |
|
7bc121b1ec | |
|
9fba903c4a | |
|
a00ca05136 | |
|
301376706f | |
|
c7555dcfd3 | |
|
c978f6fece | |
|
8769bea3a0 | |
|
f96c0f410e | |
|
d63af885ba | |
|
bcab521311 | |
|
b9289c93a6 | |
|
4bfd950caa | |
|
f92c83992e | |
|
f1341a0485 | |
|
f852359acb | |
|
43bb983f5b | |
|
d6cebf4ca0 | |
|
32ee36eb22 | |
|
e6da33121e | |
|
fda8f2fbae | |
|
75063ec266 | |
|
c79f9b4ecd | |
|
caa0688687 | |
|
15bc55dd5e | |
|
c01b010fd7 | |
|
c4251894b5 | |
|
f216af94e7 | |
|
f28c97d4d6 | |
|
e52373c59a | |
|
59d8e81b85 | |
|
0f89418a95 | |
|
8f0019af10 | |
|
c843f1e62d | |
|
0c6e7add01 | |
|
dfb3bd39f1 | |
|
cd2a6f7c29 | |
|
2a2990f19c | |
|
94a3397894 | |
|
5a5f9ba9ac | |
|
8dd9f08ba4 | |
|
c22f643df7 | |
|
a3feb425a1 | |
|
8d43b55ff8 | |
|
405a6defd2 | |
|
4c5f0961fe | |
|
0f59bf7399 | |
|
b43fb4b247 | |
|
a858300f25 | |
|
3aee505f4e | |
|
ff64685655 | |
|
1c9f9627c0 | |
|
c6c4dbd580 | |
|
06a8044c2c | |
|
69e91a9678 | |
|
71a310909d | |
|
41d63bd0cf | |
|
516eb312fc | |
|
3aa16e844d | |
|
ca2115ca57 | |
|
0adad7743a | |
|
7a40bdfb3f | |
|
66ca3315cf | |
|
d6ac22a20a | |
|
b7151388e3 | |
|
18d8a8fe22 | |
|
75a5a050cb | |
|
94fa10b35e | |
|
7a5577db94 | |
|
3c063c71d5 | |
|
0a6a781be7 | |
|
3fa15d5277 | |
|
8c611cf95d | |
|
71e02a5c6e | |
|
8a0ca54e93 | |
|
2cfbb0144e | |
|
3bb050777b | |
|
2b428d50f6 | |
|
8fc4a9aaf5 | |
|
6457e1668f | |
|
247d63be60 | |
|
da2a4c1e36 | |
|
8ee5c61fe8 | |
|
b74b987fff | |
|
54ca41afb6 | |
|
c04f3d3fbe | |
|
43b1a46669 | |
|
705bf2d9bf | |
|
9c519f1bfa | |
|
d1b7d14a82 | |
|
5d57666764 | |
|
2f83428ebe | |
|
3e5f4b3674 | |
|
773856fa90 | |
|
c4ce1072c7 | |
|
7911ed0e81 | |
|
cda991b7e2 | |
|
1b4c898a0c | |
|
1cb0e9046f | |
|
0907ea47bd | |
|
2f0e6e974b | |
|
246abee8a4 | |
|
61dbbdbf96 | |
|
1530192673 | |
|
bba2d30362 | |
|
32840cd88c | |
|
5bd78c1932 | |
|
9020b248e2 | |
|
0e7f866531 | |
|
374eed7432 | |
|
a7fe45b8a5 | |
|
32a83241cb | |
|
b3b21196a1 | |
|
039a9db2e5 | |
|
d4517fab84 | |
|
b590482ccb | |
|
7e2c505b1d | |
|
cb5b6bf924 | |
|
c3be9f221c | |
|
ba13227bed | |
|
ea5bd79b89 | |
|
eef179c1e5 | |
|
a1a20837cd | |
|
620b03f442 | |
|
cf945367a3 | |
|
4a7e7ed983 | |
|
8231bd337f | |
|
bd79a2fa8f | |
|
481e1e30bb | |
|
ef9e08d4ad | |
|
8f5eba45bd | |
|
a07727515f | |
|
a6634f775b | |
|
472476392c | |
|
ca11bf1e42 | |
|
43c2f26de3 | |
|
8b35ab973d | |
|
a5e07be2f5 | |
|
c38564a8f7 | |
|
b769b37189 | |
|
4246ae5549 | |
|
7402dec266 | |
|
fc2ea17bb8 | |
|
d09be0d5fc | |
|
fb745e80e3 | |
|
20483669b2 | |
|
0da7b3fceb | |
|
6c1a7b2b9b | |
|
483c037d41 | |
|
f55d34636b | |
|
bb3066e3f5 | |
|
cc477d58f8 | |
|
919ad1f16e | |
|
422ec83162 | |
|
cfb51cb1b9 | |
|
634ca8470d | |
|
abc489095e | |
|
96f2bf2603 | |
|
4c8af90d64 | |
|
e4852c17b7 | |
|
b463590289 | |
|
dc62cbd27f | |
|
d4c41e0270 | |
|
d6ced62aab | |
|
911ef405c7 | |
|
4748828a2e | |
|
26e5843083 | |
|
3f1b6b3a49 | |
|
18817c7633 | |
|
4e9f498dd3 | |
|
9d74f9f930 | |
|
a0367201c9 | |
|
02b7ae90f1 | |
|
4245120f6a | |
|
9aef70b980 | |
|
2b1c43c642 | |
|
858a497978 | |
|
c4b1e1b237 | |
|
fb19345e10 | |
|
8be9195302 | |
|
31090442be | |
|
02fc3e267e | |
|
e00a91ded4 | |
|
6533c0be32 | |
|
9dad806693 | |
|
ca1079f344 | |
|
d02784838e | |
|
8874532302 | |
|
f7ea901460 | |
|
fd3b693ffe | |
|
9aba97714d | |
|
266c633972 | |
|
f3d0fee21a | |
|
edd4127f8e | |
|
0b436a8c74 | |
|
0ac4ffdc87 | |
|
594e3a1ccc | |
|
5ec5ebdfae | |
|
0baf2d2eb6 | |
|
cf671c1da5 | |
|
57b225a629 | |
|
04ce39a941 | |
|
3e9bdd26f1 | |
|
bc14042935 | |
|
86798f72b1 | |
|
2768fef3ce | |
|
0b8e86e4f8 | |
|
fd65a065c6 | |
|
f647dda850 | |
|
1b23cd4e72 | |
|
af192eedab | |
|
241d8b59f0 | |
|
add92694bf | |
|
e0a2fc5841 | |
|
70fd8cbf8e | |
|
afaec644cc | |
|
1c83d674a5 | |
|
5ac579df35 | |
|
3b5ef559f5 | |
|
221e8731c0 | |
|
dc98bc2c25 | |
|
55023a5f99 | |
|
f1c4976fe0 | |
|
7be597f674 | |
|
1e5f1b8254 | |
|
fb60d5860e | |
|
98fe8736b5 | |
|
fdff9b2dbb | |
|
3914451c34 | |
|
69922a0fb3 | |
|
cc271ccc37 | |
|
323a8945eb | |
|
b8fc9fa817 | |
|
eb28beff1b | |
|
090c783147 | |
|
7b570a4026 | |
|
2f92f644b5 | |
|
1381edfa6e | |
|
60e71c7174 | |
|
b8316c61dd | |
|
5a113e1e0b | |
|
4fe74fbd56 | |
|
6a4a3e1b4d | |
|
0359c2fa5c | |
|
134acf6ec0 | |
|
7ee7de1b92 | |
|
4432e39a44 | |
|
e3e88c7a6a | |
|
3522ec02b3 | |
|
a4cb17820c | |
|
b975cfb83e | |
|
c6979fa738 | |
|
e11196304f | |
|
88882592ac | |
|
10de22f9ac | |
|
17ec9562ea | |
|
b9fd9fc395 | |
|
2be8fd3eb3 | |
|
55f2eba45b | |
|
f88c23602e | |
|
5cc85c2609 | |
|
3f421f9816 | |
|
e61f7fb0cb | |
|
591b9c8772 | |
|
83b04cda47 | |
|
cac4cd9e81 | |
|
85a4c6d35d | |
|
ab336e0e8b | |
|
bd2f436d91 | |
|
6507a0d3a1 | |
|
d441455112 | |
|
ae2eea7975 | |
|
9cf6c3faf5 | |
|
3539cda280 | |
|
52dc39a553 | |
|
4df47a6ee8 | |
|
5622b13c23 | |
|
f921dde9f6 | |
|
fa7466e7b4 | |
|
ee986363c8 | |
|
d99549ec18 | |
|
c9d11a85cc | |
|
4e6adca08c | |
|
b1210165a0 | |
|
68e1223e4d | |
|
5dbe20593d | |
|
27b8ce1db3 | |
|
750f4214b7 | |
|
7cd88ddd03 | |
|
b3df05eb8e | |
|
fcc26ab9a9 | |
|
c3992cc647 | |
|
8e2004cf67 | |
|
01d6422664 | |
|
24a4b0997c | |
|
0dddf4a490 | |
|
f5be05f143 | |
|
59835a9bae | |
|
f93973d3cb | |
|
c6bb463692 | |
|
099c4ff631 | |
|
8ca32f098f | |
|
72e8b9a198 | |
|
4a57b15d09 | |
|
e0f800d5eb | |
|
f3fcf9918b | |
|
0f8c59ef49 | |
|
03d0d4074a | |
|
a24085bd5e | |
|
8e7bd22d6c | |
|
a098f7f78b | |
|
34fd48ae0d | |
|
ceec25f061 | |
|
1266a77e61 | |
|
d53ab65589 | |
|
e45c7b4d87 | |
|
177527c959 | |
|
696459be85 | |
|
e244a0edf4 | |
|
a7c58c2c2e | |
|
84ff1890c5 | |
|
51f72abba8 | |
|
6d8150d6a4 | |
|
93789560ce | |
|
6667cf2f51 | |
|
242620ff0f | |
|
3e76d799b1 | |
|
8d6e41e199 | |
|
9cf56e1674 | |
|
c402dcac30 | |
|
df03c09a9c | |
|
d788d86239 | |
|
a0d2d6a1f8 | |
|
8944a3fc55 | |
|
9aea7fa62e | |
|
bc895d6707 | |
|
0125e33720 | |
|
93f0eea140 | |
|
8709838a8f | |
|
78e0684435 | |
|
70321353a1 | |
|
fa293717af | |
|
d358e87583 | |
|
5d116d87fe | |
|
45d11962b1 | |
|
a5bbc83359 | |
|
2f02836181 | |
|
51952b4599 | |
|
cdaf49ec30 | |
|
e08460adc3 | |
|
8352cd72b8 | |
|
1d1b6c3162 | |
|
1b1d609c88 | |
|
5314295e4c | |
|
5282951681 | |
|
f25b8dce37 | |
|
12e97a1b29 | |
|
abc6fd2825 | |
|
86264d32a0 | |
|
1f61fb30de | |
|
6c95ce849a | |
|
e020ba465a | |
|
bffe311afe | |
|
602060a673 | |
|
697c926c92 | |
|
5939c3203d | |
|
24d2220fe5 | |
|
7dc5c6d940 | |
|
1cce6dc704 | |
|
008be9b6d8 | |
|
acbe7c3149 | |
|
6ba2d3606d | |
|
6643a148e0 | |
|
5ef04f2675 | |
|
b15f88412b | |
|
1fae3eea02 | |
|
427e013d78 | |
|
8fda407183 | |
|
4bb6a56c99 | |
|
115a60c950 | |
|
9194ba29fe | |
|
c82197f408 | |
|
82d3de7c31 | |
|
e9a62461c2 | |
|
9661d14262 | |
|
5535c43bd8 | |
|
7451d455e5 | |
|
bf58601ff8 | |
|
00749b07ac | |
|
93c375fd94 | |
|
9e57c78fb3 | |
|
49c3f77d46 | |
|
232b14ccde | |
|
1fc9f137bc | |
|
0b3176cead | |
|
d8ad3300c9 | |
|
36567cc522 | |
|
7fd012418d | |
|
036c7c12e6 | |
|
32041a2ecc | |
|
9c8f6849c3 | |
|
77c07075e3 | |
|
f025783632 | |
|
766f3a0cb2 | |
|
3337f06e2e | |
|
dcf6726239 | |
|
2822dbcebc | |
|
4179ff86c2 | |
|
6c29932efe | |
|
70db51d23b | |
|
b5570040b0 | |
|
d0db6f9594 | |
|
8d92003426 | |
|
010e878286 | |
|
cf15dba2ef | |
|
5ab1266b5d | |
|
70815d8d7b | |
|
3f62cb5cee | |
|
dcaf69bc51 | |
|
886f1dbc4c | |
|
20a94937b3 | |
|
7891cd81c2 | |
|
8b35d16144 | |
|
93bf0d403e | |
|
eeb7717d88 | |
|
6a737ba48e | |
|
ec8952a8e8 | |
|
e768293b54 | |
|
a245ced3dc | |
|
77ba614948 | |
|
dd54b3da93 | |
|
6c85f39584 | |
|
ee87a9db23 | |
|
17f1499246 | |
|
a74923d574 | |
|
686458cdb7 | |
|
cbc78248e7 | |
|
84756ab0ec | |
|
4b7ca92ce7 | |
|
bad2325323 | |
|
e33879a283 | |
|
1f33ca14d8 | |
|
ca5a5f1f72 | |
|
d44c20c4a1 | |
|
243e210cbc | |
|
84d49cd45b | |
|
67a7b64f56 | |
|
6869f28718 | |
|
ad34745327 | |
|
e0b584082d | |
|
2276a136cb | |
|
8333063cc0 | |
|
72d31285c1 | |
|
51f0e3dbe5 | |
|
4e7fb5e264 | |
|
be6a07755b | |
|
57a6ea0a77 | |
|
f2971f637f | |
|
b7e6311621 | |
|
2e5f2c273b | |
|
830753d888 | |
|
03b089c972 | |
|
6b295b2d3f | |
|
a7bbbc8bb9 | |
|
3108f65b39 | |
|
2481610ee4 | |
|
b769b81639 | |
|
0a1b5ff6a8 | |
|
48594e3cd1 | |
|
78bfc62399 | |
|
1673156fd6 | |
|
1851fc8045 | |
|
e2369df648 | |
|
e186c0e69a | |
|
b2b5bc36b1 | |
|
e5e0dca360 | |
|
e3deebaa1d | |
|
3b316ddb5b | |
|
2858c56528 | |
|
9b28bd5ecd | |
|
9731d2e836 | |
|
f94de2eef8 | |
|
683bd47491 | |
|
9f1e1b25be | |
|
4d57a12dbc | |
|
eab8a98aa9 | |
|
98e06716b6 | |
|
55a97093f5 | |
|
e597d8ce3b | |
|
cb5f9b3c9c | |
|
c8c51502cb | |
|
7b89fb21a1 | |
|
21c99859e4 | |
|
2702f4f874 | |
|
b98888a628 | |
|
40bc1505ce | |
|
2bea99116d | |
|
271a3bc022 | |
|
180b6429ef | |
|
5d7128781b | |
|
3e20e4670c | |
|
e6ee2b0dbd | |
|
4610a5a363 | |
|
cc8a412687 | |
|
fd7c436b8a | |
|
6b989f80ca | |
|
c3568d5162 | |
|
c8f4ccc712 | |
|
85c39c6cb3 | |
|
8728dcb5c0 | |
|
39530a0a2e | |
|
0c7ca9a346 | |
|
fcbf706559 | |
|
99e65d49a7 | |
|
f0813ac6e4 | |
|
d63a5b23b1 | |
|
b73721905f | |
|
cde1a201f4 | |
|
20239df6f2 | |
|
39a8d1a07e | |
|
dbb17e441d | |
|
ef3ed04e2d | |
|
ddf91df764 | |
|
b3e3b27f71 | |
|
a415a846f7 | |
|
8e8cc12105 | |
|
ba2e9154f7 | |
|
8c850a26b2 | |
|
aa455be1ae | |
|
18d67a016c | |
|
96d06121c8 | |
|
8cacd0ba03 | |
|
933a8f592a | |
|
2118259769 | |
|
98459d2878 | |
|
b47431cd7c | |
|
1e31ef24c6 | |
|
1d40ab659c | |
|
2e98219009 | |
|
c61bbfe5c8 | |
|
81a82723d0 | |
|
01df4631f6 | |
|
06becce34c | |
|
91bfceaf71 | |
|
c4dd85bece | |
|
e356f1c48a | |
|
1733c28b52 | |
|
d979f7a93e | |
|
02020e676a | |
|
475ae4a4bf | |
|
57873bae72 | |
|
a17985b4cd | |
|
fb884abc41 | |
|
dd54469677 | |
|
90962e18c4 | |
|
bd39189a0e | |
|
b7d34bf4f5 | |
|
1a2dd1f16b | |
|
72f5b680f2 | |
|
39cec570d9 | |
|
ff4ab7110c | |
|
4f427b2121 | |
|
3e28887a24 | |
|
4f31a1331c | |
|
2f73df09e3 | |
|
4e6ccc05f1 | |
|
676dfb87f2 | |
|
a0454a6b43 | |
|
df2a726c1b | |
|
70940e5c06 | |
|
3d5fbc0880 | |
|
c4400b83f3 | |
|
58fff53f61 | |
|
5234586ead | |
|
5fe4e27c5d | |
|
28b30367e2 | |
|
cca4db5ce9 | |
|
f6e9da916a | |
|
ce1a54a9d1 | |
|
0db03e5f44 | |
|
0df8346bff | |
|
074350bf99 | |
|
f65fc1f25e | |
|
be56ec3626 | |
|
e5f3646fcf | |
|
0f2b2b1fe5 | |
|
f2b158b529 | |
|
b42250e3a9 | |
|
fb04feaebb | |
|
ae8105c234 | |
|
9cc8b03516 | |
|
efb7d5b21d | |
|
c111239b0d | |
|
75c2c80534 | |
|
4ba4e4ba23 | |
|
4a1642cea2 | |
|
d4b38cd3b7 | |
|
d7a44a5168 | |
|
ea473a3411 | |
|
6ae6e91238 | |
|
7920559aa5 | |
|
f65d4592d0 | |
|
fbd294c089 | |
|
025dd16d76 | |
|
e27e51c4b8 | |
|
92b860c8f4 | |
|
685e17fc0e | |
|
768aba6614 | |
|
4262d893bc | |
|
dcc01f5b01 | |
|
3e941d008e | |
|
1eb2ffaf82 | |
|
4f5328ae65 | |
|
ddffce6d44 | |
|
6761c0a9f7 | |
|
669538e9ce | |
|
85fd3b72af | |
|
81b520249b | |
|
bda35b40bd | |
|
da22adac7f | |
|
16b0ca518e | |
|
2ffcd192c0 | |
|
e4b761d611 | |
|
4195bc3b4b | |
|
eb7dfa5cc4 | |
|
250cba7c67 | |
|
aa7d1e28ea | |
|
9c1293736b | |
|
8aa8259250 | |
|
fbae38ce2e | |
|
e3b1e033b2 | |
|
35a06e8540 | |
|
9a19c65323 | |
|
ff986858f1 | |
|
1c238bf85b | |
|
9eba197fd1 | |
|
40c388e01d | |
|
b2cd5ef851 | |
|
44f24fc900 | |
|
261dd4851a | |
|
1c1b16a206 | |
|
5aaac84d55 | |
|
32a0284edc | |
|
de638f3e76 | |
|
c4a97792ea | |
|
210c4507ca | |
|
44f8409b8c | |
|
bd66e70452 | |
|
8a64a1dfb0 | |
|
491ec3ae49 | |
|
dca92d507e | |
|
1a8f7ad405 | |
|
a014eb27e6 | |
|
67949d79aa | |
|
bebaf72de3 | |
|
49094bb340 | |
|
230d23765a | |
|
c4b6149ec2 | |
|
5a1c733841 | |
|
6acb10a4fc | |
|
5f05ebbffe | |
|
5354aa5262 | |
|
5d18d477e3 | |
|
746e4091cb | |
|
b5b0fae052 | |
|
642363ccae | |
|
b52600d9cd | |
|
0e5be32299 | |
|
5823c3a53e | |
|
bb50440e55 | |
|
4b46dab775 | |
|
8ac9e386b5 | |
|
6fa009a7dd | |
|
49d36b340c | |
|
92d996f148 | |
|
b96114e02d | |
|
d53bc88f50 | |
|
5458e10134 | |
|
749a0cddaf | |
|
93537ebb83 | |
|
012ce92b71 | |
|
3e4f99e0dd | |
|
01b6a16af6 | |
|
bbc03a489e | |
|
a5dac81855 | |
|
ac9aefc43a | |
|
e4ed1c337b | |
|
3d8bf01972 | |
|
c539362c15 | |
|
71176c2e33 | |
|
675e7594da | |
|
a1bebc82d8 | |
|
64a1167e5f | |
|
0ae25c62ed | |
|
b18cc88dce | |
|
c336063b65 | |
|
bb52275686 | |
|
ea93528cba | |
|
9063b10691 | |
|
7614d3e0ff | |
|
c9e7ee3a92 | |
|
8ff01c5034 | |
|
8d3c68d7f0 | |
|
877ffa5f89 | |
|
d1e2b17f6e | |
|
1ca9a28f2d | |
|
08f2085f41 | |
|
b19f4cf251 | |
|
4e2dc90f4f | |
|
b2ce7c8794 | |
|
7eba767ffb | |
|
313905230d | |
|
d8316790a0 | |
|
f0b9ff9aba | |
|
4de8d4402f | |
|
351b3b6077 | |
|
bbed231324 | |
|
c720e8147b | |
|
339bdfc89b | |
|
87b7d5b4b7 | |
|
fc7fcd9f05 | |
|
6ccf436206 | |
|
6ef6ed8cde | |
|
2d09f22932 | |
|
88ea7c4665 | |
|
c0781a13ae | |
|
cac390a821 | |
|
f7121774e1 | |
|
5444f76bd3 | |
|
7f270942a7 | |
|
907d7c5830 | |
|
7bda3baee3 | |
|
794d30154c | |
|
e63a42a290 | |
|
e32341b24b | |
|
1bd2ca22c2 | |
|
6212ac7238 | |
|
747a0bbfad | |
|
3910ff1a13 | |
|
dfaf640101 | |
|
f147edf949 | |
|
bab798543f | |
|
4318785eb2 | |
|
31274bcbd2 | |
|
30ca879c14 | |
|
e409943a50 | |
|
dfffc4c851 | |
|
4f6ed98e40 | |
|
5c1dfbd86f | |
|
ab8aa9266e | |
|
0078615662 | |
|
ecc7b70a6b | |
|
28ca02673b | |
|
4f1ba0df84 | |
|
643e2937c4 | |
|
78f5ca5eb5 | |
|
fee06b9553 | |
|
7ebfe73bce | |
|
96e15116ba | |
|
5c96266c7c | |
|
75525196b1 | |
|
4c99899a1d | |
|
2f449887d8 | |
|
722dd03193 | |
|
98082068f6 | |
|
6726ab70a9 | |
|
3f48bddce0 | |
|
e9fdbf33f3 | |
|
0503110ddd | |
|
cb8d75d431 | |
|
b45e5f4de2 | |
|
4f8f94b85a | |
|
da8b8de371 | |
|
3218ba2a43 | |
|
526173bf76 | |
|
0d5ff432b3 | |
|
c0f91058c4 | |
|
5fa7ba487e | |
|
39d1c0565e | |
|
cb927659fa | |
|
dd339699cd | |
|
18b103c988 | |
|
52d88d3f36 | |
|
e3dec183aa | |
|
287d59b5c5 | |
|
6b33c62b84 | |
|
92965068e1 | |
|
f56411fde1 | |
|
17dc5f407e | |
|
81b95a59b1 | |
|
503e5d7768 | |
|
7199d4c847 | |
|
fae0bb061b | |
|
4c429d5b7f | |
|
0311679e37 | |
|
3eeb15bcdb | |
|
d3b33e39ce | |
|
211b11b80f | |
|
6779ae91d7 | |
|
a1d14b8773 | |
|
39fdf7457b | |
|
8cbae7d2b7 | |
|
5de62d073f | |
|
cc1942a929 | |
|
8819d9cd58 | |
|
9d754bbf2a | |
|
7fbbba37f6 | |
|
7abe97ec57 | |
|
1fbfc9dd67 | |
|
22caa5e502 | |
|
67ff2fa855 | |
|
1871f7139d | |
|
8556fc7cd1 | |
|
fc243aed00 | |
|
0c209a8277 | |
|
3f1540b84a | |
|
e17e69f2ad | |
|
0870a3065e | |
|
ce37d12d23 | |
|
57379474f1 | |
|
57fbf78ef3 | |
|
edd70b9fa5 | |
|
6d796b5642 | |
|
570b794650 | |
|
d344e1ae29 | |
|
1369f53027 | |
|
8b3ce21886 | |
|
f46edd097b | |
|
bfa781f1c2 | |
|
229ed226f6 | |
|
abbea5273f | |
|
a9c7ff9c6c | |
|
e133903809 | |
|
15b069bb47 | |
|
05f7e2d3cf | |
|
ba082864e1 | |
|
084ed53e63 | |
|
4bb9b58ec6 | |
|
2d5166c117 | |
|
0b5d4021f2 | |
|
8964dff296 | |
|
33e5defdb5 | |
|
86c4485578 | |
|
bc670c0b5f | |
|
3286c9b572 | |
|
6e45859b9b | |
|
7324683ba2 | |
|
013659e805 | |
|
bea844c18e | |
|
61bcb0d536 | |
|
5f4e5ed8eb | |
|
ef689c10f1 | |
|
e20ce4e188 | |
|
9164e521e2 | |
|
4fabbd18cf | |
|
621e301d5f | |
|
6b0028d084 | |
|
f484f568f6 | |
|
575f4f3053 | |
|
8b04f2062a | |
|
53bd751461 | |
|
cf3182f4d2 | |
|
af0dbd3a0e | |
|
40e943eb43 | |
|
99a3462eb4 | |
|
f7e5c7dd25 | |
|
f354d48bfd | |
|
6804f263d4 | |
|
d7caa150b8 | |
|
44a723b314 | |
|
15eca1372e | |
|
2f9a38b5fe | |
|
e0230d73a6 | |
|
49ab4a886f | |
|
93bbf39aa4 | |
|
165b3a8a51 | |
|
7995a4460c | |
|
2da1273ec2 | |
|
da24b7154b | |
|
4914d0c64c | |
|
9d22d741c6 | |
|
78e8dd3883 | |
|
67e8db9efd | |
|
f02fbd2ecf | |
|
a57bef13e5 | |
|
cbb07113ca | |
|
ddd0e82c90 | |
|
0ef01e1685 | |
|
a82835fff3 | |
|
93923bbcb3 | |
|
1c70edaef1 | |
|
c26f438d3b | |
|
2f08ec683a | |
|
b21ec6cf6b | |
|
aa2e8cf4f5 | |
|
d93bb2c48f | |
|
1f20772b46 | |
|
2d4f6eb2b8 | |
|
10480db895 | |
|
80b8fddbea | |
|
ec15e25694 | |
|
a07e2afc9c | |
|
f2c0719e95 | |
|
087a89dac9 | |
|
9785565c8c | |
|
9656a81c77 | |
|
605b09f2be | |
|
d36ef03630 | |
|
ebdcf7bf17 | |
|
3499e8335b | |
|
b95bbbd70b | |
|
f17483ccd2 | |
|
721a934bf0 | |
|
8c332591c9 | |
|
fca6edd5a2 | |
|
ae14542c9a | |
|
cc4c5f7bd5 | |
|
99545e8775 | |
|
e1a5d140db | |
|
015008976f | |
|
798061f7f6 | |
|
179042025a | |
|
c001c164f1 | |
|
d4492c06a9 | |
|
da4b396aaf | |
|
b4d0e08a22 | |
|
dd2ee50710 | |
|
256a452fbd | |
|
377f46814a | |
|
aafdb75a98 | |
|
132d925b70 | |
|
fd9af04123 | |
|
69cafde7f3 | |
|
cff5dcf9c9 | |
|
7770b5c850 | |
|
caec39281b | |
|
88bb697bbd | |
|
e5064de86b | |
|
6c56f1e1ce | |
|
5fbe85c21a | |
|
c3777da0f4 | |
|
48c13c738a | |
|
983a53d3c3 | |
|
f98318fb3a | |
|
560cca5bc9 | |
|
53bec81925 | |
|
4465d37d2e | |
|
e07a7b3d05 | |
|
6270af8cf9 | |
|
efe207ca37 | |
|
dda58c9323 | |
|
157a312e8a | |
|
ac1a515820 | |
|
cea52e2df6 | |
|
e017c22409 | |
|
e75b9cc4fa | |
|
3173fc6cdc | |
|
831fe1e73b | |
|
578ff39525 | |
|
16b4409f38 | |
|
c60cbb72f9 | |
|
c8dd546804 | |
|
4b2d646345 | |
|
106d4b544a | |
|
cf8c1c67fe | |
|
cdbe48cc83 | |
|
0a4a8ae4f3 | |
|
96ce336a8b | |
|
8ae0823eee | |
|
b817a16c05 | |
|
52bd3b2c35 | |
|
b6cb429d1a | |
|
997c8aac35 | |
|
6e5cf97623 | |
|
cb81518e0f | |
|
44b4fa9c68 | |
|
d54f7dd682 | |
|
48546f6b44 | |
|
207340f16f | |
|
fa4542f217 | |
|
4524ffce5e | |
|
8c974e7b77 | |
|
cb3f10f243 | |
|
75a8ee9bf9 | |
|
2affdab837 | |
|
864c6c2511 | |
|
155299cdb9 | |
|
440635447d | |
|
8d48d42201 | |
|
b26354d1e7 | |
|
857bbbf506 | |
|
e46554f481 | |
|
ba8e5b80ea | |
|
f08cd96688 | |
|
41ec54cb47 | |
|
9f4092dab5 | |
|
144366c775 | |
|
2cd97c8e60 | |
|
99e660377c | |
|
f89feba680 | |
|
629c242a83 | |
|
0ce4fd6efd | |
|
7908972d34 | |
|
77f4c2f309 | |
|
ebf4bce245 | |
|
a2e5dbc94c | |
|
6496de0fb2 | |
|
8f0bac54a3 | |
|
31f2b6d059 | |
|
2c08f52a5b | |
|
bf1595795c | |
|
fc2218b51c | |
|
9650770554 | |
|
faa989aefd | |
|
d528fae1f4 | |
|
a18e3c71b9 | |
|
61a94fde57 | |
|
662c9de179 | |
|
554221fd66 | |
|
3a3b96a38e | |
|
4b149ddfef | |
|
075eae15e5 | |
|
04b83a2680 | |
|
ddbd6c2133 | |
|
06ae417cb3 | |
|
3fad148200 | |
|
c96068316a | |
|
afe27177d5 | |
|
4b2858b53a | |
|
69cad5e608 | |
|
d232342779 | |
|
45d394fc81 | |
|
9295af59ab | |
|
2f3c0449ba | |
|
0fe5214c09 | |
|
0fa706b8d4 | |
|
958893a73d | |
|
4fde812156 | |
|
39f76bdb25 | |
|
95fcceef2f | |
|
7f05b796aa | |
|
ff3c9f0c67 | |
|
c6e8537269 | |
|
1ad18ddc8e | |
|
3e1258cc62 | |
|
9a5bc738c5 | |
|
8ede507d36 | |
|
40128080da | |
|
5accda32e5 | |
|
8d660431d7 | |
|
852d2ee143 | |
|
6cb3cd4ed7 | |
|
f1e3ecf786 | |
|
fe2d85590e | |
|
812086be1b | |
|
15456c8b41 | |
|
3d5d1408c7 | |
|
4e7d2812bd | |
|
d0be2979e4 | |
|
9c3a8f9a44 |
|
@ -0,0 +1,92 @@
|
|||
version: 'build #{build}'
|
||||
|
||||
environment:
|
||||
matrix:
|
||||
- arch: 386
|
||||
libtype: shared
|
||||
libfiles: libui.dll libui.lib
|
||||
compiler: msvc2013
|
||||
APPVEYOR_BUILD_WORKER_IMAGE: Visual Studio 2013
|
||||
- arch: 386
|
||||
libtype: static
|
||||
libfiles: libui.lib
|
||||
compiler: msvc2013
|
||||
APPVEYOR_BUILD_WORKER_IMAGE: Visual Studio 2013
|
||||
- arch: amd64
|
||||
libtype: shared
|
||||
libfiles: libui.dll libui.lib
|
||||
compiler: msvc2013
|
||||
APPVEYOR_BUILD_WORKER_IMAGE: Visual Studio 2013
|
||||
- arch: amd64
|
||||
libtype: static
|
||||
libfiles: libui.lib
|
||||
compiler: msvc2013
|
||||
APPVEYOR_BUILD_WORKER_IMAGE: Visual Studio 2013
|
||||
- arch: 386
|
||||
libtype: static
|
||||
libfiles: libui.lib
|
||||
compiler: mingw
|
||||
APPVEYOR_BUILD_WORKER_IMAGE: Visual Studio 2015
|
||||
- arch: amd64
|
||||
libtype: static
|
||||
libfiles: libui.lib
|
||||
compiler: mingw
|
||||
APPVEYOR_BUILD_WORKER_IMAGE: Visual Studio 2015
|
||||
|
||||
platform:
|
||||
- x64
|
||||
|
||||
# Note: AppVeyor tries to be "helpful" and splits cmd.exe scripts into their constitutent lines to check their error codes. There is no way to switch this off; for true multi-line scripts we have to use PowerShell. But we need to use vcvarsall.bat, so that's out of the question.
|
||||
install:
|
||||
# Set Python Version
|
||||
- set "PYTHON_ROOT=C:\python37-x64"
|
||||
- if %arch%==386 ( set "PYTHON_ROOT=C:\python37" )
|
||||
- set "PATH=%PYTHON_ROOT%;%PYTHON_ROOT%\Scripts;%PATH%"
|
||||
# Install Latest Meson
|
||||
- pip install meson
|
||||
# Install Ninja
|
||||
- powershell -Command "Invoke-WebRequest https://github.com/ninja-build/ninja/releases/download/v1.9.0/ninja-win.zip -OutFile C:\ninja-win.zip"
|
||||
- mkdir C:\ninja
|
||||
- powershell -Command "Expand-Archive -LiteralPath C:\ninja-win.zip -DestinationPath C:\ninja"
|
||||
- set "PATH=C:\ninja;%PATH%"
|
||||
# Parameters for the build_script phase, to reduce their noise.
|
||||
- set "mingwPath=C:\msys64\mingw64\bin"
|
||||
- set vcvarsallArch=x86
|
||||
- if %arch%==386 ( set "mingwPath=C:\msys64\mingw32\bin" )
|
||||
- if %arch%==386 ( set vcvarsallArch=amd64 )
|
||||
|
||||
build_script:
|
||||
- if %compiler%==mingw ( set "PATH=%mingwPath%;%PATH%" )
|
||||
- if not %compiler%==mingw ( call "C:\Program Files (x86)\Microsoft Visual Studio 12.0\VC\vcvarsall.bat" %vcvarsallArch% )
|
||||
- meson setup build --buildtype=release --default-library=%libtype%
|
||||
- ninja -C build
|
||||
|
||||
after_build:
|
||||
- set "version=%APPVEYOR_REPO_BRANCH%"
|
||||
- if %APPVEYOR_REPO_TAG%==true ( set "version=%APPVEYOR_REPO_TAG_NAME%" )
|
||||
- set "artifact=%version%-windows-%arch%-%compiler%-%libtype%"
|
||||
- cd build\meson-out
|
||||
# TODO msvc only?
|
||||
- if %libtype%==static ( ren libui.a libui.lib )
|
||||
- copy ..\..\ui.h .
|
||||
- copy ..\..\ui_windows.h .
|
||||
# remove unnecessary files
|
||||
# TODO should we do this on Azure too?
|
||||
- del libui.exp
|
||||
- 7z a "%APPVEYOR_BUILD_FOLDER%\libui-%artifact%.zip" %libfiles% ui.h ui_windows.h
|
||||
- 7z a "%APPVEYOR_BUILD_FOLDER%\examples-%artifact%.zip" controlgallery.exe cpp-multithread.exe datetime.exe drawtext.exe histogram.exe tester.exe timer.exe
|
||||
- del ui.h ui_windows.h
|
||||
|
||||
artifacts:
|
||||
- path: libui-*.zip
|
||||
name: libui
|
||||
- path: examples-*.zip
|
||||
name: examples
|
||||
|
||||
deploy:
|
||||
provider: GitHub
|
||||
artifact: libui, examples
|
||||
auth_token:
|
||||
secure: li92W7mFAC8HbAVeZN6Ugmo5H1GzKSjr6DXlMniLcCRspKmi2Nz1nlslSa+9sLfo
|
||||
on:
|
||||
appveyor_repo_tag: true # deploy on tag push only
|
18
.travis.yml
18
.travis.yml
|
@ -1,18 +0,0 @@
|
|||
os:
|
||||
- linux
|
||||
- osx
|
||||
|
||||
# This makes us use Ubuntu 14 instead of 12
|
||||
dist: trusty
|
||||
|
||||
language: c
|
||||
script:
|
||||
- if [ "$TRAVIS_OS_NAME" == "linux" ]; then sudo apt-get update; fi
|
||||
- if [ "$TRAVIS_OS_NAME" == "linux" ]; then sudo apt-get install libgtk-3-dev -y || sudo apt-cache search libgtk3; fi
|
||||
- mkdir build
|
||||
- cd build
|
||||
- cmake .. -G "Unix Makefiles"
|
||||
- make tester examples
|
||||
- rm -rf *
|
||||
- cmake .. -G "Unix Makefiles" -DBUILD_SHARED_LIBS=OFF
|
||||
- make tester examples
|
213
CMakeLists.txt
213
CMakeLists.txt
|
@ -1,213 +0,0 @@
|
|||
# 3 june 2016
|
||||
cmake_minimum_required(VERSION 2.8.11)
|
||||
|
||||
# TODOs
|
||||
# - silence entering/leaving messages?
|
||||
# - uname -s for more refined OS control
|
||||
# - Haiku for haiku
|
||||
# - debian DESTDIR? https://github.com/andlabs/libui/pull/10
|
||||
|
||||
# the docs say we need to set this up prior to project()
|
||||
set(CMAKE_OSX_DEPLOYMENT_TARGET "10.8")
|
||||
|
||||
# we want to disable incremental linking
|
||||
# see also:
|
||||
# - https://github.com/bulletphysics/bullet3/blob/master/CMakeLists.txt#L43
|
||||
# - https://cmake.org/pipermail/cmake/2010-February/035174.html
|
||||
# this must also go before project()
|
||||
set(MSVC_INCREMENTAL_DEFAULT ON)
|
||||
|
||||
# default to debug builds
|
||||
# do this before project() just to be safe
|
||||
if(NOT CMAKE_BUILD_TYPE)
|
||||
set(CMAKE_BUILD_TYPE DEBUG CACHE STRING "" FORCE)
|
||||
endif()
|
||||
|
||||
project(libui LANGUAGES C CXX)
|
||||
option(BUILD_SHARED_LIBS "Whether to build libui as a shared library or a static library" ON)
|
||||
|
||||
set(CMAKE_RUNTIME_OUTPUT_DIRECTORY "${PROJECT_BINARY_DIR}/out")
|
||||
set(CMAKE_LIBRARY_OUTPUT_DIRECTORY "${PROJECT_BINARY_DIR}/out")
|
||||
set(CMAKE_ARCHIVE_OUTPUT_DIRECTORY "${PROJECT_BINARY_DIR}/out")
|
||||
set(CMAKE_PDB_OUTPUT_DIRECTORY "${PROJECT_BINARY_DIR}/out")
|
||||
|
||||
if(APPLE)
|
||||
set(_OSNAME darwin)
|
||||
set(_HASVERSION TRUE)
|
||||
set(_VERSION "A")
|
||||
|
||||
# always use our rpath
|
||||
set(CMAKE_BUILD_WITH_INSTALL_RPATH TRUE)
|
||||
# the / is required by some older versions of OS X
|
||||
set(CMAKE_INSTALL_RPATH "@executable_path/")
|
||||
set(CMAKE_MACOSX_RPATH TRUE)
|
||||
elseif(WIN32)
|
||||
set(_OSNAME windows)
|
||||
|
||||
# and don't include the default libraries with ANY of the builds
|
||||
# note the CACHE FORCE stuff is required here
|
||||
set(CMAKE_C_STANDARD_LIBRARIES CACHE STRING "" FORCE)
|
||||
set(CMAKE_CXX_STANDARD_LIBRARIES CACHE STRING "" FORCE)
|
||||
else()
|
||||
set(_OSNAME unix)
|
||||
set(_HASVERSION TRUE)
|
||||
set(_VERSION "0")
|
||||
|
||||
# always use our rpath
|
||||
set(CMAKE_BUILD_WITH_INSTALL_RPATH TRUE)
|
||||
set(CMAKE_INSTALL_RPATH "\$ORIGIN")
|
||||
endif()
|
||||
|
||||
# common flags
|
||||
if(MSVC)
|
||||
# TODO subsystem version
|
||||
|
||||
# TODO /Wall does too much
|
||||
# TODO -Wno-switch equivalent
|
||||
# TODO /sdl turns C4996 into an ERROR
|
||||
# don't use /analyze; that requires us to write annotations everywhere
|
||||
# TODO undecided flags from qo?
|
||||
# /RTCc is not supplied because it's discouraged as of VS2015; see https://www.reddit.com/r/cpp/comments/46mhne/rtcc_rejects_conformant_code_with_visual_c_2015/d06auq5
|
||||
# /EHsc is to shut the compiler up in some cases
|
||||
# TODO make /EHsc C++-only
|
||||
set(_COMMON_CFLAGS
|
||||
/W4 /wd4100
|
||||
/bigobj /nologo
|
||||
/RTC1 /RTCs /RTCu
|
||||
/EHsc
|
||||
)
|
||||
|
||||
# note the /MANIFEST:NO (which must be / and uppercase); thanks FraGag (https://github.com/andlabs/libui/issues/93#issuecomment-223183436)
|
||||
# TODO warnings on undefined symbols
|
||||
set(_COMMON_LDFLAGS
|
||||
/LARGEADDRESSAWARE
|
||||
/NOLOGO
|
||||
/INCREMENTAL:NO
|
||||
/MANIFEST:NO
|
||||
)
|
||||
|
||||
# TODO autogenerate a .def file?
|
||||
|
||||
# more incremental linking fixes
|
||||
# TODO actually get rid of incremental linking here
|
||||
else()
|
||||
set(_COMMON_CFLAGS
|
||||
-Wall -Wextra -pedantic
|
||||
-Wno-unused-parameter
|
||||
-Wno-switch
|
||||
-fvisibility=hidden
|
||||
)
|
||||
# don't use C_VERSION or CXX_VERSION because they use GNU standards
|
||||
set(CMAKE_C_FLAGS "${CMAKE_C_FLAGS} --std=c99")
|
||||
set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} --std=c++11")
|
||||
|
||||
set(_COMMON_LDFLAGS
|
||||
-fvisibility=hidden
|
||||
)
|
||||
|
||||
# don't require shipping the MinGW-w64 DLLs
|
||||
if(WIN32)
|
||||
list(APPEND _COMMON_LDFLAGS
|
||||
-static
|
||||
-static-libgcc
|
||||
-static-libstdc++
|
||||
)
|
||||
endif()
|
||||
endif()
|
||||
|
||||
# problem:
|
||||
# - target_link_libraries() only supports - for flags
|
||||
# - but cmake only doesn't generate the manifest if the flag has a /
|
||||
macro(_target_link_options_private _target)
|
||||
foreach(_opt IN LISTS ${ARGN})
|
||||
set_property(TARGET ${_target} APPEND_STRING PROPERTY
|
||||
LINK_FLAGS " ${_opt}")
|
||||
endforeach()
|
||||
endmacro()
|
||||
|
||||
add_subdirectory("common")
|
||||
add_subdirectory("${_OSNAME}")
|
||||
add_library(${_LIBUINAME} ${_LIBUI_SOURCES})
|
||||
target_include_directories(${_LIBUINAME}
|
||||
PUBLIC .
|
||||
PRIVATE ${_LIBUI_INCLUEDIRS})
|
||||
target_compile_definitions(${_LIBUINAME}
|
||||
PRIVATE ${_LIBUI_DEFS})
|
||||
# cmake produces this for us by default but only for shared libraries
|
||||
target_compile_definitions(${_LIBUINAME}
|
||||
PRIVATE libui_EXPORTS)
|
||||
target_compile_options(${_LIBUINAME}
|
||||
PUBLIC ${_COMMON_CFLAGS}
|
||||
PRIVATE ${_LIBUI_CFLAGS})
|
||||
# TODO link directories?
|
||||
# because we need 2.8.11 for CentOS, we can't use target_link_libraries(INTERFACE) for static executables :(
|
||||
if(BUILD_SHARED_LIBS)
|
||||
target_link_libraries(${_LIBUINAME}
|
||||
PRIVATE ${_LIBUI_LIBS})
|
||||
endif()
|
||||
# on Windows the linker for static libraries is different; don't give it the flags
|
||||
if(BUILD_SHARED_LIBS)
|
||||
_target_link_options_private(${_LIBUINAME}
|
||||
_COMMON_LDFLAGS
|
||||
_LIBUI_LDFLAGS)
|
||||
endif()
|
||||
if(NOT BUILD_SHARED_LIBS)
|
||||
_handle_static()
|
||||
# TODO figure out a way to tell libui that it's static
|
||||
target_compile_definitions(${_LIBUINAME}
|
||||
PUBLIC _UI_STATIC)
|
||||
endif()
|
||||
if(NOT MSVC)
|
||||
# on non-MSVC compilers cmake adds an extra lib-
|
||||
# note that we apply this to libui, not to any intermediates
|
||||
set_target_properties(libui PROPERTIES
|
||||
OUTPUT_NAME ui)
|
||||
|
||||
# flags for warning on undefined symbols
|
||||
# TODO figure out why FreeBSD follows linked libraries here
|
||||
# TODO figure out MSVC equivalents
|
||||
if(BUILD_SHARED_LIBS)
|
||||
if(NOT (${CMAKE_SYSTEM_NAME} STREQUAL FreeBSD))
|
||||
# on OS X we don't need to do this; Apple's linker warns about undefined symbols in -shared builds!
|
||||
if(NOT APPLE)
|
||||
target_link_libraries(libui
|
||||
PRIVATE -Wl,--no-undefined -Wl,--no-allow-shlib-undefined
|
||||
)
|
||||
endif()
|
||||
endif()
|
||||
endif()
|
||||
endif()
|
||||
if(BUILD_SHARED_LIBS)
|
||||
if(_HASVERSION)
|
||||
set_target_properties(${_LIBUINAME} PROPERTIES
|
||||
SOVERSION "${_VERSION}")
|
||||
endif()
|
||||
endif()
|
||||
|
||||
macro(_add_exec _name)
|
||||
add_executable(${_name}
|
||||
WIN32 EXCLUDE_FROM_ALL
|
||||
${ARGN})
|
||||
target_link_libraries(${_name} libui ${_LIBUI_STATIC_RES})
|
||||
_target_link_options_private(${_name}
|
||||
_COMMON_LDFLAGS)
|
||||
# make shared-linked executables PIC too
|
||||
if(BUILD_SHARED_LIBS)
|
||||
set_property(TARGET ${_name} PROPERTY
|
||||
POSITION_INDEPENDENT_CODE True)
|
||||
endif()
|
||||
# because we need 2.8.11 for CentOS, we can't use target_link_libraries(PUBLIC) for static executables :(
|
||||
if(NOT BUILD_SHARED_LIBS)
|
||||
target_link_libraries(${_name} ${_LIBUI_LIBS})
|
||||
endif()
|
||||
|
||||
# TODOfor some reason these don't propagate
|
||||
if(NOT WIN32)
|
||||
target_include_directories(${_name}
|
||||
PUBLIC .)
|
||||
target_compile_options(${_name}
|
||||
PUBLIC ${_COMMON_CFLAGS})
|
||||
endif()
|
||||
endmacro()
|
||||
add_subdirectory("test")
|
||||
add_subdirectory("examples")
|
|
@ -0,0 +1,131 @@
|
|||
# Contributing to libui
|
||||
|
||||
libui is an open source project that openly accepts contributions. I appreciate your help!
|
||||
|
||||
## Rules for contributing code
|
||||
|
||||
While libui is open to contributions, a number of recent, significantly large contributions and uncontributed forks have recently surfaced that do not present themselves in a form that makes it easy for libui to accept them. In order to give your contribution a high chance of being accepted into libui, please keep the following in mind as you prepare your contribution.
|
||||
|
||||
### Commit messages and pull request description
|
||||
|
||||
libui does not enforce rules about the length or detail that a commit message. I'm not looking for an essay. However, single-word descriptions of nontrivial changes are *not* acceptable. I should be able to get a glimpse of what a commit does from the commit message, even if it's just one sentence to describe a trivial change. (Yes, I know I haven't followed this rule strictly myself, but I try not to break it too.) And a commit message should encompass everything; typically, I make a number of incremental commits toward a feature, so the commit messages don't have to be too long to explain everything.
|
||||
|
||||
Your pull request description, on the other hand, must be a summary of the sum total of all the changes made to libui. Don't just drop a pull request on me with a one-line-long elevator pitch of what you added. Describe your proposed API changes, implementation requirements, and any important consequences of your work.
|
||||
|
||||
### Code formatting
|
||||
|
||||
libui uses K&R C formatting rules for overall code structure: spaces after keywords like `if`, `{` on the same line as a statement with a space, `{` on its own line after a function or method signature (even those inside the class body), no space after the name of a function, etc.
|
||||
|
||||
Use hard tabs, NOT spaces, for indentation. I use a proportional-width font and my text editor doesn't set tabs to a multiple of the space width, so I *will* be able to tell. If you use a fixed-width font, I suggest setting a tab width of 4 spaces per tab, but don't put diagrams in comments with hard tabs, because not everyone does this.
|
||||
|
||||
Expressions should have a space around binary operators, and use parentheses where it would help humans gather the meaning of an expression, regardless of whether a computer could tell what is correct.
|
||||
|
||||
When breaking expressions into multiple lines, always break *after* an operator, such as `,` or `&&`.
|
||||
|
||||
There should be a newline between a function's variables and a function's code. After that, you can place newlines to delimit different parts of a function, but don't go crazy.
|
||||
|
||||
In the event you are unsure of something, refer to existing libui code for examples. I may wind up fixing minor details later anyway, so don't fret about getting minor details right the first time.
|
||||
|
||||
### Naming
|
||||
|
||||
libui uses camel-case for naming, with a handful of very specific exceptions (namely GObject method names, where GObject itself enforces the naming convention).
|
||||
|
||||
All public API names should begin with `ui` and followed by a capital letter. All public struct field names should begin with a capital letter. This is identical to the visibiilty rules of Go, assuming a package name of `ui`.
|
||||
|
||||
Private API names — specifcally those used by more than one source file — should begin with `uipriv` and be followed by a capital letter. This avoids namespace collisions in static libraries.
|
||||
|
||||
Static functions and static objects do not have naming restrictions.
|
||||
|
||||
Acronyms should **NOT** be mixed-case. `http` for the first word in a camel-case name, `HTTP` for all else, but **NEVER** `Http`. This is possibly the only aspect of the controversial nature of code style that I consider indefensibly stupid.
|
||||
|
||||
### API documentation
|
||||
|
||||
(TODO I am writing an API documentation tool; once that becomes stable enough I can talk about documenting libui properly. You'll see vestiges of it throughout ui.h, though.)
|
||||
|
||||
### Other commenting
|
||||
|
||||
(TODO write this part)
|
||||
|
||||
### Compatibility
|
||||
|
||||
libui takes backward compatibility seriously. Your code should not break the current compatibility requirements. All platforms provide a series of macros, defined in the various `uipriv_*.h` files (or `winapi.hpp` on Windows), that specify the minimum required version. If you find yourself needing to remove these or ignore resultant warnings or errors, you're probably breaking compatibility.
|
||||
|
||||
Choosing to drop older versions of Windows, GTK+, and OS X that I could have easily continued to support was not done lightly. If you want to discuss dropping support for an older version of any of these for the benefit of libui, file an issue pleading your case (see below).
|
||||
|
||||
GTK+ versions are harder to drop because I am limited by Linux distribution packaging. In general, I will consider bumping GTK+ versions on a new Ubuntu LTS release, choosing the earliest version available on the major distributions at the time of the *previous* Ubuntu LTS release. As of writing, the next milestone will be *after* April 2018, and the target GTK+ version appears to be 3.18, judging by Ubuntu 16.04 LTS alone. This may be bumped back depending on other distros (or it may not be bumped at all), but you may wish to keep this in mind as you write.
|
||||
|
||||
(TODO talk about future.c/.cpp/.m files)
|
||||
|
||||
As for language compatibility, libui is written in C99. I have no intention of changing this.
|
||||
|
||||
As for build system compatibility, libui uses CMake 3.1.0. If you wish to bump the version, file an issue pleading your case (but see below).
|
||||
|
||||
**If you do plead your case**, keep in mind that "it's old" is not a sufficient reason to drop things. If you can prove that **virtually no one** uses the minimum version anymore, then that is stronger evidence. The best evidence, however, is that not upgrading will hold libui back in some significant way — but beware that there are some things I won't add to libui itself.
|
||||
|
||||
### Windows-specific notes
|
||||
|
||||
The Windows backend of libui is written in C++ using C++11.
|
||||
|
||||
Despite using C++, please refrain from using the following:
|
||||
|
||||
- using C++ in ui_windows.h (this file should still be C compatible)
|
||||
- smart pointers
|
||||
- namespaces
|
||||
- `using namespace`
|
||||
- ATL, MFC, WTL
|
||||
|
||||
The following are not recommended, for consistency with the rest of libui:
|
||||
|
||||
- variable declarations anywhere in a function (keep them all at the top)
|
||||
- `for (int x...` (C++11 foreach syntax is fine, though)
|
||||
- omitting the `struct` on type names for ordinary structs
|
||||
|
||||
The format of a class should be
|
||||
|
||||
```c++
|
||||
class name : public ancestor {
|
||||
int privateVariable;
|
||||
// etc.
|
||||
public:
|
||||
// public stuff here
|
||||
};
|
||||
```
|
||||
|
||||
### GTK+-specific notes
|
||||
|
||||
Avoid GNU-specific language features. I build with strict C99 conformance.
|
||||
|
||||
### OS X-specific notes
|
||||
|
||||
Avoid GNU-specific/clang-specific language features. I build with strict C99 conformance.
|
||||
|
||||
libui is presently **not** ARC-compliant. Features that require ARC should be avoided for now. I may consider changing this in the future, but it will be a significant change.
|
||||
|
||||
To ensure maximum compiler output in the event of a coding error, there should not be any implicit method calls in Objective-C code. For instance, don't do
|
||||
|
||||
```objective-c
|
||||
[[array objectAtIndex:i] method]
|
||||
```
|
||||
|
||||
Instead, cast the result of `objectAtIndex:` to the appropriate type, and then call the method. (TODO learn about, then decide a policy on, soft-generics on things other than `id`)
|
||||
|
||||
The format of a class should be
|
||||
|
||||
```objective-c
|
||||
@interface name : parent<protocols> {
|
||||
// ivars
|
||||
}
|
||||
// properties
|
||||
- (ret)method:(int)arg;
|
||||
// more methods
|
||||
@end
|
||||
|
||||
@implementation name
|
||||
|
||||
- (ret)method:(int)arg
|
||||
{
|
||||
// note the lack of semicolon
|
||||
}
|
||||
|
||||
@end
|
||||
```
|
|
@ -0,0 +1,141 @@
|
|||
# Useful things in newer versions
|
||||
|
||||
## Windows
|
||||
### Windows 7
|
||||
http://channel9.msdn.com/blogs/pdc2008/pc43
|
||||
|
||||
TODO look up PDC 2008 talk "new shell user interface"
|
||||
|
||||
- new animation and text engine
|
||||
- ribbon control (didn't this have some additional license?)
|
||||
- LVITEM.piColFmt
|
||||
|
||||
### Windows 8
|
||||
|
||||
### Windows 8.1
|
||||
|
||||
### Windows 10
|
||||
|
||||
## GTK+
|
||||
TODO what ships with Ubuntu Quantal (12.10)?
|
||||
|
||||
### GTK+ 3.6
|
||||
ships with: Ubuntu Raring (13.04)
|
||||
|
||||
- GtkEntry and GtkTextView have input purposes and input hints for external input methods but do not change input themselves
|
||||
- according to Company, we connect to insert-text for that
|
||||
- GtkLevelBar
|
||||
- GtkMenuButton
|
||||
- **GtkSearchEntry**
|
||||
|
||||
### GTK+ 3.8
|
||||
ships with: Ubuntu Saucy (13.10)
|
||||
|
||||
Not many interesting new things to us here, unless you count widget-internal tickers and single-click instead of double-click to select list items (a la KDE)... and oh yeah, also widget opacity.
|
||||
|
||||
### GTK+ 3.10
|
||||
ships with: **Ubuntu Trusty (14.04 LTS)**
|
||||
<br>GLib version: 2.40
|
||||
|
||||
- tab character stops in GtkEntry
|
||||
- GtkHeaderBar
|
||||
- intended for titlebar overrides; GtkInfoBar is what I keep thinking GtkHeaderBar is
|
||||
- **GtkListBox**
|
||||
- GtkRevealer for smooth animations of disclosure triangles
|
||||
- GtkSearchBar for custom search popups
|
||||
- **GtkStack and GtkStackSwitcher**
|
||||
- titlebar overrides (seems to be the hot new thing)
|
||||
|
||||
### GTK+ 3.12
|
||||
ships with: Ubuntu Utopic (14.10)
|
||||
<br>GLib version: 2.42
|
||||
|
||||
- GtkActionBar (basically like the bottom-of-the-window toolbars in Mac programs)
|
||||
- gtk_get_locale_direction(), for internationalization
|
||||
- more control over GtkHeaderBar
|
||||
- **GtkPopover**
|
||||
- GtkPopovers on GtkMenuButtons
|
||||
- GtkStack signaling
|
||||
- **gtk_tree_path_new_from_indicesv()** (for when we add Table if we have trees too)
|
||||
|
||||
### GTK+ 3.14
|
||||
ships with: **Debian Jessie**, Ubuntu Vivid (15.04)
|
||||
<br>GLib version: Debian: 2.42, Ubuntu: 2.44
|
||||
|
||||
- gestures
|
||||
- better GtkListbox selection handling
|
||||
- more style classes (TODO also prior?)
|
||||
- delayed switch changes on GtkSwitch
|
||||
|
||||
### GTK+ 3.16
|
||||
ships with: Ubuntu Wily (15.10)
|
||||
<br>GLib version: 2.46
|
||||
|
||||
- gtk_clipboard_get_default() (???)
|
||||
- **GtkGLArea**
|
||||
- proper xalign and yalign for GtkLabel; should get rid of runtime deprecation warnings
|
||||
- better control of GtkListBox model-based creation (probably not relevant but)
|
||||
- GtkModelButton (for GActions; probably not relevant?)
|
||||
- wide handles on GtkPaned
|
||||
- GtkPopoverMenu
|
||||
- IPP paper names in GtkPaperSize (TODO will this be important for printing?)
|
||||
- multiple matches in GtkSearchEntry (TODO evaluate priority)
|
||||
- **GtkStackSidebar**
|
||||
- GTK_STYLE_CLASS_LABEL, GTK_STYLE_CLASS_MONOSPACE, GTK_STYLE_CLASS_STATUSBAR, GTK_STYLE_CLASS_TOUCH_SELECTION, GTK_STYLE_CLASS_WIDE (TODO figure out which of these are useful)
|
||||
- GtkTextView: extend-selection
|
||||
- GtkTextView: font fallbacks
|
||||
|
||||
### GTK+ 3.18
|
||||
|
||||
### GTK+ 3.20
|
||||
|
||||
## Cocoa
|
||||
### Mac OS X 10.8
|
||||
|
||||
- Foundation ([full details](https://developer.apple.com/library/mac/releasenotes/Foundation/RN-FoundationOlderNotes/#//apple_ref/doc/uid/TP40008080-TRANSLATED_CHAPTER_965-TRANSLATED_DEST_999B))
|
||||
- NSDateComponents supports leap months
|
||||
- NSNumberFormatter and NSDateFormatter default to 10.4 behavior by default (need to explicitly do this on 10.7)
|
||||
- **NSUserNotification and NSUserNotificationCenter for Growl-style notifications**
|
||||
- better linguistic triggers for Spanish and Italian
|
||||
- NSByteCountFormatter
|
||||
- AppKit ([full details](https://developer.apple.com/library/mac/releasenotes/AppKit/RN-AppKitOlderNotes/#X10_8Notes))
|
||||
- view-based NSTableView/NSOutlineView have expansion tooltips
|
||||
- NSScrollView magnification
|
||||
- Quick Look events; TODO see if they conflict with keyboard handling in Area
|
||||
- NSPageController (maybe useful?)
|
||||
- not useful for package UI, but may be useful for a new library (probably not by me): NSSharingService
|
||||
- NSOpenPanel and NSSavePanel are now longer NSPanels or NSWindows in sandboxed applications; this may be an issue should anyone dare to enable sandboxing on a program that uses package ui
|
||||
- NSTextAlternatives
|
||||
- -[NSOpenGLContext setFullScreen] now ineffective
|
||||
- +[NSColor underPageBackgroundColor]
|
||||
|
||||
### Mac OS X 10.9
|
||||
|
||||
- Foundation ([full details](https://developer.apple.com/library/mac/releasenotes/Foundation/RN-Foundation/))
|
||||
- system-provided progress reporting/cancellation support
|
||||
- NSURLComponents
|
||||
- **NSCalendar, NSDateFormatter, and NSNumberFormatter are now thread-safe**
|
||||
- various NSCalendar and NSDateComponents improvements
|
||||
- AppKit ([full details](https://developer.apple.com/library/mac/releasenotes/AppKit/RN-AppKit/))
|
||||
- sheet handling is now block-based, queued, and in NSWindow; the delegate-based NSApplication API will still exist, except without the queue
|
||||
- similar changes to NSAlert
|
||||
- **return value changes to NSAlert**
|
||||
- window visibility APIs (occlusion)
|
||||
- NSApplicationActivationPolicyAccessory
|
||||
- fullscreen toolbar behavior changes
|
||||
- status items for multiple menu bars
|
||||
- better NSSharingService support
|
||||
- a special accelerated scrolling mode, Responsive Scrolling; won't matter for us since I plan to support the scroll wheel and it won't
|
||||
- NSScrollView live scrolling notifications
|
||||
- NSScrollView floating (anchored/non-scrolling) subviews
|
||||
- better multimonitor support
|
||||
- better key-value observing for NSOpenPanel/NSSavePanel (might want to look this up to see if we can override some other juicy details... TODO)
|
||||
- better accessory view key-view handling in NSOpenPanel/NSSavePanel
|
||||
- NSAppearance
|
||||
- **-[NSTableView moveRowAtIndex:toIndex:] bug regarding first responders fixed**
|
||||
- view-specific RTL overrides
|
||||
|
||||
### Mac OS X 10.10
|
||||
|
||||
### Mac OS X 10.11
|
||||
* **NSLayoutGuide**
|
|
@ -0,0 +1,7 @@
|
|||
all:
|
||||
|
||||
redomod:
|
||||
rm -f go.*
|
||||
GO111MODULE= go mod init
|
||||
GO111MODULE= go mod tidy
|
||||
|
|
@ -0,0 +1,113 @@
|
|||
# Old News
|
||||
|
||||
* **27 November 2016**
|
||||
* Decided to split the table stuff into its own branch. It will be developed independently of everything else, along with a few other features.
|
||||
|
||||
* **2 November 2016**
|
||||
* Added two new functions to replace the deleted `uiWindowPosition()` and friends: `uiAreaBeginUserWindowMove()` and `uiAreaBeginUserWindowResize()`. When used in a `uiAreaHandler.Mouse()` event handler, these let you initiate a user-driven mouse move or mouse resize of the window at any point in a uiArea.
|
||||
|
||||
* **31 October 2016**
|
||||
* @krakjoe noticed that I accidentally used thread-unsafe code in uiQueueMain() on Unix. Fixed.
|
||||
|
||||
* **24 October 2016**
|
||||
* `uiWindowSetContentSize()` on Unix no longer needs to call up the GTK+ main loop. As a result, bugs related to strange behavior using that function (and the now-deleted `uiWindowSetPosition()` and `uiWindowCenter()`) should go away. I'll need to go through the bugs to verify as much, though.
|
||||
|
||||
* **22 October 2016**
|
||||
* Due to being unable to guarantee they will work (especially as we move toward capability-driven window systems like Wayland), or being unable to work without hacking that breaks other things, the following functions have been removed: `uiWindowPosition()`, `uiWindowSetPosition()`, `uiWindowCenter()`, and `uiWindowOnPositionChanged()`. Centering may come back at some point in the future, albeit in a possibly restricted form. A function to initiate a user move when a part of a uiArea is clicked will be provided soon.
|
||||
|
||||
* **21 October 2016**
|
||||
* `uiDrawTextWeightUltraBold` is now spelled correctly. Thanks to @krakjoe.
|
||||
|
||||
* **18 June 2016**
|
||||
* Help decide [the design of tables and trees in libui](https://github.com/andlabs/libui/issues/159); the implementation starts within the next few days, if not tomorrow!
|
||||
|
||||
* **17 June 2016**
|
||||
* **CMake 3.1.0 is now required.** This is due to CMake's rapid development pace in the past few years adding things libui needs to build on as many systems as possible. If your OS is supported by libui but its repositories ship with an older version of CMake, you will need to find an updated one somewhere.
|
||||
* Please help [plan out a better menu API](https://github.com/andlabs/libui/issues/152).
|
||||
* `uiMainSteps()` no longer takes any arguments and no longer needs to invoke a function to do the work. You still need to call it, but once you do, it will return immediately and you can then get right to your main loop.
|
||||
* **CMake 3.1.0 is now required.** This is due to CMake's rapid development pace in the past few years adding things libui needs to build on as many systems as possible. If your OS is supported by libui but its repositories ship with an older version of CMake, you will need to find an updated one somewhere.
|
||||
* Added `uiNewVerticalSeparator()` to complement `uiNewHorizontalSeparator()`.
|
||||
|
||||
* **16 June 2016**
|
||||
* Added `uiWindowContentSize()`, `uiWindowSetContentSize()`, and `uiWindowOnContentSizeChanged()` methods for manipulating uiWindow content sizes. Note the use of "content size"; the size you work with does NOT include window decorations (titlebars, menus, etc.).
|
||||
* Added `uiWindowFullscreen()` and `uiWindowSetFullscreen()` to allow making fullscreen uiWindows, taking advantage of OS facilities for fullscreen and without changing the screen resolution (!).
|
||||
* Added `uiWindowBorderless()` and `uiWindowSetBorderless()` for allowing borderless uiWindows.
|
||||
* Added `uiMainSteps()`. You call this instead of `uiMain()` if you want to run the main loop yourself. You pass in a function that will be called; within that function, you call `uiMainStep()` repeatedly until it returns 0, doing whatever you need to do in the meantime. (This was needed because just having `uiMainStep()` by itself only worked on some systems.)
|
||||
* Added `uiProgressBarValue()` and allowed passing -1 to `uiProgressBarSetValue()` to make an indeterminate progress bar. Thanks to @emersion.
|
||||
|
||||
* **15 June 2016**
|
||||
* Added `uiFormDelete()`; thanks to @emersion.
|
||||
* Added `uiWindowPosition()`, `uiWindowSetPosition()`, `uiWindowCenter()`, and `uiWindowOnPositionChanged()`, methods for manipulating uiWindow position.
|
||||
|
||||
* **14 June 2016**
|
||||
* uiDarwinControl now has a `ChildVisibilityChanged()` method and a corresponding `NotifyVisibilityChanged()` function that is called by the default show/hide handlers. This is used to make visibility changes work on OS X; uiBox, uiForm, and uiGrid all respect these now.
|
||||
* The same has been done on the Windows side as well.
|
||||
* Hiding and showing controls and padding calculations are now correct on Windows at long last.
|
||||
* Hiding a control in a uiForm now hides its label on all platforms.
|
||||
|
||||
* **13 June 2016**
|
||||
* `intmax_t` and `uintmax_t` are no longer used for libui API functions; now we use `int`. This should make things much easier for bindings. `int` should be at least 32 bits wide; this should be sufficient for all but the most extreme cases.
|
||||
|
||||
* **12 June 2016**
|
||||
* Added `uiGrid`, a new container control that arranges controls in rows and columns, with stretchy ("expanding") rows, stretchy ("expanding") columns, cells that span rows and columns, and cells whose content is aligned in either direction rather than just filling. It's quite powerful, is it? =P
|
||||
|
||||
* **8 June 2016**
|
||||
* Added `uiForm`, a new container control that arranges controls vertically, with properly aligned labels on each. Have fun!
|
||||
|
||||
* **6 June 2016**
|
||||
* Added `uiRadioButtonsSelected()`, `uiRadioButtonsSetSelected()`, and `uiRadioButtonsOnSelected()` to control selection of a radio button and catch an event when such a thing happens.
|
||||
|
||||
* **5 June 2016**
|
||||
* **Alpha 3.1 is here.** This was a much-needed update to Alpha 3 that changes a few things:
|
||||
* **The build system is now cmake.** cmake 2.8.11 or higher is needed.
|
||||
* Static linking is now fully possible.
|
||||
* MinGW linking is back, but static only.
|
||||
* Added `uiNewPasswordEntry()`, which creates a new `uiEntry` suitable for entering passwords.
|
||||
* Added `uiNewSearchEntry()`, which creates a new `uiEntry` suitable for searching. On some systems, the `OnChanged()` event will be slightly delayed and/or combined, to produce a more natural feel when searching.
|
||||
|
||||
* **29 May 2016**
|
||||
* **Alpha 3 is here!** Get it [here](https://github.com/andlabs/libui/releases/tag/alpha3).
|
||||
* The next packaged release will introduce:
|
||||
* uiGrid, another way to lay out controls, a la GtkGrid
|
||||
* uiOpenGLArea, a way to render OpenGL content in a libui uiArea
|
||||
* uiTable, a data grid control that may or may not have tree facilities (if it does, it will be called uiTree instead)
|
||||
* a complete, possibly rewritten, drawing and text rendering infrastructure
|
||||
* Thanks to @pcwalton, we can now statically link libui! Simply do `make STATIC=1` instead of just `make`.
|
||||
* On Windows you must link both `libui.lib` and `libui.res` AND provide a Common Controls 6 manifest for output static binaries to work properly.
|
||||
|
||||
* **28 May 2016**
|
||||
* As promised, **the minimum system requirements are now OS X 10.8 and GTK+ 3.10 for OS X and Unix, respectively**.
|
||||
|
||||
* **26 May 2016**
|
||||
* Two OS X-specific functions have been added: `uiDarwinMarginAmount()` and `uiDarwinPaddingAmount()`. These return the amount of margins and padding, respectively, to give to a control, and are intended for container implementations. These are suitable for the constant of a NSLayoutConstraint. They both take a pointer parameter that is reserved for future use and should be `NULL`.
|
||||
|
||||
* **25 May 2016**
|
||||
* uiDrawTextLayout attributes are now specified in units of *graphemes* on all platforms. This means characters as seen from a user's perspective, not Unicode codepoints or UTF-8 bytes. So a long string of combining marker codepoints after one codepoint would still count as one grapheme.
|
||||
|
||||
* **24 May 2016**
|
||||
* You can now help choose [a potential new build system for libui](https://github.com/andlabs/libui/issues/62).
|
||||
* Tomorrow I will decide if OS X 10.7 will also be dropped alongside GTK+ 3.4-3.8 this Saturday. Stay tuned.
|
||||
* As promised, `uiCombobox` is now split into `uiCombobox` for non-editable comboboxes and `uiEditableCombobox` for editable comboboxes. Mind the function changes as well :)
|
||||
* There is a new function `uiMainStep()`, which runs one iteration of the main loop. It takes a single boolean argument, indicating whether to wait for an event to occur or not. It returns true if an event was processed (or if no event is available if you don't want to wait) and false if the event loop was told to stop (for instance, `uiQuit()` was called).
|
||||
|
||||
* **23 May 2016**
|
||||
* Fixed surrogate pair drawing on OS X.
|
||||
|
||||
* **22 May 2016**
|
||||
* Two more open questions I'd like your feedback on are available [here](https://github.com/andlabs/libui/issues/48) and [here](https://github.com/andlabs/libui/issues/25).
|
||||
* Sometime in the next 48 hours (before 23:59 EDT on 24 May 2016) I will split `uiCombobox` into two separate controls, `uiCombobox` and `uiEditableCombobox`, each with slightly different events and "selected item" mechanics. Prepare your existing code.
|
||||
* Removed `uiControlVerifyDestroy()`; that is now part of `uiFreeControl()` itself.
|
||||
* Added `uiPi`, a constant for π. This is provided for C and C++ programmers, where there is no standard named constant for π; bindings authors shouldn't need to worry about this.
|
||||
* Fixed uiMultilineEntry not properly having line breaks on Windows.
|
||||
* Added `uiNewNonWrappingMultilineEntry()`, which creates a uiMultilineEntry that scrolls horizontally instead of wrapping lines. (This is not documented as being changeable after the fact on Windows, hence it's a creation-time choice.)
|
||||
* uiAreas on Windows and some internal Direct2D areas now respond to `WM_PRINTCLIENT` properly, which should hopefully increase the quality of screenshots.
|
||||
* uiDateTimePicker on GTK+ works properly on RTL layouts and no longer disappears off the bottom of the screen if not enough room is available. It will also no longer be marked for localization of the time format (what the separator should be and whether to use 24-hour time), as that information is not provided by the locale system. :(
|
||||
* Added `uiUserBugCannotSetParentOnToplevel()`, which should be used by implementations of toplevel controls in their `SetParent()` implementations. This will also be the beginning of consolidating common user bug messages into a single place, though this will be one of the only few exported user bug functions.
|
||||
* uiSpinbox and uiSlider now merely swap their min and max if min ≥ max. They will no longer panic and do nothing, respectively.
|
||||
* Matrix scaling will no longer leave the matrix in an invalid state on OS X and GTK+.
|
||||
* `uiMultilineEntrySetText()` and `uiMutlilineEntryAppend()` on GTK+ no longer fire `OnChanged()` events.
|
||||
|
||||
* **21 May 2016**
|
||||
* I will now post announcements and updates here.
|
||||
* Now that Ubuntu 16.04 LTS is here, no earlier than next Saturday, 28 May 2016 at noon EDT, **I will bump the minimum GTK+ version from 3.4 to 3.10**. This will add a lot of new features that I can now add to libui, such as search-oriented uiEntries, lists of arbitrary control layouts, and more. If you are still running a Linux distribution that doesn't come with 3.10, you will either need to upgrade or use jhbuild to set up a newer version of GTK+ in a private environment.
|
||||
* You can decide if I should also drop OS X 10.7 [here](https://github.com/andlabs/libui/issues/46).
|
198
README.md
198
README.md
|
@ -1,73 +1,81 @@
|
|||
# libui: a portable GUI library for C
|
||||
|
||||
This README is being written.<br>
|
||||
[](https://travis-ci.org/andlabs/libui)
|
||||
*(currently failing because the version of cmake that Travis uses treats Objective-C files as C++; if you know the fix please file a PR)*
|
||||
[](https://dev.azure.com/andlabs/libui/_build/latest?definitionId=1&branchName=master)<br>
|
||||
[](https://ci.appveyor.com/project/andlabs/libui/branch/master)
|
||||
|
||||
## Announcements
|
||||
## Status
|
||||
|
||||
* **5 June 2016**
|
||||
* **Alpha 3.1 is here.** This was a much-needed update to Alpha 3 that changes a few things:
|
||||
* **The build system is now cmake.** cmake 2.8.11 or higher is needed.
|
||||
* Static linking is now fully possible.
|
||||
* MinGW linking is back, but static only.
|
||||
It has come to my attention that I have not been particularly clear about how usable or feature-complete libui is, and that this has fooled many people into expecting more from libui right this moment than I have explicitly promised to make available. I apologize for not doing this sooner.
|
||||
|
||||
* **29 May 2016**
|
||||
* **Alpha 3 is here!** Get it [here](https://github.com/andlabs/libui/releases/tag/alpha3).
|
||||
* The next packaged release will introduce:
|
||||
* uiGrid, another way to lay out controls, a la GtkGrid
|
||||
* uiOpenGLArea, a way to render OpenGL content in a libui uiArea
|
||||
* uiTable, a data grid control that may or may not have tree facilities (if it does, it will be called uiTree instead)
|
||||
* a complete, possibly rewritten, drawing and text rendering infrastructure
|
||||
libui is currently **mid-alpha** software. Much of what is currently present runs stabily enough for the examples and perhaps some small programs to work, but the stability is still a work-in-progress, much of what is already there is not feature-complete, some of it will be buggy on certain platforms, and there's a lot of stuff missing. In short, here's a list of features that I would like to add to libui, but that aren't in yet:
|
||||
|
||||
* **24 May 2016**
|
||||
* You can now help choose [a potential new build system for libui](https://github.com/andlabs/libui/issues/62).
|
||||
* Tomorrow I will decide if OS X 10.7 will also be dropped alongside GTK+ 3.4-3.8 this Saturday. Stay tuned.
|
||||
- trees
|
||||
- clipboard support, including drag and drop
|
||||
- more and better dialogs
|
||||
- printing
|
||||
- accessibility for uiArea and custom controls
|
||||
- document-based programs
|
||||
- tighter OS integration (especially for document-based programs), to allow programs to fully feel native, rather than merely look and act native
|
||||
- better support for standard dialogs and features (search bars, etc.)
|
||||
- OpenGL support
|
||||
|
||||
* **22 May 2016**
|
||||
* Two more open questions I'd like your feedback on are available [here](https://github.com/andlabs/libui/issues/48) and [here](https://github.com/andlabs/libui/issues/25).
|
||||
* Sometime in the next 48 hours (before 23:59 EDT on 24 May 2016) I will split `uiCombobox` into two separate controls, `uiCombobox` and `uiEditableCombobox`, each with slightly different events and "selected item" mechanics. Prepare your existing code.
|
||||
In addition, [here](https://github.com/andlabs/libui/issues?utf8=%E2%9C%93&q=master+in%3Atitle+is%3Aissue+is%3Aopen) is a list of issues generalizing existing problems.
|
||||
|
||||
* **21 May 2016**
|
||||
* I will now post announcements and updates here.
|
||||
* Now that Ubuntu 16.04 LTS is here, no earlier than next Saturday, 28 May 2016 at noon EDT, **I will bump the minimum GTK+ version from 3.4 to 3.10**. This will add a lot of new features that I can now add to libui, such as search-oriented uiEntries, lists of arbitrary control layouts, and more. If you are still running a Linux distribution that doesn't come with 3.10, you will either need to upgrade or use jhbuild to set up a newer version of GTK+ in a private environment.
|
||||
* You can decide if I should also drop OS X 10.7 [here](https://github.com/andlabs/libui/issues/46).
|
||||
Furthermore, libui is not properly fully documented yet. This is mainly due to the fact that the API was initially unstable enough so as to result in rewriting documentation multiple times, in addition to me not being happy with really any existing C code documentation tool. That being said, I have started to pin down my ideal code documentation style in parts of `ui.h`, most notably in the uiAttributedString APIs. Over time, I plan on extending this to the rest of the headers. You can also use [the documentation for libui's Go bindings](https://godoc.org/github.com/andlabs/ui) as a reference, though it is somewhat stale and not optimally written.
|
||||
|
||||
## Updates
|
||||
But libui is not dead; I am working on it whenever I can, and I hope to get it to a point of real quality soon!
|
||||
|
||||
*Note that today's entry may be updated later today.*
|
||||
## News
|
||||
|
||||
* **29 May 2016**
|
||||
* Thanks to @pcwalton, we can now statically link libui! Simply do `make STATIC=1` instead of just `make`.
|
||||
* On Windows you must link both `libui.lib` and `libui.res` AND provide a Common Controls 6 manifest for output static binaries to work properly.
|
||||
*Note that today's entry (Eastern Time) may be updated later today.*
|
||||
|
||||
* **28 May 2016**
|
||||
* As promised, **the minimum system requirements are now OS X 10.8 and GTK+ 3.10 for OS X and Unix, respectively**.
|
||||
* **7 April 2019**
|
||||
* **The build system has been switched to Meson.** See below for instructions. This change was made because the previous build system, CMake, caused countless headaches over trivial issues. Meson was chosen due to how unproblematic setting up libui's build just right was, as well as having design goals that are by coincidence closely aligned with what libui wants.
|
||||
* Travis CI has been replaced with Azure Pipelines and much of the AppVeyor CI configuration was integrated into the Azure Pipelines configuration. This shouldn't affect most developers.
|
||||
|
||||
* **26 May 2016**
|
||||
* Two OS X-specific functions have been added: `uiDarwinMarginAmount()` and `uiDarwinPaddingAmount()`. These return the amount of margins and padding, respectively, to give to a control, and are intended for container implementations. These are suitable for the constant of a NSLayoutConstraint. They both take a pointer parameter that is reserved for future use and should be `NULL`.
|
||||
* **1 September 2018**
|
||||
* **Alpha 4.1 is here.** This is an emergency fix to Alpha 4 to fix `uiImageAppend()` not working as documented. It now works properly, with one important difference you'll need to care about: **it now requires image data to be alpha-premultiplied**. In addition, `uiImage` also is implemented slightly more nicely now, and `ui.h` has minor documentation typo fixes.
|
||||
* Alpha 4.1 also tries to make everything properly PIC-enabled.
|
||||
|
||||
* **25 May 2016**
|
||||
* uiDrawTextLayout attributes are now specified in units of *graphemes* on all platforms. This means characters as seen from a user's perspective, not Unicode codepoints or UTF-8 bytes. So a long string of combining marker codepoints after one codepoint would still count as one grapheme.
|
||||
* **10 August 2018**
|
||||
* **Alpha 4 is finally here.** Everything from Alpha 3.5 and what's listed below is in this release; the two biggest changes are still the new text drawing API and new uiTable control. In between all that is a whole bunch of bugfixes, and hopefully more stability too. Thanks to everybody who helped contribute!
|
||||
* Alpha 4 should hopefully also include automated binary releases via CI. Thanks to those who helped set that up!
|
||||
|
||||
* **24 May 2016**
|
||||
* As promised, `uiCombobox` is now split into `uiCombobox` for non-editable comboboxes and `uiEditableCombobox` for editable comboboxes. Mind the function changes as well :)
|
||||
* There is a new function `uiMainStep()`, which runs one iteration of the main loop. It takes a single boolean argument, indicating whether to wait for an event to occur or not. It returns true if an event was processed (or if no event is available if you don't want to wait) and false if the event loop was told to stop (for instance, `uiQuit()` was called).
|
||||
* **8 August 2018**
|
||||
* Finally introduced an API for loading images, `uiImage`, and a new control, `uiTable`, for displaying tabular data. These provide enough basic functionality for now, but will be improved over time. You can read the documentation for the new features as they are [here](https://github.com/andlabs/libui/blob/f47e1423cf95ad7b1001663f3381b5a819fc67b9/uitable.h). Thanks to everyone who helped get to this point, in particular @bcampbell for the initial Windows code, and to everyone else for their patience!
|
||||
|
||||
* **23 May 2016**
|
||||
* Fixed surrogate pair drawing on OS X.
|
||||
* **30 May 2018**
|
||||
* Merged the previous Announcements and Updates section of this README into a single News section, and merged the respective archive files into a single NEWS.md file.
|
||||
|
||||
* **22 May 2016**
|
||||
* Removed `uiControlVerifyDestroy()`; that is now part of `uiFreeControl()` itself.
|
||||
* Added `uiPi`, a constant for π. This is provided for C and C++ programmers, where there is no standard named constant for π; bindings authors shouldn't need to worry about this.
|
||||
* Fixed uiMultilineEntry not properly having line breaks on Windows.
|
||||
* Added `uiNewNonWrappingMultilineEntry()`, which creates a uiMultilineEntry that scrolls horizontally instead of wrapping lines. (This is not documented as being changeable after the fact on Windows, hence it's a creation-time choice.)
|
||||
* uiAreas on Windows and some internal Direct2D areas now respond to `WM_PRINTCLIENT` properly, which should hopefully increase the quality of screenshots.
|
||||
* uiDateTimePicker on GTK+ works properly on RTL layouts and no longer disappears off the bottom of the screen if not enough room is available. It will also no longer be marked for localization of the time format (what the separator should be and whether to use 24-hour time), as that information is not provided by the locale system. :(
|
||||
* Added `uiUserBugCannotSetParentOnToplevel()`, which should be used by implementations of toplevel controls in their `SetParent()` implementations. This will also be the beginning of consolidating common user bug messages into a single place, though this will be one of the only few exported user bug functions.
|
||||
* uiSpinbox and uiSlider now merely swap their min and max if min ≥ max. They will no longer panic and do nothing, respectively.
|
||||
* Matrix scaling will no longer leave the matrix in an invalid state on OS X and GTK+.
|
||||
* `uiMultilineEntrySetText()` and `uiMutlilineEntryAppend()` on GTK+ no longer fire `OnChanged()` events.
|
||||
* **16 May 2018**
|
||||
* Thanks to @parro-it and @msink, libui now has better CI, including AppVeyor for Windows CI, and automated creation of binary releases when I make a tagged release.
|
||||
|
||||
* **13 May 2018**
|
||||
* Added new functions to work with uiDateTimePickers: `uiDateTimePickerTime()`, `uiDateTimePickerSetTime()`, and `uiDateTimePickerOnChanged()`. These operate on standard `<time.h>` `struct tm`s. Thanks @cody271!
|
||||
* Release builds on Windows with MSVC should be fixed now; thanks @l0calh05t, @slahn, @mischnic, and @zentner-kyle.
|
||||
|
||||
* **12 May 2018**
|
||||
* GTK+ and OS X now have a cleaner build process for static libraries which no longer has intermediate files and differing configurations. As a result, certain issues should no longer be present. New naming rules for internal symbols of libui have also started being drafted; runtime symbols and edge cases still need to be handled (and the rules applied to Windows) before this can become a regular thing.
|
||||
|
||||
* **2 May 2018**
|
||||
* On Windows, you no longer need to carry around a `libui.res` file with static builds. You do need to link in the appropriate manifest file, such as the one in the `windows/` folder (I still need to figure out exactly what is needed apart from the Common Controls v6 dependency, or at least to create a complete-ish template), or at least include it alongside your executables. This also means you should no longer see random cmake errors when building the static libraries.
|
||||
|
||||
* **18 April 2018**
|
||||
* Introduced a new `uiTimer()` function for running code on a timer on the main thread. (Thanks to @cody271.)
|
||||
* Migrated all code in the `common/` directory to use `uipriv` prefixes for everything that isn't `static`. This is the first step toward fixing static library oddities within libui, allowing libui to truly be safely used as either a static library or a shared library.
|
||||
|
||||
* **18 March 2018**
|
||||
* Introduced an all-new formatted text API that allows you to process formatted text in ways that the old API wouldn't allow. You can read on the whole API [here](https://github.com/andlabs/libui/blob/8944a3fc5528445b9027b1294b6c86bae03eeb89/ui_attrstr.h). There is also a new examples for it: `drawtext`, which shows the whole API at a glance. It doesn't yet support measuring or manipulating text, nor does it currently support functions that would be necessary for things like text editors; all of this will be added back later.
|
||||
* libui also now uses my [utf library](https://github.com/andlabs/utf) for UTF-8 and UTF-16 processing, to allow consistent behavior across platforms. This usage is not completely propagated throughout libui, but the Windows port uses it in most places now, and eventually this will become what libui will use throughout.
|
||||
* Also introduced a formal set of contribution guidelines, see `CONTRIBUTING.md` for details. They are still WIP.
|
||||
|
||||
* **17 February 2018**
|
||||
* The longstanding Enter+Escape crashes on Windows have finally been fixed (thanks to @lxn).
|
||||
* **Alpha 3.5 is now here.** This is a quickie release primiarly intended to deploy the above fix to package ui itself. **It is a partial binary release; sorry!** More new things will come in the next release, which will also introduce semver (so it will be called v0.4.0 instead).
|
||||
* Alpha 3.5 also includes a new control gallery example. The screenshots below have not been updated yet.
|
||||
|
||||
*Old announcements can be found in the NEWS.md file.*
|
||||
|
||||
## Runtime Requirements
|
||||
|
||||
|
@ -78,7 +86,8 @@ This README is being written.<br>
|
|||
## Build Requirements
|
||||
|
||||
* All platforms:
|
||||
* CMake 2.8.11 or newer
|
||||
* [Meson](https://mesonbuild.com/) 0.48.0 or newer
|
||||
* Any of Meson's backends; this section assumes you are using [Ninja](https://ninja-build.org/), but there is no reason the other backends shouldn't work.
|
||||
* Windows: either
|
||||
* Microsoft Visual Studio 2013 or newer (2013 is needed for `va_copy()`) — you can build either a static or a shared library
|
||||
* MinGW-w64 (other flavors of MinGW may not work) — **you can only build a static library**; shared library support will be re-added once the following features come in:
|
||||
|
@ -88,33 +97,48 @@ This README is being written.<br>
|
|||
|
||||
## Building
|
||||
|
||||
Out-of-tree builds typical of cmake are preferred:
|
||||
libui uses only [the standard Meson build options](https://mesonbuild.com/Builtin-options.html), so a libui build can be set up just like any other:
|
||||
|
||||
```
|
||||
$ mkdir build
|
||||
$ cd build
|
||||
$ cmake ..
|
||||
$ # you must be in the top-level libui directory, otherwise this won't work
|
||||
$ meson setup build [options]
|
||||
$ ninja -C build
|
||||
```
|
||||
|
||||
Pass `-DBUILD_SHARED_LIBS=OFF` to `cmake` to build a static library. The standard cmake build configurations are provided; if none is specified, `Debug` is used.
|
||||
Once this completes, everything will be under `build/meson-out/`. (Note that unlike the previous build processes, everything is built by default, including tests and examples.)
|
||||
|
||||
If you use a makefile generator with cmake, then
|
||||
The most important options are:
|
||||
|
||||
* `--buildtype=(debug|release|...)` controls the type of build made; the default is `debug`. For a full list of valid values, consult [the Meson documentation](https://mesonbuild.com/Running-Meson.html).
|
||||
* `--default-library=(shared|static)` controls whether libui is built as a shared library or a static library; the default is `shared`. You currently cannot specify `both`, as the build process changes depending on the target type (though I am willing to look into changing things if at all possible).
|
||||
* `-Db_sanitize=which` allows enabling the chosen [sanitizer](https://github.com/google/sanitizers) on a system that supports sanitizers. The list of supported values is in [the Meson documentation](https://mesonbuild.com/Builtin-options.html#base-options).
|
||||
* `--backend=backend` allows using the specified `backend` for builds instead of `ninja` (the default). A list of supported values is in [the Meson documentation](https://mesonbuild.com/Builtin-options.html#universal-options).
|
||||
|
||||
Most other built-in options will work, though keep in mind there are a handful of options that cannot be overridden because libui depends on them holding a specific value; if you do override these, though, libui will warn you when you run `meson`.
|
||||
|
||||
The Meson website and documentation has more in-depth usage instructions.
|
||||
|
||||
For the sake of completeness, I should note that the default value of `--layout` is `flat`, not the usual `mirror`. This is done both to make creating the release archives easier as well as to reduce the chance that shared library builds will fail to start on Windows because the DLL is in another directory. You can always specify this manually if you want.
|
||||
|
||||
Backends other than `ninja` should work, but are untested by me.
|
||||
|
||||
## Installation
|
||||
|
||||
Meson also supports installing from source; if you use Ninja, just do
|
||||
|
||||
```
|
||||
$ make
|
||||
$ make tester # for the test program
|
||||
$ make examples # for examples
|
||||
$ ninja -C build install
|
||||
```
|
||||
|
||||
and pass `VERBOSE=1` to see build commands. Build targets will be in the `build/out` folder.
|
||||
When running `meson`, the `--prefix` option will set the installation prefix. [The Meson documentation](https://mesonbuild.com/Builtin-options.html#universal-options) has more information, and even lists more fine-grained options that you can use to control the installation.
|
||||
|
||||
Project file generators should work, but are untested by me.
|
||||
#### Arch Linux
|
||||
|
||||
On Windows, I use the `Unix Makefiles` generator and GNU make (built using the `build_w32.bat` script included in the source and run in the Visual Studio command line). In this state, if MinGW-w64 (either 32-bit or 64-bit) is not in your `%PATH%`, cmake will use MSVC by default; otherwise, cmake will use with whatever MinGW-w64 is in your path. `set PATH=%PATH%;c:\msys2\mingw(32/64)\bin` should be enough to temporarily change to a MinGW-w64 build for the current command line session only if you installed MinGW-w64 through [MSYS2](https://msys2.github.io/); no need to change global environment variables constantly.
|
||||
Can be built from AUR: https://aur.archlinux.org/packages/libui-git/
|
||||
|
||||
## Documentation
|
||||
|
||||
Needs to be written. Consult ui.h and the examples for details for now.
|
||||
Needs to be written. Consult `ui.h` and the examples for details for now.
|
||||
|
||||
## Language Bindings
|
||||
|
||||
|
@ -124,17 +148,41 @@ Other people have made bindings to other languages:
|
|||
|
||||
Language | Bindings
|
||||
--- | ---
|
||||
C#/.net | [LibUI.Binding](https://github.com/NattyNarwhal/LibUI.Binding), [SharpUI](https://github.com/benpye/sharpui/)
|
||||
Crystal | [libui.cr](https://github.com/Fusion/libui.cr)
|
||||
D | [DerelictLibui](https://github.com/Extrawurst/DerelictLibui)
|
||||
Haskell | [libui-haskell](https://github.com/ajnsit/libui-haskell)
|
||||
JavaScript | [libui.js (merged into libui-node?)](https://github.com/mavenave/libui.js)
|
||||
C++ | [libui-cpp](https://github.com/billyquith/libui-cpp), [cpp-libui-qtlike](https://github.com/aoloe/cpp-libui-qtlike)
|
||||
C# / .NET Framework | [LibUI.Binding](https://github.com/NattyNarwhal/LibUI.Binding)
|
||||
C# / .NET Core | [DevZH.UI](https://github.com/noliar/DevZH.UI), [SharpUI](https://github.com/benpye/sharpui/), [TCD.UI](https://github.com/tacdevel/tcdfx)
|
||||
CHICKEN Scheme | [wasamasa/libui](https://github.com/wasamasa/libui)
|
||||
Common Lisp | [jinwoo/cl-ui](https://github.com/jinwoo/cl-ui)
|
||||
Crystal | [libui.cr](https://github.com/Fusion/libui.cr), [hedron](https://github.com/Qwerp-Derp/hedron)
|
||||
D | [DerelictLibui (flat API)](https://github.com/Extrawurst/DerelictLibui), [libuid (object-oriented)](https://github.com/mogud/libuid)
|
||||
Euphoria | [libui-euphoria](https://github.com/ghaberek/libui-euphoria)
|
||||
Harbour | [hbui](https://github.com/rjopek/hbui)
|
||||
Haskell | [haskell-libui](https://github.com/beijaflor-io/haskell-libui)
|
||||
JavaScript/Node.js | [libui-node](https://github.com/parro-it/libui-node), [libui.js (merged into libui-node?)](https://github.com/mavenave/libui.js), [proton-native](https://github.com/kusti8/proton-native), [vuido](https://github.com/mimecorg/vuido)
|
||||
Julia | [Libui.jl](https://github.com/joa-quim/Libui.jl)
|
||||
Lua | [libuilua](https://github.com/zevv/libuilua), [libui-lua](https://github.com/mdombroski/libui-lua)
|
||||
Node.js | [libui-node](https://github.com/parro-it/libui-node)
|
||||
Kotlin | [kotlin-libui](https://github.com/msink/kotlin-libui)
|
||||
Lua | [libuilua](https://github.com/zevv/libuilua), [libui-lua](https://github.com/mdombroski/libui-lua), [lui](http://tset.de/lui/index.html), [lui](https://github.com/zhaozg/lui)
|
||||
Nim | [ui](https://github.com/nim-lang/ui)
|
||||
Perl6 | [perl6-libui](https://github.com/Garland-g/perl6-libui)
|
||||
PHP | [ui](https://github.com/krakjoe/ui)
|
||||
Python | [pylibui](https://github.com/joaoventura/pylibui)
|
||||
Ruby | [libui-ruby](https://github.com/jamescook/libui-ruby)
|
||||
Rust | [libui-rs](https://github.com/pcwalton/libui-rs)
|
||||
Ruby | [libui-ruby](https://github.com/jamescook/libui-ruby), [libui](https://github.com/kojix2/libui)
|
||||
Rust | [libui-rs](https://github.com/rust-native-ui/libui-rs)
|
||||
Scala | [scalaui](https://github.com/lolgab/scalaui)
|
||||
Swift | [libui-swift](https://github.com/sclukey/libui-swift)
|
||||
|
||||
## Frequently Asked Questions
|
||||
|
||||
### Why does my program start in the background on OS X if I run from the command line?
|
||||
OS X normally does not start program executables directly; instead, it uses [Launch Services](https://developer.apple.com/reference/coreservices/1658613-launch_services?language=objc) to coordinate the launching of the program between the various parts of the system and the loading of info from an .app bundle. One of these coordination tasks is responsible for bringing a newly launched app into the foreground. This is called "activation".
|
||||
|
||||
When you run a binary directly from the Terminal, however, you are running it directly, not through Launch Services. Therefore, the program starts in the background, because no one told it to activate! Now, it turns out [there is an API](https://developer.apple.com/reference/appkit/nsapplication/1428468-activateignoringotherapps) that we can use to force our app to be activated. But if we use it, then we'd be trampling over Launch Services, which already knows whether it should activate or not. Therefore, libui does not step over Launch Services, at the cost of requiring an extra user step if running directly from the command line.
|
||||
|
||||
See also [this](https://github.com/andlabs/libui/pull/20#issuecomment-211381971) and [this](http://stackoverflow.com/questions/25318524/what-exactly-should-i-pass-to-nsapp-activateignoringotherapps-to-get-my-appl).
|
||||
|
||||
## Contributing
|
||||
|
||||
See `CONTRIBUTING.md`.
|
||||
|
||||
## Screenshots
|
||||
|
||||
|
|
174
TODO.md
174
TODO.md
|
@ -1,3 +1,13 @@
|
|||
- make sure the last line of text layouts include leading
|
||||
|
||||
- documentation notes:
|
||||
- static binaries do not link system libraries, meaning apps still depend on shared GTK+, etc.
|
||||
- ui*Buttons are NOT compatible with uiButton functions
|
||||
|
||||
- more robust layout handling
|
||||
- uiFormTie() for ensuring multiple uiForms have the same label area widths
|
||||
- uiSizeGroup for size groups (GtkSizeGroup on GTK+, auto layout constraints on OS X; consider adding after 10.8 is gone)
|
||||
|
||||
- windows: should the initial hwndInsertAfter be HWND_BOTTOM for what we want?
|
||||
|
||||
- windows: document the rules for controls and containers
|
||||
|
@ -86,3 +96,167 @@ don't forget LONGTERMs as well
|
|||
|
||||
notes
|
||||
- http://blogs.msdn.com/b/oldnewthing/archive/2004/03/29/101121.aspx on accelerators
|
||||
|
||||
- group and tab should act as if they have no child if the child is hidden
|
||||
on windows
|
||||
|
||||
|
||||
|
||||
- a way to do recursive main loops
|
||||
- how do we handle 0 returns from non-recursive uiMainStep() calls that aren't the main loop? (event handlers, for instance)
|
||||
- should repeated calls to uiMainStep() after uiQuit() return 0 reliably? this will be needed for non-recursive loops
|
||||
|
||||
http://stackoverflow.com/questions/38338426/meaning-of-ampersand-in-rc-files/38338841?noredirect=1#comment64093084_38338841
|
||||
|
||||
label shortcut keys
|
||||
|
||||
- remove whining from source code
|
||||
|
||||
[01:41:47] <vrishab> Hi. does pango support "fgalpha". I see that foreground="112233xx" works ( alpha=xx ), but fgalpha is a no-op
|
||||
[01:52:29] <vrishab> pango_attr_foreground_alpha_new (32767) seems to be called in either case, but only the "foreground" attr works
|
||||
[01:56:09] lolek (lolek@ip-91-244-230-76.simant.pl) joined the channel
|
||||
[01:57:48] <vrishab> ok. seems like "foreground" is mandatory attr, 1. "foreground-without-alpha" + "alpha" works 2. "foreground-with-alpha" works. 3. "alpha" alone doesn
|
||||
[01:57:52] <vrishab> 't work
|
||||
[01:58:29] <vrishab> Is there a way to just specify alpha on the current foreground color ?
|
||||
[02:00:23] lolek (lolek@ip-91-244-230-76.simant.pl) left the channel
|
||||
[02:07:41] mjog (mjog@uniwide-pat-pool-129-94-8-98.gw.unsw.edu.au) left IRC (Quit: mjog)
|
||||
[02:08:10] seb128 (seb128@53542B83.cm-6-5a.dynamic.ziggo.nl) joined the channel
|
||||
[02:12:37] <andlabs> huh
|
||||
[02:12:41] <andlabs> what version of pango?
|
||||
[02:13:05] <vrishab> the latest .
|
||||
[02:15:00] <vrishab> 1.40.3
|
||||
[02:20:46] <andlabs> I'll ahve to keep this in mind then, thanks
|
||||
[02:20:59] <andlabs> if only there was a cairo-specific attribute for alpha...
|
||||
|
||||
FONT LOADING
|
||||
|
||||
[00:10:08] <hergertme> andlabs: is there API yet to load from memory? last i checked i only found from file (which we use in builder). https://git.gnome.org/browse/gnome-builder/tree/libide/editor/ide-editor-map-bin.c#n115
|
||||
[00:13:12] mrmcq2u_ (mrmcq2u@109.79.53.90) joined the channel
|
||||
[00:14:59] mrmcq2u (mrmcq2u@109.79.73.102) left IRC (Ping timeout: 181 seconds)
|
||||
[00:15:19] <andlabs> hergertme: no, which is why I was asking =P
|
||||
[00:15:30] <andlabs> I would have dug down if I could ensure at least something about the backends a GTK+ 3 program uses
|
||||
[00:15:39] <andlabs> on all platforms except windows and os x
|
||||
[00:16:11] <hergertme> to the best of my (partially outdated, given pace of foss) knowledge there isn't an api to load from memory
|
||||
[00:16:28] <hergertme> you can possibly make a tmpdir and put a temp file in there
|
||||
[00:16:52] <hergertme> and load that as your font dir in your FcConfig, so any PangoFontDescription would point to that one font, no matter what
|
||||
[00:17:18] <hergertme> (using the API layed out in that link)
|
||||
[00:18:18] dsr1014__ (dsr1014@c-73-72-102-18.hsd1.il.comcast.net) joined the channel
|
||||
[00:35:18] simukis_ (simukis@78-60-58-6.static.zebra.lt) left IRC (Quit: simukis_)
|
||||
[00:35:48] dreamon_ (dreamon@ppp-188-174-49-41.dynamic.mnet-online.de) joined the channel
|
||||
[00:40:09] samtoday_ (samtoday@114-198-116-132.dyn.iinet.net.au) joined the channel
|
||||
[00:40:32] mjog (mjog@120.18.225.46) joined the channel
|
||||
[00:40:38] <andlabs> hergertme: not necessarily fontconfig
|
||||
[00:40:45] <andlabs> it can be with ft2 or xft I guess
|
||||
[00:40:55] <andlabs> especially since I want the API NOT to make the font part of the font panel
|
||||
[00:42:07] <hergertme> what sort of deprecated code are you trying to support?
|
||||
[00:42:35] <hergertme> both of those are deprecated in pango fwiw
|
||||
[00:43:06] <hergertme> on Linux im pretty sure we use FC everywhere these days
|
||||
[00:44:46] <hergertme> (and gtk_widget_set_font_map() is how you get your custom font into a widget without affecting the global font lists, as layed out in that link)
|
||||
[00:49:14] vasaikar (vasaikar@125.16.97.121) joined the channel
|
||||
[00:50:14] karlt (karl@2400:e780:801:224:f121:e611:d139:e70e) left IRC (Client exited)
|
||||
[00:50:49] karlt (karl@2400:e780:801:224:f121:e611:d139:e70e) joined the channel
|
||||
[00:51:43] PioneerAxon (PioneerAxo@122.171.61.146) left IRC (Ping timeout: 180 seconds)
|
||||
[00:57:47] PioneerAxon (PioneerAxo@106.201.37.181) joined the channel
|
||||
[01:03:01] karlt (karl@2400:e780:801:224:f121:e611:d139:e70e) left IRC (Ping timeout: 181 seconds)
|
||||
[01:05:49] muhannad (muhannad@95.218.26.152) left IRC (Quit: muhannad)
|
||||
[01:07:51] <andlabs> hergertme: hm
|
||||
[01:07:54] <andlabs> all right, thanks
|
||||
[01:08:05] <andlabs> hergertme: fwiw right now my requirement is 3.10
|
||||
[01:10:47] <hergertme> ah, well you'll probably be missing the neccesary font API on gtk_widget
|
||||
[01:11:04] <hergertme> but pango should be fine even back as far as https://developer.gnome.org/pango/1.28/PangoFcFontMap.html
|
||||
[01:11:56] <andlabs> good
|
||||
[01:12:04] <andlabs> because this is for custom drawing into a DrawingArea
|
||||
[01:14:12] <hergertme> presumably just create your PangoContext as normal, but call pango_context_set_font_map() with the map you've setup. now, the load a font from a file i dont think was added to FontConfig until later though (not sure what release)
|
||||
[01:15:53] <hergertme> FcConfigAppFontAddFile() <-- that API
|
||||
[01:16:30] <hergertme> great, and they don't say what version the API was added in teh docs
|
||||
function: ide_editor_map_bin_add()
|
||||
|
||||
- Mouse ClickLock: do we need to do anything special? *should* we? https://msdn.microsoft.com/en-us/library/windows/desktop/ms724947(v=vs.85).aspx
|
||||
- consider a uiAnticipateDoubleClick() or uiDoubleClickTime() (for a uiQueueTimer()) or something: https://blogs.msdn.microsoft.com/oldnewthing/20041015-00/?p=37553
|
||||
|
||||
- determine whether MSGF_USER is for and if it's correct for our uiArea message filter (if we have one)
|
||||
|
||||
- source file encoding and MSVC compiler itself? https://stackoverflow.com/questions/20518040/how-can-i-get-the-directwrite-padwrite-sample-to-work
|
||||
- also need to worry about object file and output encoding...
|
||||
- this also names the author of the padwrite sample
|
||||
|
||||
- OpenType features TODOs
|
||||
- https://stackoverflow.com/questions/32545675/what-are-the-default-typography-settings-used-by-idwritetextlayout
|
||||
- feature/shaping interaction rules for arabic: https://www.microsoft.com/typography/OpenTypeDev/arabic/intro.htm
|
||||
- other stuff, mostly about UIs and what users expect to be able to set
|
||||
- https://klim.co.nz/blog/towards-an-ideal-opentype-user-interface/
|
||||
- https://libregraphicsmeeting.org/2016/designing-for-many-applications-opentype-features-ui/
|
||||
- https://www.youtube.com/watch?v=wEyDhsH076Y
|
||||
- https://twitter.com/peter_works
|
||||
- http://ilovetypography.com/2014/10/22/better-ui-for-better-typography-adobe-petition/
|
||||
- http://silgraphite.sourceforge.net/ui/studynote.html
|
||||
|
||||
- add NXCOMPAT (DEP awareness) to the Windows builds
|
||||
- and ASLR too? or is that not a linker setting
|
||||
|
||||
OS X: embedding an Info.plist into a binary directly
|
||||
https://www.objc.io/issues/6-build-tools/mach-o-executables/
|
||||
TODO will this let Dictation work?
|
||||
TODO investigate ad-hoc codesigning
|
||||
|
||||
https://blogs.msdn.microsoft.com/oldnewthing/20040112-00/?p=41083 def files for decoration (I forget if I said this earlier)
|
||||
|
||||
TODO ClipCursor() stuff; probably not useful for libui but still
|
||||
https://blogs.msdn.microsoft.com/oldnewthing/20140102-00/?p=2183
|
||||
https://blogs.msdn.microsoft.com/oldnewthing/20061117-03/?p=28973
|
||||
https://msdn.microsoft.com/en-us/library/windows/desktop/ms648383(v=vs.85).aspx
|
||||
|
||||
https://cmake.org/Wiki/CMake_Useful_Variables
|
||||
set(CMAKE_SHARED_LINKER_FLAGS "-Wl,--no-undefined")
|
||||
On Unix systems, this will make linker report any unresolved symbols from object files (which is quite typical when you compile many targets in CMake projects, but do not bother with linking target dependencies in proper order).
|
||||
(I used to have something like this back when I used makefiles; did it convert in? I forget)
|
||||
|
||||
look into these for the os x port
|
||||
https://developer.apple.com/documentation/appkit/view_management/nseditor?language=objc
|
||||
https://developer.apple.com/documentation/appkit/view_management/nseditorregistration?language=objc
|
||||
|
||||
for future versions of the os x port
|
||||
https://developer.apple.com/documentation/appkit/nslayoutguide?language=objc and anchors
|
||||
https://developer.apple.com/documentation/appkit/nsuserinterfacecompression?language=objc https://developer.apple.com/documentation/appkit/nsuserinterfacecompressionoptions?language=objc
|
||||
though at some point we'll be able to use NSStackView and NSGridView directly, so...
|
||||
|
||||
Cocoa PDFs
|
||||
https://developer.apple.com/documentation/appkit/nspdfimagerep?language=objc
|
||||
https://developer.apple.com/documentation/coregraphics?language=objc
|
||||
https://developer.apple.com/library/content/documentation/Cocoa/Conceptual/Printing/osxp_pagination/osxp_pagination.html#//apple_ref/doc/uid/20001051-119037
|
||||
https://developer.apple.com/documentation/appkit/nsprintoperation/1529269-pdfoperationwithview?language=objc
|
||||
https://developer.apple.com/library/content/documentation/Cocoa/Conceptual/Printing/osxp_printapps/osxp_printapps.html#//apple_ref/doc/uid/20000861-BAJBFGED
|
||||
https://developer.apple.com/library/content/documentation/Cocoa/Conceptual/Printing/osxp_printingapi/osxp_printingapi.html#//apple_ref/doc/uid/10000083i-CH2-SW2
|
||||
https://developer.apple.com/library/content/documentation/Cocoa/Conceptual/Printing/osxp_printinfo/osxp_printinfo.html#//apple_ref/doc/uid/20000864-BAJBFGED
|
||||
https://developer.apple.com/library/content/documentation/Cocoa/Conceptual/Printing/osxp_printlayoutpanel/osxp_printlayoutpanel.html#//apple_ref/doc/uid/20000863-BAJBFGED
|
||||
https://developer.apple.com/documentation/appkit/nspagelayout?language=objc
|
||||
https://developer.apple.com/documentation/appkit/nsprintinfo?language=objc
|
||||
https://developer.apple.com/documentation/applicationservices/core_printing?language=objc
|
||||
https://developer.apple.com/documentation/applicationservices/1463247-pmcreatesession?language=objc
|
||||
https://developer.apple.com/documentation/applicationservices/pmprintsession?language=objc
|
||||
https://developer.apple.com/documentation/applicationservices/1460101-pmsessionbegincgdocumentnodialog?language=objc
|
||||
https://developer.apple.com/documentation/applicationservices/1463416-pmsessionbeginpagenodialog?language=objc
|
||||
https://developer.apple.com/documentation/applicationservices/1506831-anonymous/kpmdestinationprocesspdf?language=objc
|
||||
https://developer.apple.com/documentation/applicationservices/1461960-pmcreategenericprinter?language=objc
|
||||
https://developer.apple.com/documentation/applicationservices/1460101-pmsessionbegincgdocumentnodialog?language=objc
|
||||
https://developer.apple.com/documentation/applicationservices/1464527-pmsessionenddocumentnodialog?language=objc
|
||||
https://developer.apple.com/documentation/applicationservices/1461952-pmsessiongetcggraphicscontext?language=objc
|
||||
https://developer.apple.com/library/content/technotes/tn2248/_index.html
|
||||
https://developer.apple.com/library/content/samplecode/PMPrinterPrintWithFile/Introduction/Intro.html
|
||||
https://developer.apple.com/library/content/documentation/Cocoa/Conceptual/Printing/osxp_aboutprinting/osxp_aboutprt.html
|
||||
|
||||
- run os x code with `OBJC_DEBUG_MISSING_POOLS=YES` and other `OBJC_HELP=YES` options
|
||||
- turn off the autorelease pool to make sure we're not autoreleasing improperly
|
||||
|
||||
TODO investigate -Weverything in clang alongside -Wall in MSVC (and in gcc too maybe...)
|
||||
|
||||
mac os x accessibility
|
||||
- https://developer.apple.com/documentation/appkit/nsworkspace/1524656-accessibilitydisplayshoulddiffer?language=objc
|
||||
- https://developer.apple.com/documentation/appkit/nsworkspace/1526290-accessibilitydisplayshouldincrea?language=objc
|
||||
- https://developer.apple.com/documentation/appkit/nsworkspace/1533006-accessibilitydisplayshouldreduce?language=objc
|
||||
|
||||
uiEntry disabling bugs http://www.cocoabuilder.com/archive/cocoa/215525-nstextfield-bug-can-be.html
|
||||
uiMultilineEntry disabling https://developer.apple.com/library/content/qa/qa1461/_index.html
|
||||
|
||||
more TODOs:
|
||||
- make no guarantee about buildability of feature branches
|
||||
|
|
|
@ -0,0 +1,90 @@
|
|||
struct uiWindow {
|
||||
// constraints
|
||||
void (*onPositionChanged)(uiWindow *, void *);
|
||||
void *onPositionChangedData;
|
||||
BOOL suppressPositionChanged;
|
||||
// onContentSizeChanged
|
||||
};
|
||||
|
||||
@interface windowDelegateClass : NSObject<NSWindowDelegate> {
|
||||
// windowShouldClose:
|
||||
- (void)windowDidMove:(NSNotification *)note;
|
||||
// windowDidResize:
|
||||
@end
|
||||
|
||||
@implementation windowDelegateClass
|
||||
|
||||
// - (BOOL)windowShouldClose:(id)sender
|
||||
|
||||
// TODO doesn't happen live
|
||||
- (void)windowDidMove:(NSNotification *)note
|
||||
{
|
||||
uiWindow *w;
|
||||
|
||||
w = [self lookupWindow:((NSWindow *) [note object])];
|
||||
if (!w->suppressPositionChanged)
|
||||
(*(w->onPositionChanged))(w, w->onPositionChangedData);
|
||||
}
|
||||
|
||||
// - (void)windowDidResize:(NSNotification *)note
|
||||
|
||||
// void uiWindowSetTitle(uiWindow *w, const char *title)
|
||||
|
||||
void uiWindowPosition(uiWindow *w, int *x, int *y)
|
||||
{
|
||||
NSScreen *screen;
|
||||
NSRect r;
|
||||
|
||||
r = [w->window frame];
|
||||
*x = r.origin.x;
|
||||
// this is the right screen to use; thanks mikeash in irc.freenode.net/#macdev
|
||||
// -mainScreen is useless for positioning (it's just the key window's screen)
|
||||
// and we use -frame, not -visibleFrame, for dealing with absolute positions
|
||||
screen = (NSScreen *) [[NSScreen screens] objectAtIndex:0];
|
||||
*y = ([screen frame].size.height - r.origin.y) - r.size.height;
|
||||
}
|
||||
|
||||
void uiWindowSetPosition(uiWindow *w, int x, int y)
|
||||
{
|
||||
// -[NSWindow setFrameTopLeftPoint:] is acting weird so...
|
||||
NSRect r;
|
||||
NSScreen *screen;
|
||||
|
||||
// this fires windowDidMove:
|
||||
w->suppressPositionChanged = YES;
|
||||
r = [w->window frame];
|
||||
r.origin.x = x;
|
||||
screen = (NSScreen *) [[NSScreen screens] objectAtIndex:0];
|
||||
r.origin.y = [screen frame].size.height - (y + r.size.height);
|
||||
[w->window setFrameOrigin:r.origin];
|
||||
w->suppressPositionChanged = NO;
|
||||
}
|
||||
|
||||
void uiWindowCenter(uiWindow *w)
|
||||
{
|
||||
w->suppressPositionChanged = YES;
|
||||
[w->window center];
|
||||
w->suppressPositionChanged = NO;
|
||||
}
|
||||
|
||||
void uiWindowOnPositionChanged(uiWindow *w, void (*f)(uiWindow *, void *), void *data)
|
||||
{
|
||||
w->onPositionChanged = f;
|
||||
w->onPositionChangedData = data;
|
||||
}
|
||||
|
||||
// void uiWindowContentSize(uiWindow *w, int *width, int *height)
|
||||
|
||||
// static int defaultOnClosing(uiWindow *w, void *data)
|
||||
|
||||
static void defaultOnPositionContentSizeChanged(uiWindow *w, void *data)
|
||||
{
|
||||
// do nothing
|
||||
}
|
||||
|
||||
uiWindow *uiNewWindow(const char *title, int width, int height, int hasMenubar)
|
||||
{
|
||||
// uiWindowOnClosing(w, defaultOnClosing, NULL);
|
||||
uiWindowOnPositionChanged(w, defaultOnPositionContentSizeChanged, NULL);
|
||||
// uiWindowOnContentSizeChanged(w, defaultOnPositionContentSizeChanged, NULL);
|
||||
}
|
|
@ -0,0 +1,65 @@
|
|||
static uiSpinbox *x, *y;
|
||||
|
||||
static void moveX(uiSpinbox *s, void *data)
|
||||
{
|
||||
uiWindow *w = uiWindow(data);
|
||||
int xp, yp;
|
||||
|
||||
uiWindowPosition(w, &xp, &yp);
|
||||
xp = uiSpinboxValue(x);
|
||||
uiWindowSetPosition(w, xp, yp);
|
||||
}
|
||||
|
||||
static void moveY(uiSpinbox *s, void *data)
|
||||
{
|
||||
uiWindow *w = uiWindow(data);
|
||||
int xp, yp;
|
||||
|
||||
uiWindowPosition(w, &xp, &yp);
|
||||
yp = uiSpinboxValue(y);
|
||||
uiWindowSetPosition(w, xp, yp);
|
||||
}
|
||||
|
||||
static void updatepos(uiWindow *w)
|
||||
{
|
||||
int xp, yp;
|
||||
|
||||
uiWindowPosition(w, &xp, &yp);
|
||||
uiSpinboxSetValue(x, xp);
|
||||
uiSpinboxSetValue(y, yp);
|
||||
}
|
||||
|
||||
static void center(uiButton *b, void *data)
|
||||
{
|
||||
uiWindow *w = uiWindow(data);
|
||||
|
||||
uiWindowCenter(w);
|
||||
updatepos(w);
|
||||
}
|
||||
|
||||
void onMove(uiWindow *w, void *data)
|
||||
{
|
||||
printf("move\n");
|
||||
updatepos(w);
|
||||
}
|
||||
|
||||
uiBox *makePage15(uiWindow *w)
|
||||
{
|
||||
hbox = newHorizontalBox();
|
||||
// TODO if I make this 1 and not add anything else AND not call uiWindowOnPositionChanged(), on OS X the box won't be able to grow vertically
|
||||
uiBoxAppend(page15, uiControl(hbox), 0);
|
||||
|
||||
uiBoxAppend(hbox, uiControl(uiNewLabel("Position")), 0);
|
||||
x = uiNewSpinbox(INT_MIN, INT_MAX);
|
||||
uiBoxAppend(hbox, uiControl(x), 1);
|
||||
y = uiNewSpinbox(INT_MIN, INT_MAX);
|
||||
uiBoxAppend(hbox, uiControl(y), 1);
|
||||
button = uiNewButton("Center");
|
||||
uiBoxAppend(hbox, uiControl(button), 0);
|
||||
|
||||
uiSpinboxOnChanged(x, moveX, w);
|
||||
uiSpinboxOnChanged(y, moveY, w);
|
||||
uiButtonOnClicked(button, center, w);
|
||||
uiWindowOnPositionChanged(w, onMove, NULL);
|
||||
updatepos(w);
|
||||
}
|
|
@ -0,0 +1,6 @@
|
|||
// uiWindowSetTitle
|
||||
_UI_EXTERN void uiWindowPosition(uiWindow *w, int *x, int *y);
|
||||
_UI_EXTERN void uiWindowSetPosition(uiWindow *w, int x, int y);
|
||||
_UI_EXTERN void uiWindowCenter(uiWindow *w);
|
||||
_UI_EXTERN void uiWindowOnPositionChanged(uiWindow *w, void (*f)(uiWindow *, void *), void *data);
|
||||
// uiWindowContentSize
|
|
@ -0,0 +1,97 @@
|
|||
struct uiWindow {
|
||||
// void *onClosingData;
|
||||
void (*onPositionChanged)(uiWindow *, void *);
|
||||
void *onPositionChangedData;
|
||||
gboolean changingPosition;
|
||||
// void (*onContentSizeChanged)(uiWindow *, void *);
|
||||
};
|
||||
|
||||
// static gboolean onClosing(GtkWidget *win, GdkEvent *e, gpointer data)
|
||||
|
||||
static gboolean onConfigure(GtkWidget *win, GdkEvent *e, gpointer data)
|
||||
{
|
||||
uiWindow *w = uiWindow(data);
|
||||
|
||||
// there doesn't seem to be a way to determine if only moving or only resizing is happening :/
|
||||
if (w->changingPosition)
|
||||
w->changingPosition = FALSE;
|
||||
else
|
||||
(*(w->onPositionChanged))(w, w->onPositionChangedData);
|
||||
// always continue handling
|
||||
return FALSE;
|
||||
}
|
||||
|
||||
// static void onSizeAllocate(GtkWidget *widget, GdkRectangle *allocation, gpointer data)
|
||||
|
||||
// static int defaultOnClosing(uiWindow *w, void *data)
|
||||
|
||||
static void defaultOnPositionContentSizeChanged(uiWindow *w, void *data)
|
||||
{
|
||||
// do nothing
|
||||
}
|
||||
|
||||
// static void uiWindowDestroy(uiControl *c)
|
||||
|
||||
// void uiWindowSetTitle(uiWindow *w, const char *title)
|
||||
|
||||
// TODO allow specifying either as NULL on all platforms
|
||||
void uiWindowPosition(uiWindow *w, int *x, int *y)
|
||||
{
|
||||
gint rx, ry;
|
||||
|
||||
gtk_window_get_position(w->window, &rx, &ry);
|
||||
*x = rx;
|
||||
*y = ry;
|
||||
}
|
||||
|
||||
void uiWindowSetPosition(uiWindow *w, int x, int y)
|
||||
{
|
||||
w->changingPosition = TRUE;
|
||||
gtk_window_move(w->window, x, y);
|
||||
// gtk_window_move() is asynchronous
|
||||
// we need to wait for a configure-event
|
||||
// thanks to hergertme in irc.gimp.net/#gtk+
|
||||
while (w->changingPosition)
|
||||
if (!uiMainStep(1))
|
||||
break; // stop early if uiQuit() called
|
||||
}
|
||||
|
||||
void uiWindowCenter(uiWindow *w)
|
||||
{
|
||||
gint x, y;
|
||||
GtkAllocation winalloc;
|
||||
GdkWindow *gdkwin;
|
||||
GdkScreen *screen;
|
||||
GdkRectangle workarea;
|
||||
|
||||
gtk_widget_get_allocation(w->widget, &winalloc);
|
||||
gdkwin = gtk_widget_get_window(w->widget);
|
||||
screen = gdk_window_get_screen(gdkwin);
|
||||
gdk_screen_get_monitor_workarea(screen,
|
||||
gdk_screen_get_monitor_at_window(screen, gdkwin),
|
||||
&workarea);
|
||||
|
||||
x = (workarea.width - winalloc.width) / 2;
|
||||
y = (workarea.height - winalloc.height) / 2;
|
||||
// TODO move up slightly? see what Mutter or GNOME Shell or GNOME Terminal do(es)?
|
||||
uiWindowSetPosition(w, x, y);
|
||||
}
|
||||
|
||||
// TODO this and size changed get set during uiWindowDestroy
|
||||
void uiWindowOnPositionChanged(uiWindow *w, void (*f)(uiWindow *, void *), void *data)
|
||||
{
|
||||
w->onPositionChanged = f;
|
||||
w->onPositionChangedData = data;
|
||||
}
|
||||
|
||||
// void uiWindowContentSize(uiWindow *w, int *width, int *height)
|
||||
|
||||
uiWindow *uiNewWindow(const char *title, int width, int height, int hasMenubar)
|
||||
{
|
||||
// g_signal_connect(w->widget, "delete-event", G_CALLBACK(onClosing), w);
|
||||
g_signal_connect(w->widget, "configure-event", G_CALLBACK(onConfigure), w);
|
||||
// g_signal_connect(w->childHolderWidget, "size-allocate", G_CALLBACK(onSizeAllocate), w);
|
||||
// uiWindowOnClosing(w, defaultOnClosing, NULL);
|
||||
uiWindowOnPositionChanged(w, defaultOnPositionContentSizeChanged, NULL);
|
||||
// uiWindowOnContentSizeChanged(w, defaultOnPositionContentSizeChanged, NULL);
|
||||
}
|
|
@ -0,0 +1,86 @@
|
|||
struct uiWindow {
|
||||
// BOOL hasMenubar;
|
||||
void (*onPositionChanged)(uiWindow *, void *);
|
||||
void *onPositionChangedData;
|
||||
BOOL changingPosition; // to avoid triggering the above when programmatically doing this
|
||||
// void (*onContentSizeChanged)(uiWindow *, void *);
|
||||
};
|
||||
|
||||
static LRESULT CALLBACK windowWndProc(HWND hwnd, UINT uMsg, WPARAM wParam, LPARAM lParam)
|
||||
{
|
||||
case WM_WINDOWPOSCHANGED:
|
||||
if ((wp->flags & SWP_NOMOVE) == 0)
|
||||
if (!w->changingPosition)
|
||||
(*(w->onPositionChanged))(w, w->onPositionChangedData);
|
||||
// and continue anyway
|
||||
// if ((wp->flags & SWP_NOSIZE) != 0)
|
||||
}
|
||||
|
||||
// static int defaultOnClosing(uiWindow *w, void *data)
|
||||
|
||||
static void defaultOnPositionContentSizeChanged(uiWindow *w, void *data)
|
||||
{
|
||||
// do nothing
|
||||
}
|
||||
|
||||
// static std::map<uiWindow *, bool> windows;
|
||||
|
||||
// void uiWindowSetTitle(uiWindow *w, const char *title)
|
||||
|
||||
void uiWindowPosition(uiWindow *w, int *x, int *y)
|
||||
{
|
||||
RECT r;
|
||||
|
||||
uiWindowsEnsureGetWindowRect(w->hwnd, &r);
|
||||
*x = r.left;
|
||||
*y = r.top;
|
||||
}
|
||||
|
||||
void uiWindowSetPosition(uiWindow *w, int x, int y)
|
||||
{
|
||||
w->changingPosition = TRUE;
|
||||
if (SetWindowPos(w->hwnd, NULL, x, y, 0, 0, SWP_NOACTIVATE | SWP_NOOWNERZORDER | SWP_NOSIZE | SWP_NOZORDER) == 0)
|
||||
logLastError(L"error moving window");
|
||||
w->changingPosition = FALSE;
|
||||
}
|
||||
|
||||
// static void windowMonitorRect(HWND hwnd, RECT *r)
|
||||
|
||||
// TODO use the work rect instead?
|
||||
void uiWindowCenter(uiWindow *w)
|
||||
{
|
||||
RECT wr, mr;
|
||||
int x, y;
|
||||
LONG wwid, mwid;
|
||||
LONG wht, mht;
|
||||
|
||||
uiWindowsEnsureGetWindowRect(w->hwnd, &wr);
|
||||
windowMonitorRect(w->hwnd, &mr);
|
||||
wwid = wr.right - wr.left;
|
||||
mwid = mr.right - mr.left;
|
||||
x = (mwid - wwid) / 2;
|
||||
wht = wr.bottom - wr.top;
|
||||
mht = mr.bottom - mr.top;
|
||||
y = (mht - wht) / 2;
|
||||
// y is now evenly divided, however https://msdn.microsoft.com/en-us/library/windows/desktop/dn742502(v=vs.85).aspx says that 45% should go above and 55% should go below
|
||||
// so just move 5% of the way up
|
||||
// TODO should this be on the work area?
|
||||
// TODO is this calculation correct?
|
||||
y -= y / 20;
|
||||
uiWindowSetPosition(w, x, y);
|
||||
}
|
||||
|
||||
void uiWindowOnPositionChanged(uiWindow *w, void (*f)(uiWindow *, void *), void *data)
|
||||
{
|
||||
w->onPositionChanged = f;
|
||||
w->onPositionChangedData = data;
|
||||
}
|
||||
|
||||
// void uiWindowContentSize(uiWindow *w, int *width, int *height)
|
||||
|
||||
uiWindow *uiNewWindow(const char *title, int width, int height, int hasMenubar)
|
||||
{
|
||||
// uiWindowOnClosing(w, defaultOnClosing, NULL);
|
||||
uiWindowOnPositionChanged(w, defaultOnPositionContentSizeChanged, NULL);
|
||||
// uiWindowOnContentSizeChanged(w, defaultOnPositionContentSizeChanged, NULL);
|
||||
}
|
|
@ -0,0 +1 @@
|
|||
Yes, you keep ownership of the uiAreaHandler. libui only cares about the address you give uiNewArea(); it doesn't copy anything. You can even use the same uiAreaHandler on multiple uiAreas, which is why you get the uiArea as a parameter in each function.
|
|
@ -0,0 +1,750 @@
|
|||
// pseudo-go
|
||||
|
||||
func (f *CTFont) IsRegistered() bool {
|
||||
n := f.Attribute(kCTFontRegistrationScopeAttribute)
|
||||
if n == nil {
|
||||
return false
|
||||
}
|
||||
return n.(*CFNumber).Uint32Value() == kCTFontManagerScopeNone
|
||||
}
|
||||
|
||||
// this type is in libFontRegistry.dylib; functions like x_list.Prepend() are called things like x_list_prepend() there
|
||||
type x_list struct {
|
||||
Data interface{}
|
||||
Next *x_list
|
||||
}
|
||||
|
||||
func (x *x_list) Prepend(data interface{}) *x_list {
|
||||
y := malloc(sizeof (x_list))
|
||||
if y != nil {
|
||||
y.data = data
|
||||
y.next = x
|
||||
return y
|
||||
}
|
||||
return x
|
||||
}
|
||||
|
||||
func (x *x_list) Reverse() *x_list {
|
||||
if x == nil {
|
||||
return nil
|
||||
}
|
||||
|
||||
var old, next *x_list
|
||||
|
||||
next = nil
|
||||
for {
|
||||
old = x
|
||||
x = old.next
|
||||
old.next = next
|
||||
next = old
|
||||
if x == nil {
|
||||
break
|
||||
}
|
||||
}
|
||||
return old
|
||||
}
|
||||
|
||||
func (x *x_list) Concat(y *x_list) *x_list {
|
||||
if x == nil {
|
||||
return y
|
||||
}
|
||||
start := x
|
||||
z := x
|
||||
for {
|
||||
x = z
|
||||
z = z.next
|
||||
if z == nil {
|
||||
break
|
||||
}
|
||||
}
|
||||
x.next = y
|
||||
return start
|
||||
}
|
||||
|
||||
// based on CoreGraphics dylib's _CGFontCopyName
|
||||
// note that this is different from the public API function CGFontCopyPostScriptName() (which is font type-independent)
|
||||
// also note that in reality these keys are strings but the implementation of the function turns them into ints and only uses them as such
|
||||
const (
|
||||
kCGFontNameKeyPostScriptName = 0x6
|
||||
kCGFontNameKeyPreferredSubfamily = 0x11
|
||||
kCGFontNameKeyFontSubfamily = 0x2
|
||||
kCGFontNameKeyFullName = 0x4
|
||||
kCGFontNameKeyPreferredFamily = 0x10
|
||||
kCGFontNameKeyFontFamily = 0x1
|
||||
)
|
||||
func (f *CGFont) CopyName(key int) (string, bool) {
|
||||
table := f.TableForTag('name')
|
||||
b := table.Bytes()
|
||||
n := table.Len()
|
||||
|
||||
// this code looks weird, but we're imitating the assembly, or the effective effects thereof
|
||||
offCount := uint16(0)
|
||||
offStringOffset := uint16(2)
|
||||
if n > 1 {
|
||||
offCount = 2
|
||||
offStringOffset = 4
|
||||
}
|
||||
|
||||
count := uint16(0)
|
||||
if int(offCount) <= n {
|
||||
count = uint16be(b[offCount:offCount + 2])
|
||||
}
|
||||
|
||||
offNameRecord := offStringOffset + 2
|
||||
stringOffset := uint16(0)
|
||||
if int(offNameRecord) <= n {
|
||||
stringOffset = uint16be(b[offStringOffset:offStringOffset + 2])
|
||||
}
|
||||
|
||||
type NameRecord struct {
|
||||
PlatformID uint16
|
||||
PlatformSpecificID uint16
|
||||
LanguageID uint16
|
||||
NameID uint16
|
||||
Length uint16
|
||||
Offset uint16
|
||||
}
|
||||
|
||||
var nameList *x_list
|
||||
|
||||
addrStrings := offNameRecords + (12 * count)
|
||||
if addrStrings != stringOffset {
|
||||
goto hasLanguageTags
|
||||
}
|
||||
pos := offNameRecords
|
||||
if count == 0 {
|
||||
// TODO note assembly logic here
|
||||
} else {
|
||||
for {
|
||||
var nr NameRecord
|
||||
|
||||
nr.PlatformID = 0
|
||||
next := pos + 2
|
||||
if int(next) <= n {
|
||||
nr.PlatformID = uint16be(b[pos:pos + 2])
|
||||
pos = next
|
||||
}
|
||||
|
||||
nr.PlatformSpecificID = 0
|
||||
next = pos + 2
|
||||
if int(next) <= n {
|
||||
nr.PlatformSpecificID = uint16be(b[pos:pos + 2])
|
||||
pos = next
|
||||
}
|
||||
|
||||
nr.LanguageID = 0
|
||||
next = pos + 2
|
||||
if int(next) <= n {
|
||||
nr.LanguageID = uint16be(b[pos:pos + 2])
|
||||
pos = next
|
||||
}
|
||||
|
||||
nr.NameID = 0
|
||||
next = pos + 2
|
||||
if int(next) <= n {
|
||||
nr.NameID = uint16be(b[pos:pos + 2])
|
||||
pos = next
|
||||
}
|
||||
|
||||
nr.Length = 0
|
||||
next = pos + 2
|
||||
if int(next) <= n {
|
||||
nr.Length = uint16be(b[pos:pos + 2])
|
||||
pos = next
|
||||
}
|
||||
|
||||
nr.Offset = 0
|
||||
next = pos + 2
|
||||
if int(next) <= n {
|
||||
nr.Offset = uint16be(b[pos:pos + 2])
|
||||
pos = next
|
||||
}
|
||||
|
||||
strpos := stringOffset + nr.Offset
|
||||
if strpos >= n {
|
||||
// TODO put comment about imitating the assembly comparisons here
|
||||
} else {
|
||||
realLen := nr.Length
|
||||
strend = strpos + nr.Length
|
||||
if strend > n {
|
||||
realLen = nr.Length - strpos
|
||||
strend = strpos + realLen
|
||||
}
|
||||
b := malloc(12 + realLen + 1)
|
||||
if b != nil {
|
||||
name := (*sfnt_name_t)(b)
|
||||
name.PlatformID = nr.PlatformID
|
||||
name.PlatformSpecificID = nr.PlatformSpecificID
|
||||
name.LanguageID = nr.LanguageID
|
||||
name.NameID = nr.NameID
|
||||
name.Length = realLen
|
||||
memcpy(&(name.Name), b[strpos:strend], realLen)
|
||||
name.Name[realLen] = 0
|
||||
nameList = nameList.Prepend(name)
|
||||
}
|
||||
}
|
||||
count--
|
||||
if count == 0 {
|
||||
break
|
||||
}
|
||||
}
|
||||
}
|
||||
nameList = nameList.Reverse()
|
||||
|
||||
hasLanguageTags:
|
||||
add_localized_names := func(platformID uint16, platformSpecificID uint16, to *x_list) *x_list {
|
||||
out := (*x_list)(nil)
|
||||
if nameList == nil {
|
||||
xx TODO logic verbatim etc.
|
||||
} else {
|
||||
x := nameList
|
||||
for {
|
||||
name := (*sfnt_name_t)(x.data)
|
||||
if name.PlatformID != platformID {
|
||||
xx TODO
|
||||
} else {
|
||||
if platformSpecificID == 0xFFFF || name.PlatformSpecificID == platformSpecificID {
|
||||
out = out.Prepend(name)
|
||||
}
|
||||
}
|
||||
x = x.next
|
||||
if x == nil {
|
||||
break
|
||||
}
|
||||
}
|
||||
}
|
||||
out = out.Reverse()
|
||||
return to.Concat(out)
|
||||
}
|
||||
localized := (*x_list)(nil)
|
||||
localized = add_localized_names(0x1, 0xFFFF, localized)
|
||||
localized = add_localized_names(0, 0xFFFF, localized)
|
||||
localized = add_localized_names(0x3, 0xFFFF, localized)
|
||||
localized = add_localized_names(0x1, 0, localized)
|
||||
localized = add_localized_names(0x3, 0x9, localized)
|
||||
localized = add_localized_names(0x3, 0x409, localized)
|
||||
|
||||
sysLocale := CFLocaleGetSystem()
|
||||
|
||||
}
|
||||
|
||||
// based on libFontRegistry.dylib's __ZNK8OS2Table15DetermineWeightERf — OS2Table::DetermineWeight(float&) const
|
||||
func RegistryDetermineOS2Weight(table *CFData) (float32, bool) {
|
||||
if table == nil {
|
||||
return 0, false
|
||||
}
|
||||
if table.Len() < 78 {
|
||||
return 0, false
|
||||
}
|
||||
|
||||
b := table.Bytes()
|
||||
usWeightClass := uint16be(b[4:6])
|
||||
if usWeightClass >= 10 {
|
||||
// do nothing; we are preserving the original asm comparisons
|
||||
} else {
|
||||
usWeightClass *= 100
|
||||
}
|
||||
/* TODO:
|
||||
000000000000b37e mov dx, word [rax+4]
|
||||
000000000000b382 mov cx, dx
|
||||
000000000000b385 rol cx, 0x8
|
||||
000000000000b389 movzx esi, cx
|
||||
000000000000b38c imul ecx, ecx, 100
|
||||
000000000000b38f cmp esi, 10
|
||||
000000000000b392 cmovae cx, si
|
||||
000000000000b396 test dx, dx
|
||||
000000000000b399 cmove cx, si
|
||||
what's the function of the last two instructions? */
|
||||
|
||||
// note that this is an unsigned comparison, so underflow will result in a number > 998
|
||||
// the effect is the same as (usWeightClass == 0) || (usWeightClass >= 1000)
|
||||
if (usWeightClass - 1) > 998 {
|
||||
// note the - 2 here; the switch cases below reflect that!
|
||||
// also note that b[0x22] and panose will be unsigned, so underflow will result in a number > 9
|
||||
panose := b[0x22] - 2
|
||||
if panose > 9 {
|
||||
return 0, false
|
||||
}
|
||||
switch panose {
|
||||
case 0:
|
||||
return float32as(-0.500000, 0xbf000000), true
|
||||
case 1:
|
||||
return float32as(-0.400000, 0xbecccccd), true
|
||||
case 2:
|
||||
// yes, this returns false; I don't know why
|
||||
return float32as(-0.300000, 0xbe99999a), false
|
||||
case 3:
|
||||
return float32as(-0.230000, 0xbe6b851f), true
|
||||
case 4:
|
||||
return float32as(0.230000, 0x3e6b851f), true
|
||||
case 5:
|
||||
return float32as(0.250000, 0x3e800000), true
|
||||
case 6:
|
||||
return float32as(0.400000, 0x3ecccccd), true
|
||||
case 7:
|
||||
return float32as(0.560000, 0x3f0f5c29), true
|
||||
case 8:
|
||||
return float32as(0.620000, 0x3f1eb852), true
|
||||
case 9:
|
||||
return float32as(0.800000, 0x3f4ccccd), true
|
||||
}
|
||||
// should not reach here
|
||||
}
|
||||
|
||||
// let's mimic the assembly here too
|
||||
// the gotos avoid the massive if nesting
|
||||
// also note I'm using Go idioms and not saying "else return", imagine those if you must
|
||||
if usWeightClass > 100 {
|
||||
if usWeightClass > 200 {
|
||||
goto do201AndUp
|
||||
}
|
||||
return float32as(-0.500000, 0xbf000000), true
|
||||
}
|
||||
return float32as(-0.800000, 0xbf4ccccd), true
|
||||
|
||||
do201AndUp:
|
||||
if usWeightClass > 300 {
|
||||
if usWeightClass > 400 {
|
||||
goto do401AndUp
|
||||
}
|
||||
return float32as(0.000000, 0x0), true
|
||||
}
|
||||
return float32as(-0.400000, 0xbecccccd), true
|
||||
|
||||
do401AndUp:
|
||||
if usWeightClass > 500 {
|
||||
if usWeightClass > 600 {
|
||||
goto do601AndUp
|
||||
}
|
||||
return float32as(0.250000, 0x3e800000), true
|
||||
}
|
||||
return float32as(0.230000, 0x3e6b851f), true
|
||||
|
||||
do601AndUp:
|
||||
if usWeightClass > 700 {
|
||||
if usWeightClass > 800 {
|
||||
goto do801AndUp
|
||||
}
|
||||
return float32as(0.500000, 0x3f000000), true
|
||||
}
|
||||
return float32as(0.400000, 0x3ecccccd), true
|
||||
|
||||
do801AndUp:
|
||||
if usWeightClass > 900 {
|
||||
if usWeightClass > 950 {
|
||||
return float32(0.800000, 0x3f4ccccd), true
|
||||
}
|
||||
return float32(0.750000, 0x3f400000), true
|
||||
}
|
||||
return float32as(0.620000, 0x3f1eb852), true
|
||||
}
|
||||
|
||||
// based on libFontRegistry.dylib's __ZN11TFontTraitsC2EP6CGFontRK13TFontMetadata — TFontTraits::TFontTraits(CGFont*, TFontMetadata const&)
|
||||
func (f *Font) WeightFromFontRegistry32() float32 {
|
||||
var weight float32
|
||||
var hasWeight bool = false
|
||||
|
||||
cgfont := f.CGFont()
|
||||
if f.RegistryHasMetadata() {
|
||||
wv := f.RegistryMetadataValueForKey("MTD_Typeface_Weight_VisualDescriptor")
|
||||
if wv != nil {
|
||||
if wn, ok := wv.(string); ok {
|
||||
// note: uses CFStringCompare(0)
|
||||
switch wn {
|
||||
case "reg":
|
||||
weight = float32as(0.000000, 0x0)
|
||||
hasWeight = true
|
||||
case "semi":
|
||||
weight = float32as(0.300000, 0x3e99999a)
|
||||
hasWeight = true
|
||||
case "bold":
|
||||
weight = float32as(0.400000, 0x3ecccccd)
|
||||
hasWeight = true
|
||||
case "light":
|
||||
weight = float32as(-0.400000, 0xbecccccd)
|
||||
hasWeight = true
|
||||
case "med":
|
||||
weight = float32as(0.230000, 0x3e6b851f)
|
||||
hasWeight = true
|
||||
case "heavy":
|
||||
weight = float32as(0.560000, 0x3f0f5c29)
|
||||
hasWeight = true
|
||||
case "black":
|
||||
weight = float32as(0.620000, 0x3f1eb852)
|
||||
hasWeight = true
|
||||
case "thin":
|
||||
weight = float32as(-0.600000, 0xbf19999a)
|
||||
hasWeight = true
|
||||
case "ulight":
|
||||
weight = float32as(-0.800000, 0xbf4ccccd)
|
||||
hasWeight = true
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
cgpsname, ok := cgfont.CopyName(kCGFontNameKeyPostScriptName)
|
||||
if ok {
|
||||
// note: uses CFStringCompare(0)
|
||||
switch cgpsname {
|
||||
case "LucidaGrande",
|
||||
".LucidaGrandeUI",
|
||||
".Keyboard":
|
||||
weight = float32as(0.000000, 0x0)
|
||||
hasWeight = true
|
||||
case "STHeiti":
|
||||
weight = float32as(0.240000, 0x3e75c28f)
|
||||
hasWeight = true
|
||||
case "STXihei":
|
||||
weight = float32as(-0.100000, 0xbdcccccd)
|
||||
hasWeight = true
|
||||
case "TimesNewRomanPSMT":
|
||||
weight = float32as(0.000000, 0x0)
|
||||
hasWeight = true
|
||||
}
|
||||
}
|
||||
|
||||
styleGlossaryStrings := []int{
|
||||
kCGFontNameKeyPreferredSubfamily,
|
||||
kCGFontNameKeyFontSubfamily,
|
||||
kCGFontNameKeyFullName,
|
||||
kCGFontNameKeyPreferredFamily,
|
||||
kCGFontNameKeyFontFamily,
|
||||
}
|
||||
weightNameMap := []struct {
|
||||
key string
|
||||
val float32
|
||||
}{
|
||||
{ "Ultra Light", float32as(-0.800000f, 0xbf4ccccd) },
|
||||
{ "Ultra Black", float32as(0.750000f, 0x3f400000) },
|
||||
{ "Extra Light", float32as(-0.500000f, 0xbf000000) },
|
||||
{ "UltraBlack", float32as(0.750000f, 0x3f400000) },
|
||||
{ "ExtraBlack", float32as(0.800000f, 0x3f4ccccd) },
|
||||
{ "UltraLight", float32as(-0.800000f, 0xbf4ccccd) },
|
||||
{ "ExtraLight", float32as(-0.500000f, 0xbf000000) },
|
||||
{ "Ultra Thin", float32as(-0.800000f, 0xbf4ccccd) },
|
||||
{ "Extra Thin", float32as(-0.800000f, 0xbf4ccccd) },
|
||||
{ "Heavy Face", float32as(0.560000f, 0x3f0f5c29) },
|
||||
{ "Semi Light", float32as(-0.200000f, 0xbe4ccccd) },
|
||||
{ "Extra Bold", float32as(0.500000f, 0x3f000000) },
|
||||
{ "Ultra Bold", float32as(0.700000f, 0x3f333333) },
|
||||
{ "HeavyFace", float32as(0.560000f, 0x3f0f5c29) },
|
||||
{ "ExtraBold", float32as(0.500000f, 0x3f000000) },
|
||||
{ "UltraBold", float32as(0.700000f, 0x3f333333) },
|
||||
{ "Ext Black", float32as(0.800000f, 0x3f4ccccd) },
|
||||
{ "SemiLight", float32as(-0.200000f, 0xbe4ccccd) },
|
||||
{ "Demi Bold", float32as(0.250000f, 0x3e800000) },
|
||||
{ "Semi Bold", float32as(0.300000f, 0x3e99999a) },
|
||||
{ "Ext Light", float32as(-0.500000f, 0xbf000000) },
|
||||
{ "Ext Bold", float32as(0.500000f, 0x3f000000) },
|
||||
{ "DemiBold", float32as(0.250000f, 0x3e800000) },
|
||||
{ "SemiBold", float32as(0.300000f, 0x3e99999a) },
|
||||
{ "HairLine", float32as(-0.800000f, 0xbf4ccccd) },
|
||||
{ "Ext Thin", float32as(-0.800000f, 0xbf4ccccd) },
|
||||
{ "Medium", float32as(0.230000f, 0x3e6b851f) },
|
||||
{ "Poster", float32as(0.800000f, 0x3f4ccccd) },
|
||||
{ "Light", float32as(-0.400000f, 0xbecccccd) },
|
||||
{ "Ultra", float32as(0.500000f, 0x3f000000) },
|
||||
{ "Heavy", float32as(0.560000f, 0x3f0f5c29) },
|
||||
{ "Extra", float32as(0.500000f, 0x3f000000) },
|
||||
{ "Black", float32as(0.620000f, 0x3f1eb852) },
|
||||
{ "Super", float32as(0.620000f, 0x3f1eb852) },
|
||||
{ "Obese", float32as(0.850000f, 0x3f59999a) },
|
||||
{ "Lite", float32as(-0.400000f, 0xbecccccd) },
|
||||
{ "Book", float32as(-0.230000f, 0xbe6b851f) },
|
||||
{ "Demi", float32as(0.250000f, 0x3e800000) },
|
||||
{ "Semi", float32as(0.300000f, 0x3e99999a) },
|
||||
{ "Thin", float32as(-0.500000f, 0xbf000000) },
|
||||
{ "Bold", float32as(0.400000f, 0x3ecccccd) },
|
||||
{ "Nord", float32as(0.800000f, 0x3f4ccccd) },
|
||||
{ "Fat", float32as(0.750000f, 0x3f400000) },
|
||||
{ "W1", float32as(-0.230000f, 0xbe6b851f) },
|
||||
{ "W2", float32as(-0.500000f, 0xbf000000) },
|
||||
{ "W3", float32as(-0.230000f, 0xbe6b851f) },
|
||||
{ "W4", float32as(0.000000f, 0x0) },
|
||||
{ "W5", float32as(0.230000f, 0x3e6b851f) },
|
||||
{ "W6", float32as(0.300000f, 0x3e99999a) },
|
||||
{ "W7", float32as(0.440000f, 0x3ee147ae) },
|
||||
{ "W8", float32as(0.540000f, 0x3f0a3d71) },
|
||||
{ "W9", float32as(0.620000f, 0x3f1eb852) },
|
||||
}
|
||||
for _, key := range styleGlossaryStrings {
|
||||
if hasWeight {
|
||||
break
|
||||
}
|
||||
str, ok := cgfont.CopyName(key)
|
||||
if !ok {
|
||||
continue
|
||||
}
|
||||
for _, m := range weightNameMap {
|
||||
if str.FindWithOptions(m.key, CFRangeMake(0, str.CFStringLength()), kCFCompareCaseInsensitive | kCFCompareBackwards | kCFCompareNonliteral, nil) {
|
||||
weight = m.val
|
||||
hasWeight = true
|
||||
break
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if !hasWeight {
|
||||
os2table := cgfont.TableForTag('OS/2')
|
||||
weight, hasWeight = RegistryDetermineOS2Weight(os2table)
|
||||
}
|
||||
|
||||
if !hasWeight {
|
||||
headtable := cgfont.TableForTag('head')
|
||||
if headtable != nil {
|
||||
if headtable.Len() >= 54 {
|
||||
b := headtable.Bytes()
|
||||
if (b[0x2d] & 1) != 0 {
|
||||
weight = float32as(0.400000, 0x3ecccccd)
|
||||
hasWeight = true
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
styleGlossaryAbbreviationKeys := []int{
|
||||
kCGFontNameKeyPreferredSubfamily,
|
||||
kCGFontNameKeyFontSubfamily,
|
||||
}
|
||||
abbreviatedWeightNameMap := []struct {
|
||||
key string
|
||||
val float32
|
||||
}{
|
||||
{ "EL", float32as(-0.200000, 0xbe4ccccd) },
|
||||
{ "EB", float32as(0.500000, 0x3f000000) },
|
||||
{ "SB", float32as(0.300000, 0x3e99999a) },
|
||||
{ "UH", float32as(0.800000, 0x3f4ccccd) },
|
||||
{ "U", float32as(0.700000, 0x3f333333) },
|
||||
{ "L", float32as(-0.400000, 0xbecccccd) },
|
||||
{ "H", float32as(0.560000, 0x3f0f5c29) },
|
||||
{ "B", float32as(0.400000, 0x3ecccccd) },
|
||||
{ "M", float32as(0.230000, 0x3e6b851f) },
|
||||
{ "R", float32as(0.000000, 0x0) },
|
||||
}
|
||||
if !hasWeight {
|
||||
for _, key := range styleGlossaryAbbreviationStrings {
|
||||
str, ok := cgfont.CopyName(key)
|
||||
if !ok {
|
||||
continue
|
||||
}
|
||||
for _, m := range abbreviatedWeightNameMap {
|
||||
if str.Compare(m.key, kCFCompareCaseInsensitive) == kCFCompareEqualTo {
|
||||
weight = m.val
|
||||
hasWeight = true
|
||||
break
|
||||
}
|
||||
}
|
||||
if hasWeight {
|
||||
break
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if !hasWeight {
|
||||
return float32as(0.000000, 0x0)
|
||||
}
|
||||
return weight
|
||||
}
|
||||
|
||||
// because Core Text gets registry traits as a CFDictionary, convert the float to a double with CFNumber as that is what actually would be done
|
||||
func (f *Font) WeightFromFontRegistry() float64 {
|
||||
return CFNumberWithFloat32(f.WeightFromFontRegistry32()).Float64Value()
|
||||
}
|
||||
|
||||
// based on CoreText dylib's __Z13WeightOfClasst — WeightOfClass(unsigned short)
|
||||
func CoreText_WeightOfClass(usWeightClass uint16) float64 {
|
||||
if usWeightClass >= 11 {
|
||||
// do nothing; we are preserving the original asm comparisons
|
||||
// and yes, this one is 11, but the one above is 10
|
||||
} else {
|
||||
usWeightClass *= 100
|
||||
}
|
||||
|
||||
// figure out what two floats our weight will be between
|
||||
i := usWeightClass / 100
|
||||
j := i + 1
|
||||
if j > 10 {
|
||||
j = 10
|
||||
}
|
||||
b := float64(i * 100)
|
||||
c := float64(j * 100)
|
||||
|
||||
a := float64(0)
|
||||
if b != c {
|
||||
a = float64(usWeightClass)
|
||||
a -= b
|
||||
c -= b
|
||||
a /= c
|
||||
}
|
||||
scales := []float32{
|
||||
float32as(-1.000000, 0xbf800000),
|
||||
float32as(-0.700000, 0xbf333333),
|
||||
float32as(-0.500000, 0xbf000000),
|
||||
float32as(-0.230000, 0xbe6b851f),
|
||||
float32as(0.000000, 0x0),
|
||||
float32as(0.200000, 0x3e4ccccd),
|
||||
float32as(0.300000, 0x3e99999a),
|
||||
float32as(0.400000, 0x3ecccccd),
|
||||
float32as(0.600000, 0x3f19999a),
|
||||
float32as(0.800000, 0x3f4ccccd),
|
||||
float32as(1.000000, 0x3f800000),
|
||||
}
|
||||
c = float64(scale[i])
|
||||
b = float64[scale[j])
|
||||
return fma(a, b, c)
|
||||
}
|
||||
|
||||
// based on CoreText dylib's __ZL33CreateTraitsByStyleGlossaryStringPK10__CFString — CreateTraitsByStyleGlossaryString(__CFString const*)
|
||||
func CoreText_WeightByStyleGlossaryString(str string) (weight float64, ok bool) {
|
||||
str.Fold(kCFCompareCaseInsensitive, nil)
|
||||
weightNameMap := []struct {
|
||||
key string
|
||||
val float32
|
||||
}{
|
||||
{ "ultra light", float32as(-0.800000, 0xbf4ccccd) },
|
||||
{ "ultra black", float32as(0.750000, 0x3f400000) },
|
||||
{ "extra light", float32as(-0.500000, 0xbf000000) },
|
||||
{ "ultralight", float32as(-0.800000, 0xbf4ccccd) },
|
||||
{ "ultrablack", float32as(0.750000, 0x3f400000) },
|
||||
{ "extrablack", float32as(0.800000, 0x3f4ccccd) },
|
||||
{ "extralight", float32as(-0.500000, 0xbf000000) }
|
||||
{ "heavy face", float32as(0.560000, 0x3f0f5c29) },
|
||||
{ "semi light", float32as(-0.200000, 0xbe4ccccd) },
|
||||
{ "extra bold", float32as(0.500000, 0x3f000000) },
|
||||
{ "ultra bold", float32as(0.700000, 0x3f333333) },
|
||||
{ "heavyface", float32as(0.560000, 0x3f0f5c29) },
|
||||
{ "extrabold", float32as(0.500000, 0x3f000000) },
|
||||
{ "ultrabold", float32as(0.700000, 0x3f333333) },
|
||||
{ "semilight", float32as(-0.200000, 0xbe4ccccd) },
|
||||
{ "demi bold", float32as(0.250000, 0x3e800000) },
|
||||
{ "semi bold", float32as(0.300000, 0x3e99999a) },
|
||||
{ "demibold", float32as(0.250000, 0x3e800000) },
|
||||
{ "semibold", float32as(0.300000, 0x3e99999a) },
|
||||
{ "hairline", float32as(-0.700000, 0xbf333333) },
|
||||
{ "medium", float32as(0.230000, 0x3e6b851f) },
|
||||
{ "poster", float32as(0.800000, 0x3f4ccccd) },
|
||||
{ "light", float32as(-0.400000, 0xbecccccd) },
|
||||
{ "heavy", float32as(0.560000, 0x3f0f5c29) },
|
||||
{ "extra", float32as(0.500000, 0x3f000000) },
|
||||
{ "black", float32as(0.620000, 0x3f1eb852) },
|
||||
{ "super", float32as(0.620000, 0x3f1eb852) },
|
||||
{ "obese", float32as(0.850000, 0x3f59999a) },
|
||||
{ "lite", float32as(-0.400000, 0xbecccccd) },
|
||||
{ "book", float32as(-0.230000, 0xbe6b851f) },
|
||||
{ "demi", float32as(0.250000, 0x3e800000) },
|
||||
{ "semi", float32as(0.300000, 0x3e99999a) },
|
||||
{ "thin", float32as(-0.500000, 0xbf000000) },
|
||||
{ "bold", float32as(0.400000, 0x3ecccccd) },
|
||||
{ "nord", float32as(0.800000, 0x3f4ccccd) },
|
||||
{ "fat", float32as(0.750000, 0x3f400000) },
|
||||
{ "w1", float32as(-0.700000, 0xbf333333) },
|
||||
{ "w2", float32as(-0.500000, 0xbf000000) },
|
||||
{ "w3", float32as(-0.230000, 0xbe6b851f) },
|
||||
{ "w4", float32as(0.000000, 0x0) },
|
||||
{ "w5", float32as(0.230000, 0x3e6b851f) },
|
||||
{ "w6", float32as(0.300000, 0x3e99999a) },
|
||||
{ "w7", float32as(0.440000, 0x3ee147ae) },
|
||||
{ "w8", float32as(0.540000, 0x3f0a3d71) },
|
||||
{ "w9", float32as(0.620000, 0x3f1eb852) },
|
||||
}
|
||||
for _, m := range weightNameMap {
|
||||
if strstr(str, m.key) != nil {
|
||||
val := CFNumberWithFloat32(m.val)
|
||||
return val.Float64Value(), true
|
||||
}
|
||||
}
|
||||
return 0, false
|
||||
}
|
||||
|
||||
// based on CoreText dylib's __ZNK9TBaseFont29CreateTraitsValuesPerFontInfoEP12MetadataFlag — TBaseFont::CreateTraitsValuesPerFontInfo(MetadataFlag*) const
|
||||
func (f *CTFont) Weight() float64 {
|
||||
if f.IsRegistered() {
|
||||
return f.WeightFromFontRegistry()
|
||||
}
|
||||
|
||||
weight := float64as(2.0, 0x4000000000000000)
|
||||
ebx := -1
|
||||
hasWeight := false
|
||||
|
||||
name := f.Name(kCTFontPostScriptNameKey)
|
||||
if name != nil {
|
||||
switch *name {
|
||||
case "LucidaGrande":
|
||||
weight = float64as(0.000000, 0x0)
|
||||
hasWeight = true
|
||||
case ".LucidaGrandeUI":
|
||||
weight = float64as(0.000000, 0x0)
|
||||
hasWeight = true
|
||||
case "STHeiti":
|
||||
weight = float64as(0.240000, 0x3fceb851eb851eb8)
|
||||
hasWeight = true
|
||||
case "STXihei":
|
||||
weight = float64as(-0.100000, 0xbfb999999999999a)
|
||||
hasWeight = true
|
||||
case "TimesNewRomanPSMT":
|
||||
weight = float64as(0.000000, 0x0)
|
||||
hasWeight = true
|
||||
// there is one more hardcoded case, for "Times-Roman", but that will only set the class style, not the weight
|
||||
}
|
||||
}
|
||||
|
||||
os2table := f.Table('OS/2')
|
||||
if os2table != nil {
|
||||
if !hasWeight {
|
||||
var usWeightClass uint16
|
||||
|
||||
valid := false
|
||||
if os2table.Len() > 77 {
|
||||
b := os2table.Bytes()
|
||||
usWeightClass = uint16be(b[4:6])
|
||||
if usWeightClass > 1000 {
|
||||
weight = 0
|
||||
hasWeight = false
|
||||
} else {
|
||||
valid = true
|
||||
}
|
||||
} else {
|
||||
usWeightClass = 0
|
||||
valid = true
|
||||
}
|
||||
if valid {
|
||||
weight = CoreText_WeightOfClass(usWeightClass)
|
||||
hasWeight = true
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
styleGlossaryNames := []string{
|
||||
kCTFontSubFamilyNameKey,
|
||||
kCTFontFullNameKey,
|
||||
kCTFontFamilyNameKey,
|
||||
}
|
||||
for _, key := range styleGlossaryNames {
|
||||
name := f.Name(key)
|
||||
if name == nil {
|
||||
continue
|
||||
}
|
||||
candidate, ok := CoreText_WeightByStyleGlossaryString(*name)
|
||||
if !ok {
|
||||
continue
|
||||
}
|
||||
if !hasWeight {
|
||||
weight = candidate
|
||||
hasWeight = true
|
||||
}
|
||||
}
|
||||
|
||||
if hasWeight {
|
||||
return weight
|
||||
}
|
||||
return 0
|
||||
}
|
||||
|
||||
func (f *Font) ShouldEnableBoldSymbolicTrait() bool {
|
||||
if f.IsRegistered() {
|
||||
return f.ShouldEnableBoldSymbolicTraitFromRegistry()
|
||||
}
|
||||
no := f.Weight() <= float64as(0.239000, 0x3fce978d4fdf3b64)
|
||||
return !no
|
||||
}
|
|
@ -0,0 +1,149 @@
|
|||
-0.100000 0xbdcccccd registered postscript name "STXihei"
|
||||
-0.100000 0xbfb999999999999a unregistered postscript name "STXihei"
|
||||
-0.200000 0xbe4ccccd registered subfamily abbr "EL"
|
||||
-0.200000 0xbe4ccccd "Semi Light"
|
||||
-0.200000 0xbe4ccccd "SemiLight"
|
||||
-0.200000 0xbe4ccccd "semi light"
|
||||
-0.200000 0xbe4ccccd "semilight"
|
||||
-0.230000 0xbe6b851f "Book"
|
||||
-0.230000 0xbe6b851f "W1"
|
||||
-0.230000 0xbe6b851f "W3"
|
||||
-0.230000 0xbe6b851f "book"
|
||||
-0.230000 0xbe6b851f "w3"
|
||||
-0.230000 0xbe6b851f panose 5
|
||||
-0.400000 0xbecccccd registered subfamily abbr "L"
|
||||
-0.400000 0xbecccccd "Light"
|
||||
-0.400000 0xbecccccd "Lite"
|
||||
-0.400000 0xbecccccd "light"
|
||||
-0.400000 0xbecccccd "light"
|
||||
-0.400000 0xbecccccd "lite"
|
||||
-0.400000 0xbecccccd registered OS2 weights 3, 201 - 300
|
||||
-0.400000 0xbecccccd panose 3
|
||||
-0.500000 0xbf000000 "Ext Light"
|
||||
-0.500000 0xbf000000 "Extra Light"
|
||||
-0.500000 0xbf000000 "ExtraLight"
|
||||
-0.500000 0xbf000000 "Thin"
|
||||
-0.500000 0xbf000000 "W2"
|
||||
-0.500000 0xbf000000 "extra light"
|
||||
-0.500000 0xbf000000 "extralight"
|
||||
-0.500000 0xbf000000 "thin"
|
||||
-0.500000 0xbf000000 "w2"
|
||||
-0.500000 0xbf000000 registered OS2 weights 2, 101 - 200
|
||||
-0.500000 0xbf000000 panose 2
|
||||
-0.600000 0xbf19999a "thin"
|
||||
-0.700000 0xbf333333 "hairline"
|
||||
-0.700000 0xbf333333 "w1"
|
||||
-0.800000 0xbf4ccccd "Ext Thin"
|
||||
-0.800000 0xbf4ccccd "Extra Thin"
|
||||
-0.800000 0xbf4ccccd "HairLine"
|
||||
-0.800000 0xbf4ccccd "Ultra Light"
|
||||
-0.800000 0xbf4ccccd "Ultra Thin"
|
||||
-0.800000 0xbf4ccccd "UltraLight"
|
||||
-0.800000 0xbf4ccccd "ulight"
|
||||
-0.800000 0xbf4ccccd "ultra light"
|
||||
-0.800000 0xbf4ccccd "ultralight"
|
||||
-0.800000 0xbf4ccccd registered OS2 weights 1, 10 - 100
|
||||
0.000000 0x0 registered postscript name ".Keyboard"
|
||||
0.000000 0x0 registered postscript name ".LucidaGrandeUI"
|
||||
0.000000 0x0 unregistered postscript name ".LucidaGrandeUI"
|
||||
0.000000 0x0 registered postscript name "LucidaGrande"
|
||||
0.000000 0x0 unregistered postscript name "LucidaGrande"
|
||||
0.000000 0x0 registered subfamily abbr "R"
|
||||
0.000000 0x0 registered postscript name "TimesNewRomanPSMT"
|
||||
0.000000 0x0 unregistered postscript name "TimesNewRomanPSMT"
|
||||
0.000000 0x0 "W4"
|
||||
0.000000 0x0 "reg"
|
||||
0.000000 0x0 "w4"
|
||||
0.000000 0x0 registered OS2 weights 4, 301 - 400
|
||||
0.000000 0x0 default
|
||||
0.230000 0x3e6b851f registered OS2 weights 5, 401 - 500
|
||||
0.230000 0x3e6b851f registered subfamily abbr "M"
|
||||
0.230000 0x3e6b851f "Medium"
|
||||
0.230000 0x3e6b851f "W5"
|
||||
0.230000 0x3e6b851f "med"
|
||||
0.230000 0x3e6b851f "medium"
|
||||
0.230000 0x3e6b851f "w5"
|
||||
0.230000 0x3e6b851f panose 6
|
||||
0.240000 0x3e75c28f registered postscript name "STHeiti"
|
||||
0.240000 0x3fceb851eb851eb8 unregistered postscript name "STHeiti"
|
||||
0.250000 0x3e800000 "Demi Bold"
|
||||
0.250000 0x3e800000 "Demi"
|
||||
0.250000 0x3e800000 "DemiBold"
|
||||
0.250000 0x3e800000 "demi bold"
|
||||
0.250000 0x3e800000 "demi"
|
||||
0.250000 0x3e800000 "demibold"
|
||||
0.250000 0x3e800000 registered OS2 weights 6, 501 - 600
|
||||
0.250000 0x3e800000 panose 7
|
||||
0.300000 0x3e99999a registered subfamily abbr "SB"
|
||||
0.300000 0x3e99999a "Semi Bold"
|
||||
0.300000 0x3e99999a "Semi"
|
||||
0.300000 0x3e99999a "SemiBold"
|
||||
0.300000 0x3e99999a "W6"
|
||||
0.300000 0x3e99999a "semi bold"
|
||||
0.300000 0x3e99999a "semi"
|
||||
0.300000 0x3e99999a "semi"
|
||||
0.300000 0x3e99999a "semibold"
|
||||
0.300000 0x3e99999a "w6"
|
||||
0.400000 0x3ecccccd registered subfamily abbr "B"
|
||||
0.400000 0x3ecccccd "Bold"
|
||||
0.400000 0x3ecccccd "bold"
|
||||
0.400000 0x3ecccccd "bold"
|
||||
0.400000 0x3ecccccd 'head'[0x2D] & 1
|
||||
0.400000 0x3ecccccd registered OS2 weights 7, 601 - 700
|
||||
0.400000 0x3ecccccd panose 8
|
||||
0.440000 0x3ee147ae "W7"
|
||||
0.440000 0x3ee147ae "w7"
|
||||
0.500000 0x3f000000 registered subfamily abbr "EB"
|
||||
0.500000 0x3f000000 "Ext Bold"
|
||||
0.500000 0x3f000000 "Extra Bold"
|
||||
0.500000 0x3f000000 "Extra"
|
||||
0.500000 0x3f000000 "ExtraBold"
|
||||
0.500000 0x3f000000 "Ultra"
|
||||
0.500000 0x3f000000 "extra bold"
|
||||
0.500000 0x3f000000 "extra"
|
||||
0.500000 0x3f000000 "extrabold"
|
||||
0.500000 0x3f000000 registered OS2 weights 8, 701 - 800
|
||||
0.540000 0x3f0a3d71 "W8"
|
||||
0.540000 0x3f0a3d71 "w8"
|
||||
0.560000 0x3f0f5c29 registered subfamily abbr "H"
|
||||
0.560000 0x3f0f5c29 "Heavy Face"
|
||||
0.560000 0x3f0f5c29 "Heavy"
|
||||
0.560000 0x3f0f5c29 "HeavyFace"
|
||||
0.560000 0x3f0f5c29 "heavy face"
|
||||
0.560000 0x3f0f5c29 "heavy"
|
||||
0.560000 0x3f0f5c29 "heavy"
|
||||
0.560000 0x3f0f5c29 "heavyface"
|
||||
0.560000 0x3f0f5c29 panose 9
|
||||
0.620000 0x3f1eb852 "Black"
|
||||
0.620000 0x3f1eb852 "Super"
|
||||
0.620000 0x3f1eb852 "W9"
|
||||
0.620000 0x3f1eb852 "black"
|
||||
0.620000 0x3f1eb852 "black"
|
||||
0.620000 0x3f1eb852 "super"
|
||||
0.620000 0x3f1eb852 "w9"
|
||||
0.620000 0x3f1eb852 registered OS2 weights 9, 801 - 900
|
||||
0.620000 0x3f1eb852 panose 10
|
||||
0.700000 0x3f333333 registered subfamily abbr "U"
|
||||
0.700000 0x3f333333 "Ultra Bold"
|
||||
0.700000 0x3f333333 "UltraBold"
|
||||
0.700000 0x3f333333 "ultra bold"
|
||||
0.700000 0x3f333333 "ultrabold"
|
||||
0.750000 0x3f400000 "Fat"
|
||||
0.750000 0x3f400000 "Ultra Black"
|
||||
0.750000 0x3f400000 "UltraBlack"
|
||||
0.750000 0x3f400000 "fat"
|
||||
0.750000 0x3f400000 "ultra black"
|
||||
0.750000 0x3f400000 "ultrablack"
|
||||
0.750000 0x3f400000 registered OS2 weights 901 - 950
|
||||
0.800000 0x3f4ccccd "Ext Black"
|
||||
0.800000 0x3f4ccccd "ExtraBlack"
|
||||
0.800000 0x3f4ccccd "Nord"
|
||||
0.800000 0x3f4ccccd "Poster"
|
||||
0.800000 0x3f4ccccd registered subfamily abbr "UH"
|
||||
0.800000 0x3f4ccccd "extrablack"
|
||||
0.800000 0x3f4ccccd "nord"
|
||||
0.800000 0x3f4ccccd "poster"
|
||||
0.800000 0x3f4ccccd registered OS2 weights 951 - 999
|
||||
0.800000 0x3f4ccccd panose 11
|
||||
0.850000 0x3f59999a "Obese"
|
||||
0.850000 0x3f59999a "obese"
|
|
@ -0,0 +1,146 @@
|
|||
-0.100000
|
||||
postscript name "STXihei" (which, according to http://www.typophile.com/node/93813, means "ST Hei Light", and so we can assume it's the Light version of STHeiti, which I think is Medium though is given Regular status below because meh)
|
||||
|
||||
-0.200000
|
||||
subfamily abbreviation "EL" (???????????)
|
||||
style string contains "Semi Light"
|
||||
style string contains "SemiLight"
|
||||
|
||||
-0.230000
|
||||
style string contains "Book"
|
||||
style string contains "W1" (registered fonts only; probably a typo and -0.7 was expected instead)
|
||||
style string contains "W3"
|
||||
panose 5
|
||||
|
||||
-0.400000
|
||||
subfamily abbreviation "L"
|
||||
style string contains "Light"
|
||||
style string contains "Lite"
|
||||
ATSD style "light"
|
||||
OS2 weights 3, 201 - 300
|
||||
panose 3
|
||||
|
||||
-0.500000
|
||||
style string contains "Ext Light"
|
||||
style string contains "Extra Light"
|
||||
style string contains "ExtraLight"
|
||||
style string contains "Thin"
|
||||
style string contains "W2"
|
||||
OS2 weights 2, 101 - 200
|
||||
panose 2
|
||||
|
||||
-0.600000
|
||||
ATSD style "thin"
|
||||
|
||||
-0.700000
|
||||
style string contains "hairline"
|
||||
style string contains "w1"
|
||||
|
||||
-0.800000
|
||||
style string contains "Ext Thin"
|
||||
style string contains "Extra Thin"
|
||||
style string contains "HairLine"
|
||||
style string contains "Ultra Light"
|
||||
style string contains "Ultra Thin"
|
||||
style string contains "UltraLight"
|
||||
ATSD style "ulight"
|
||||
OS2 weights 1, 10 - 100
|
||||
|
||||
0.000000
|
||||
ostscript name ".Keyboard"
|
||||
postscript name ".LucidaGrandeUI"
|
||||
postscript name "LucidaGrande"
|
||||
subfamily abbreviation "R"
|
||||
postscript name "TimesNewRomanPSMT"
|
||||
style string contains "W4"
|
||||
ATSD style "reg"
|
||||
OS2 weights 4, 301 - 400
|
||||
default
|
||||
|
||||
0.230000
|
||||
OS2 weights 5, 401 - 500
|
||||
subfamily abbreviation "M"
|
||||
style string contains "Medium"
|
||||
style string contains "W5"
|
||||
ATSD style "med"
|
||||
panose 6
|
||||
|
||||
0.240000
|
||||
postscript name "STHeiti"
|
||||
|
||||
0.250000
|
||||
style string contains "Demi Bold"
|
||||
style string contains "Demi"
|
||||
style string contains "DemiBold"
|
||||
OS2 weights 6, 501 - 600
|
||||
panose 7
|
||||
|
||||
0.300000
|
||||
subfamily abbreviation "SB"
|
||||
style string contains "Semi Bold"
|
||||
style string contains "Semi"
|
||||
style string contains "SemiBold"
|
||||
style string contains "W6"
|
||||
ATSD style "semi"
|
||||
|
||||
0.400000
|
||||
subfamily abbreviation "B"
|
||||
style string contains "Bold"
|
||||
ATSD style "bold"
|
||||
('head' table byte 0x2D) & 1 != 0
|
||||
OS2 weights 7, 601 - 700
|
||||
panose 8
|
||||
|
||||
0.440000
|
||||
style string contains "W7"
|
||||
|
||||
0.500000
|
||||
subfamily abbreviation "EB"
|
||||
style string contains "Ext Bold"
|
||||
style string contains "Extra Bold"
|
||||
style string contains "Extra"
|
||||
style string contains "ExtraBold"
|
||||
style string contains "Ultra"
|
||||
OS2 weights 8, 701 - 800
|
||||
|
||||
0.540000
|
||||
style string contains "W8"
|
||||
|
||||
0.560000
|
||||
subfamily abbreviation "H"
|
||||
style string contains "Heavy Face"
|
||||
style string contains "Heavy"
|
||||
style string contains "HeavyFace"
|
||||
ATSD style "heavy"
|
||||
panose 9
|
||||
|
||||
0.620000
|
||||
style string contains "Black"
|
||||
style string contains "Super"
|
||||
style string contains "W9"
|
||||
ATSD style "black"
|
||||
OS2 weights 9, 801 - 900
|
||||
panose 10
|
||||
|
||||
0.700000
|
||||
subfamily abbreviation "U"
|
||||
style string contains "Ultra Bold"
|
||||
style string contains "UltraBold"
|
||||
|
||||
0.750000
|
||||
style string contains "Fat"
|
||||
style string contains "Ultra Black"
|
||||
style string contains "UltraBlack"
|
||||
OS2 weights 901 - 950
|
||||
|
||||
0.800000
|
||||
style string contains "Ext Black"
|
||||
style string contains "ExtraBlack"
|
||||
style string contains "Nord"
|
||||
style string contains "Poster"
|
||||
subfamily abbreviation "UH"
|
||||
OS2 weights 951 - 999
|
||||
panose 11
|
||||
|
||||
0.850000
|
||||
style string contains "Obese"
|
|
@ -0,0 +1,149 @@
|
|||
-0.100000 0xbdcccccd "STXihei"
|
||||
-0.100000 0xbfb999999999999a "STXihei"
|
||||
-0.200000 0xbe4ccccd "EL"
|
||||
-0.200000 0xbe4ccccd "Semi Light"
|
||||
-0.200000 0xbe4ccccd "SemiLight"
|
||||
-0.200000 0xbe4ccccd "semi light"
|
||||
-0.200000 0xbe4ccccd "semilight"
|
||||
-0.230000 0xbe6b851f "Book"
|
||||
-0.230000 0xbe6b851f "W1"
|
||||
-0.230000 0xbe6b851f "W3"
|
||||
-0.230000 0xbe6b851f "book"
|
||||
-0.230000 0xbe6b851f "w3"
|
||||
-0.230000 0xbe6b851f panose 5
|
||||
-0.400000 0xbecccccd "L"
|
||||
-0.400000 0xbecccccd "Light"
|
||||
-0.400000 0xbecccccd "Lite"
|
||||
-0.400000 0xbecccccd "light"
|
||||
-0.400000 0xbecccccd "light"
|
||||
-0.400000 0xbecccccd "lite"
|
||||
-0.400000 0xbecccccd 3, 201 - 300
|
||||
-0.400000 0xbecccccd panose 3
|
||||
-0.500000 0xbf000000 "Ext Light"
|
||||
-0.500000 0xbf000000 "Extra Light"
|
||||
-0.500000 0xbf000000 "ExtraLight"
|
||||
-0.500000 0xbf000000 "Thin"
|
||||
-0.500000 0xbf000000 "W2"
|
||||
-0.500000 0xbf000000 "extra light"
|
||||
-0.500000 0xbf000000 "extralight"
|
||||
-0.500000 0xbf000000 "thin"
|
||||
-0.500000 0xbf000000 "w2"
|
||||
-0.500000 0xbf000000 2, 101 - 200
|
||||
-0.500000 0xbf000000 panose 2
|
||||
-0.600000 0xbf19999a "thin"
|
||||
-0.700000 0xbf333333 "hairline"
|
||||
-0.700000 0xbf333333 "w1"
|
||||
-0.800000 0xbf4ccccd "Ext Thin"
|
||||
-0.800000 0xbf4ccccd "Extra Thin"
|
||||
-0.800000 0xbf4ccccd "HairLine"
|
||||
-0.800000 0xbf4ccccd "Ultra Light"
|
||||
-0.800000 0xbf4ccccd "Ultra Thin"
|
||||
-0.800000 0xbf4ccccd "UltraLight"
|
||||
-0.800000 0xbf4ccccd "ulight"
|
||||
-0.800000 0xbf4ccccd "ultra light"
|
||||
-0.800000 0xbf4ccccd "ultralight"
|
||||
-0.800000 0xbf4ccccd 1, 10 - 100
|
||||
0.000000 0x0 ".Keyboard"
|
||||
0.000000 0x0 ".LucidaGrandeUI"
|
||||
0.000000 0x0 ".LucidaGrandeUI"
|
||||
0.000000 0x0 "LucidaGrande"
|
||||
0.000000 0x0 "LucidaGrande"
|
||||
0.000000 0x0 "R"
|
||||
0.000000 0x0 "TimesNewRomanPSMT"
|
||||
0.000000 0x0 "TimesNewRomanPSMT"
|
||||
0.000000 0x0 "W4"
|
||||
0.000000 0x0 "reg"
|
||||
0.000000 0x0 "w4"
|
||||
0.000000 0x0 4, 301 - 400
|
||||
0.000000 0x0 default
|
||||
0.230000 0x3e6b851f 5, 401 - 500
|
||||
0.230000 0x3e6b851f "M"
|
||||
0.230000 0x3e6b851f "Medium"
|
||||
0.230000 0x3e6b851f "W5"
|
||||
0.230000 0x3e6b851f "med"
|
||||
0.230000 0x3e6b851f "medium"
|
||||
0.230000 0x3e6b851f "w5"
|
||||
0.230000 0x3e6b851f panose 6
|
||||
0.240000 0x3e75c28f "STHeiti"
|
||||
0.240000 0x3fceb851eb851eb8 "STHeiti"
|
||||
0.250000 0x3e800000 "Demi Bold"
|
||||
0.250000 0x3e800000 "Demi"
|
||||
0.250000 0x3e800000 "DemiBold"
|
||||
0.250000 0x3e800000 "demi bold"
|
||||
0.250000 0x3e800000 "demi"
|
||||
0.250000 0x3e800000 "demibold"
|
||||
0.250000 0x3e800000 6, 501 - 600
|
||||
0.250000 0x3e800000 panose 7
|
||||
0.300000 0x3e99999a "SB"
|
||||
0.300000 0x3e99999a "Semi Bold"
|
||||
0.300000 0x3e99999a "Semi"
|
||||
0.300000 0x3e99999a "SemiBold"
|
||||
0.300000 0x3e99999a "W6"
|
||||
0.300000 0x3e99999a "semi bold"
|
||||
0.300000 0x3e99999a "semi"
|
||||
0.300000 0x3e99999a "semi"
|
||||
0.300000 0x3e99999a "semibold"
|
||||
0.300000 0x3e99999a "w6"
|
||||
0.400000 0x3ecccccd "B"
|
||||
0.400000 0x3ecccccd "Bold"
|
||||
0.400000 0x3ecccccd "bold"
|
||||
0.400000 0x3ecccccd "bold"
|
||||
0.400000 0x3ecccccd 'head'[0x2D] & 1
|
||||
0.400000 0x3ecccccd 7, 601 - 700
|
||||
0.400000 0x3ecccccd panose 8
|
||||
0.440000 0x3ee147ae "W7"
|
||||
0.440000 0x3ee147ae "w7"
|
||||
0.500000 0x3f000000 "EB"
|
||||
0.500000 0x3f000000 "Ext Bold"
|
||||
0.500000 0x3f000000 "Extra Bold"
|
||||
0.500000 0x3f000000 "Extra"
|
||||
0.500000 0x3f000000 "ExtraBold"
|
||||
0.500000 0x3f000000 "Ultra"
|
||||
0.500000 0x3f000000 "extra bold"
|
||||
0.500000 0x3f000000 "extra"
|
||||
0.500000 0x3f000000 "extrabold"
|
||||
0.500000 0x3f000000 8, 701 - 800
|
||||
0.540000 0x3f0a3d71 "W8"
|
||||
0.540000 0x3f0a3d71 "w8"
|
||||
0.560000 0x3f0f5c29 "H"
|
||||
0.560000 0x3f0f5c29 "Heavy Face"
|
||||
0.560000 0x3f0f5c29 "Heavy"
|
||||
0.560000 0x3f0f5c29 "HeavyFace"
|
||||
0.560000 0x3f0f5c29 "heavy face"
|
||||
0.560000 0x3f0f5c29 "heavy"
|
||||
0.560000 0x3f0f5c29 "heavy"
|
||||
0.560000 0x3f0f5c29 "heavyface"
|
||||
0.560000 0x3f0f5c29 panose 9
|
||||
0.620000 0x3f1eb852 "Black"
|
||||
0.620000 0x3f1eb852 "Super"
|
||||
0.620000 0x3f1eb852 "W9"
|
||||
0.620000 0x3f1eb852 "black"
|
||||
0.620000 0x3f1eb852 "black"
|
||||
0.620000 0x3f1eb852 "super"
|
||||
0.620000 0x3f1eb852 "w9"
|
||||
0.620000 0x3f1eb852 9, 801 - 900
|
||||
0.620000 0x3f1eb852 panose 10
|
||||
0.700000 0x3f333333 "U"
|
||||
0.700000 0x3f333333 "Ultra Bold"
|
||||
0.700000 0x3f333333 "UltraBold"
|
||||
0.700000 0x3f333333 "ultra bold"
|
||||
0.700000 0x3f333333 "ultrabold"
|
||||
0.750000 0x3f400000 "Fat"
|
||||
0.750000 0x3f400000 "Ultra Black"
|
||||
0.750000 0x3f400000 "UltraBlack"
|
||||
0.750000 0x3f400000 "fat"
|
||||
0.750000 0x3f400000 "ultra black"
|
||||
0.750000 0x3f400000 "ultrablack"
|
||||
0.750000 0x3f400000 901 - 950
|
||||
0.800000 0x3f4ccccd "Ext Black"
|
||||
0.800000 0x3f4ccccd "ExtraBlack"
|
||||
0.800000 0x3f4ccccd "Nord"
|
||||
0.800000 0x3f4ccccd "Poster"
|
||||
0.800000 0x3f4ccccd "UH"
|
||||
0.800000 0x3f4ccccd "extrablack"
|
||||
0.800000 0x3f4ccccd "nord"
|
||||
0.800000 0x3f4ccccd "poster"
|
||||
0.800000 0x3f4ccccd 951 - 999
|
||||
0.800000 0x3f4ccccd panose 11
|
||||
0.850000 0x3f59999a "Obese"
|
||||
0.850000 0x3f59999a "obese"
|
|
@ -0,0 +1,149 @@
|
|||
0.000000 0x0 "reg"
|
||||
0.300000 0x3e99999a "semi"
|
||||
0.400000 0x3ecccccd "bold"
|
||||
-0.400000 0xbecccccd "light"
|
||||
0.230000 0x3e6b851f "med"
|
||||
0.560000 0x3f0f5c29 "heavy"
|
||||
0.620000 0x3f1eb852 "black"
|
||||
-0.600000 0xbf19999a "thin"
|
||||
-0.800000 0xbf4ccccd "ulight"
|
||||
0.000000 0x0 "LucidaGrande"
|
||||
0.000000 0x0 ".LucidaGrandeUI"
|
||||
0.000000 0x0 ".Keyboard"
|
||||
0.240000 0x3e75c28f "STHeiti"
|
||||
-0.100000 0xbdcccccd "STXihei"
|
||||
0.000000 0x0 "TimesNewRomanPSMT"
|
||||
-0.800000 0xbf4ccccd "Ultra Light"
|
||||
0.750000 0x3f400000 "Ultra Black"
|
||||
-0.500000 0xbf000000 "Extra Light"
|
||||
0.750000 0x3f400000 "UltraBlack"
|
||||
0.800000 0x3f4ccccd "ExtraBlack"
|
||||
-0.800000 0xbf4ccccd "UltraLight"
|
||||
-0.500000 0xbf000000 "ExtraLight"
|
||||
-0.800000 0xbf4ccccd "Ultra Thin"
|
||||
-0.800000 0xbf4ccccd "Extra Thin"
|
||||
0.560000 0x3f0f5c29 "Heavy Face"
|
||||
-0.200000 0xbe4ccccd "Semi Light"
|
||||
0.500000 0x3f000000 "Extra Bold"
|
||||
0.700000 0x3f333333 "Ultra Bold"
|
||||
0.560000 0x3f0f5c29 "HeavyFace"
|
||||
0.500000 0x3f000000 "ExtraBold"
|
||||
0.700000 0x3f333333 "UltraBold"
|
||||
0.800000 0x3f4ccccd "Ext Black"
|
||||
-0.200000 0xbe4ccccd "SemiLight"
|
||||
0.250000 0x3e800000 "Demi Bold"
|
||||
0.300000 0x3e99999a "Semi Bold"
|
||||
-0.500000 0xbf000000 "Ext Light"
|
||||
0.500000 0x3f000000 "Ext Bold"
|
||||
0.250000 0x3e800000 "DemiBold"
|
||||
0.300000 0x3e99999a "SemiBold"
|
||||
-0.800000 0xbf4ccccd "HairLine"
|
||||
-0.800000 0xbf4ccccd "Ext Thin"
|
||||
0.230000 0x3e6b851f "Medium"
|
||||
0.800000 0x3f4ccccd "Poster"
|
||||
-0.400000 0xbecccccd "Light"
|
||||
0.500000 0x3f000000 "Ultra"
|
||||
0.560000 0x3f0f5c29 "Heavy"
|
||||
0.500000 0x3f000000 "Extra"
|
||||
0.620000 0x3f1eb852 "Black"
|
||||
0.620000 0x3f1eb852 "Super"
|
||||
0.850000 0x3f59999a "Obese"
|
||||
-0.400000 0xbecccccd "Lite"
|
||||
-0.230000 0xbe6b851f "Book"
|
||||
0.250000 0x3e800000 "Demi"
|
||||
0.300000 0x3e99999a "Semi"
|
||||
-0.500000 0xbf000000 "Thin"
|
||||
0.400000 0x3ecccccd "Bold"
|
||||
0.800000 0x3f4ccccd "Nord"
|
||||
0.750000 0x3f400000 "Fat"
|
||||
-0.230000 0xbe6b851f "W1"
|
||||
-0.500000 0xbf000000 "W2"
|
||||
-0.230000 0xbe6b851f "W3"
|
||||
0.000000 0x0 "W4"
|
||||
0.230000 0x3e6b851f "W5"
|
||||
0.300000 0x3e99999a "W6"
|
||||
0.440000 0x3ee147ae "W7"
|
||||
0.540000 0x3f0a3d71 "W8"
|
||||
0.620000 0x3f1eb852 "W9"
|
||||
-0.800000 0xbf4ccccd 1, 10 - 100
|
||||
-0.500000 0xbf000000 2, 101 - 200
|
||||
-0.400000 0xbecccccd 3, 201 - 300
|
||||
0.000000 0x0 4, 301 - 400
|
||||
0.230000 0x3e6b851f 5, 401 - 500
|
||||
0.250000 0x3e800000 6, 501 - 600
|
||||
0.400000 0x3ecccccd 7, 601 - 700
|
||||
0.500000 0x3f000000 8, 701 - 800
|
||||
0.620000 0x3f1eb852 9, 801 - 900
|
||||
0.750000 0x3f400000 901 - 950
|
||||
0.800000 0x3f4ccccd 951 - 999
|
||||
-0.500000 0xbf000000 panose 2
|
||||
-0.400000 0xbecccccd panose 3
|
||||
-0.230000 0xbe6b851f panose 5
|
||||
0.230000 0x3e6b851f panose 6
|
||||
0.250000 0x3e800000 panose 7
|
||||
0.400000 0x3ecccccd panose 8
|
||||
0.560000 0x3f0f5c29 panose 9
|
||||
0.620000 0x3f1eb852 panose 10
|
||||
0.800000 0x3f4ccccd panose 11
|
||||
0.400000 0x3ecccccd 'head'[0x2D] & 1
|
||||
-0.200000 0xbe4ccccd "EL"
|
||||
0.500000 0x3f000000 "EB"
|
||||
0.300000 0x3e99999a "SB"
|
||||
0.800000 0x3f4ccccd "UH"
|
||||
0.700000 0x3f333333 "U"
|
||||
-0.400000 0xbecccccd "L"
|
||||
0.560000 0x3f0f5c29 "H"
|
||||
0.400000 0x3ecccccd "B"
|
||||
0.230000 0x3e6b851f "M"
|
||||
0.000000 0x0 "R"
|
||||
0.000000 0x0 default
|
||||
0.000000 0x0 "LucidaGrande"
|
||||
0.000000 0x0 ".LucidaGrandeUI"
|
||||
0.240000 0x3fceb851eb851eb8 "STHeiti"
|
||||
-0.100000 0xbfb999999999999a "STXihei"
|
||||
0.000000 0x0 "TimesNewRomanPSMT"
|
||||
-0.800000 0xbf4ccccd "ultra light"
|
||||
0.750000 0x3f400000 "ultra black"
|
||||
-0.500000 0xbf000000 "extra light"
|
||||
-0.800000 0xbf4ccccd "ultralight"
|
||||
0.750000 0x3f400000 "ultrablack"
|
||||
0.800000 0x3f4ccccd "extrablack"
|
||||
-0.500000 0xbf000000 "extralight"
|
||||
0.560000 0x3f0f5c29 "heavy face"
|
||||
-0.200000 0xbe4ccccd "semi light"
|
||||
0.500000 0x3f000000 "extra bold"
|
||||
0.700000 0x3f333333 "ultra bold"
|
||||
0.560000 0x3f0f5c29 "heavyface"
|
||||
0.500000 0x3f000000 "extrabold"
|
||||
0.700000 0x3f333333 "ultrabold"
|
||||
-0.200000 0xbe4ccccd "semilight"
|
||||
0.250000 0x3e800000 "demi bold"
|
||||
0.300000 0x3e99999a "semi bold"
|
||||
0.250000 0x3e800000 "demibold"
|
||||
0.300000 0x3e99999a "semibold"
|
||||
-0.700000 0xbf333333 "hairline"
|
||||
0.230000 0x3e6b851f "medium"
|
||||
0.800000 0x3f4ccccd "poster"
|
||||
-0.400000 0xbecccccd "light"
|
||||
0.560000 0x3f0f5c29 "heavy"
|
||||
0.500000 0x3f000000 "extra"
|
||||
0.620000 0x3f1eb852 "black"
|
||||
0.620000 0x3f1eb852 "super"
|
||||
0.850000 0x3f59999a "obese"
|
||||
-0.400000 0xbecccccd "lite"
|
||||
-0.230000 0xbe6b851f "book"
|
||||
0.250000 0x3e800000 "demi"
|
||||
0.300000 0x3e99999a "semi"
|
||||
-0.500000 0xbf000000 "thin"
|
||||
0.400000 0x3ecccccd "bold"
|
||||
0.800000 0x3f4ccccd "nord"
|
||||
0.750000 0x3f400000 "fat"
|
||||
-0.700000 0xbf333333 "w1"
|
||||
-0.500000 0xbf000000 "w2"
|
||||
-0.230000 0xbe6b851f "w3"
|
||||
0.000000 0x0 "w4"
|
||||
0.230000 0x3e6b851f "w5"
|
||||
0.300000 0x3e99999a "w6"
|
||||
0.440000 0x3ee147ae "w7"
|
||||
0.540000 0x3f0a3d71 "w8"
|
||||
0.620000 0x3f1eb852 "w9"
|
|
@ -0,0 +1,247 @@
|
|||
registered font, preexisting metadata weight
|
||||
"reg": float32as(0.000000, 0x0)
|
||||
"semi": float32as(0.300000, 0x3e99999a)
|
||||
"bold": float32as(0.400000, 0x3ecccccd)
|
||||
"light": float32as(-0.400000, 0xbecccccd)
|
||||
"med": float32as(0.230000, 0x3e6b851f)
|
||||
"heavy": float32as(0.560000, 0x3f0f5c29)
|
||||
"black": float32as(0.620000, 0x3f1eb852)
|
||||
"thin": float32as(-0.600000, 0xbf19999a)
|
||||
"ulight": float32as(-0.800000, 0xbf4ccccd)
|
||||
|
||||
registered font, postscript name (probably only for TrueType and OpenType) special cases, overrides the above
|
||||
"LucidaGrande": float32as(0.000000, 0x0)
|
||||
".LucidaGrandeUI": float32as(0.000000, 0x0)
|
||||
".Keyboard": float32as(0.000000, 0x0)
|
||||
"STHeiti": float32as(0.240000, 0x3e75c28f)
|
||||
"STXihei": float32as(-0.100000, 0xbdcccccd)
|
||||
"TimesNewRomanPSMT": float32as(0.000000, 0x0)
|
||||
|
||||
registered font, style glossary strings, tried in this order (possibly TrueType and OpenType only): preferred subfamily, subfamily, full name, preferred family, family; case-insensitive search, reverse search, "nonliteral" (kCFCompareNonliteral)
|
||||
"Ultra Light": float32as(-0.800000, 0xbf4ccccd)
|
||||
"Ultra Black": float32as(0.750000, 0x3f400000)
|
||||
"Extra Light": float32as(-0.500000, 0xbf000000)
|
||||
"UltraBlack": float32as(0.750000, 0x3f400000)
|
||||
"ExtraBlack": float32as(0.800000, 0x3f4ccccd)
|
||||
"UltraLight": float32as(-0.800000, 0xbf4ccccd)
|
||||
"ExtraLight": float32as(-0.500000, 0xbf000000)
|
||||
"Ultra Thin": float32as(-0.800000, 0xbf4ccccd)
|
||||
"Extra Thin": float32as(-0.800000, 0xbf4ccccd)
|
||||
"Heavy Face": float32as(0.560000, 0x3f0f5c29)
|
||||
"Semi Light": float32as(-0.200000, 0xbe4ccccd)
|
||||
"Extra Bold": float32as(0.500000, 0x3f000000)
|
||||
"Ultra Bold": float32as(0.700000, 0x3f333333)
|
||||
"HeavyFace": float32as(0.560000, 0x3f0f5c29)
|
||||
"ExtraBold": float32as(0.500000, 0x3f000000)
|
||||
"UltraBold": float32as(0.700000, 0x3f333333)
|
||||
"Ext Black": float32as(0.800000, 0x3f4ccccd)
|
||||
"SemiLight": float32as(-0.200000, 0xbe4ccccd)
|
||||
"Demi Bold": float32as(0.250000, 0x3e800000)
|
||||
"Semi Bold": float32as(0.300000, 0x3e99999a)
|
||||
"Ext Light": float32as(-0.500000, 0xbf000000)
|
||||
"Ext Bold": float32as(0.500000, 0x3f000000)
|
||||
"DemiBold": float32as(0.250000, 0x3e800000)
|
||||
"SemiBold": float32as(0.300000, 0x3e99999a)
|
||||
"HairLine": float32as(-0.800000, 0xbf4ccccd)
|
||||
"Ext Thin": float32as(-0.800000, 0xbf4ccccd)
|
||||
"Medium": float32as(0.230000, 0x3e6b851f)
|
||||
"Poster": float32as(0.800000, 0x3f4ccccd)
|
||||
"Light": float32as(-0.400000, 0xbecccccd)
|
||||
"Ultra": float32as(0.500000, 0x3f000000)
|
||||
"Heavy": float32as(0.560000, 0x3f0f5c29)
|
||||
"Extra": float32as(0.500000, 0x3f000000)
|
||||
"Black": float32as(0.620000, 0x3f1eb852)
|
||||
"Super": float32as(0.620000, 0x3f1eb852)
|
||||
"Obese": float32as(0.850000, 0x3f59999a)
|
||||
"Lite": float32as(-0.400000, 0xbecccccd)
|
||||
"Book": float32as(-0.230000, 0xbe6b851f)
|
||||
"Demi": float32as(0.250000, 0x3e800000)
|
||||
"Semi": float32as(0.300000, 0x3e99999a)
|
||||
"Thin": float32as(-0.500000, 0xbf000000)
|
||||
"Bold": float32as(0.400000, 0x3ecccccd)
|
||||
"Nord": float32as(0.800000, 0x3f4ccccd)
|
||||
"Fat": float32as(0.750000, 0x3f400000)
|
||||
"W1": float32as(-0.230000, 0xbe6b851f)
|
||||
"W2": float32as(-0.500000, 0xbf000000)
|
||||
"W3": float32as(-0.230000, 0xbe6b851f)
|
||||
"W4": float32as(0.000000, 0x0)
|
||||
"W5": float32as(0.230000, 0x3e6b851f)
|
||||
"W6": float32as(0.300000, 0x3e99999a)
|
||||
"W7": float32as(0.440000, 0x3ee147ae)
|
||||
"W8": float32as(0.540000, 0x3f0a3d71)
|
||||
"W9": float32as(0.620000, 0x3f1eb852)
|
||||
|
||||
registered font, OS2 weights; table length >= 78
|
||||
1, 10 - 100: float32as(-0.800000, 0xbf4ccccd)
|
||||
2, 101 - 200: float32as(-0.500000, 0xbf000000)
|
||||
3, 201 - 300: float32as(-0.400000, 0xbecccccd)
|
||||
4, 301 - 400: float32as(0.000000, 0x0)
|
||||
5, 401 - 500: float32as(0.230000, 0x3e6b851f)
|
||||
6, 501 - 600: float32as(0.250000, 0x3e800000)
|
||||
7, 601 - 700: float32as(0.400000, 0x3ecccccd)
|
||||
8, 701 - 800: float32as(0.500000, 0x3f000000)
|
||||
9, 801 - 900: float32as(0.620000, 0x3f1eb852)
|
||||
901 - 950: float32as(0.750000, 0x3f400000)
|
||||
951 - 999: float32as(0.800000, 0x3f4ccccd)
|
||||
0, 1000: panose
|
||||
panose 2: float32as(-0.500000, 0xbf000000)
|
||||
panose 3: float32as(-0.400000, 0xbecccccd)
|
||||
panose 4: !!!! see note should be float32as(-0.300000, 0xbe99999a) but is treated as invalid instead due to returning false instead of true
|
||||
panose 5: float32as(-0.230000, 0xbe6b851f)
|
||||
panose 6: float32as(0.230000, 0x3e6b851f)
|
||||
panose 7: float32as(0.250000, 0x3e800000)
|
||||
panose 8: float32as(0.400000, 0x3ecccccd)
|
||||
panose 9: float32as(0.560000, 0x3f0f5c29)
|
||||
panose 10: float32as(0.620000, 0x3f1eb852)
|
||||
panose 11: float32as(0.800000, 0x3f4ccccd)
|
||||
|
||||
registered font, head table, low bit of byte 0x2D
|
||||
'head'[0x2D] & 1: float32as(0.400000, 0x3ecccccd)
|
||||
|
||||
registered font, abbreviated weight glossary, checks for (possibly in TrueType and OpenType only) in order: preferred subfamily, family; case-insensitive strict comparison
|
||||
"EL": float32as(-0.200000, 0xbe4ccccd)
|
||||
"EB": float32as(0.500000, 0x3f000000)
|
||||
"SB": float32as(0.300000, 0x3e99999a)
|
||||
"UH": float32as(0.800000, 0x3f4ccccd)
|
||||
"U": float32as(0.700000, 0x3f333333)
|
||||
"L": float32as(-0.400000, 0xbecccccd)
|
||||
"H": float32as(0.560000, 0x3f0f5c29)
|
||||
"B": float32as(0.400000, 0x3ecccccd)
|
||||
"M": float32as(0.230000, 0x3e6b851f)
|
||||
"R": float32as(0.000000, 0x0)
|
||||
|
||||
registered font
|
||||
default: float32as(0.000000, 0x0)
|
||||
|
||||
// based on CoreText dylib's __Z13WeightOfClasst — WeightOfClass(unsigned short)
|
||||
func CoreText_WeightOfClass(usWeightClass uint16) float64 {
|
||||
if usWeightClass >= 11 {
|
||||
// do nothing; we are preserving the original asm comparisons
|
||||
// and yes, this one is 11, but the one above is 10
|
||||
} else {
|
||||
usWeightClass *= 100
|
||||
}
|
||||
|
||||
// figure out what two floats our weight will be between
|
||||
i := usWeightClass / 100
|
||||
j := i + 1
|
||||
if j > 10 {
|
||||
j = 10
|
||||
}
|
||||
b := float64(i * 100)
|
||||
c := float64(j * 100)
|
||||
|
||||
a := float64(0)
|
||||
if b != c {
|
||||
a = float64(usWeightClass)
|
||||
a -= b
|
||||
c -= b
|
||||
a /= c
|
||||
}
|
||||
scales := []float32{
|
||||
float32as(-1.000000, 0xbf800000),
|
||||
float32as(-0.700000, 0xbf333333),
|
||||
float32as(-0.500000, 0xbf000000),
|
||||
float32as(-0.230000, 0xbe6b851f),
|
||||
float32as(0.000000, 0x0),
|
||||
float32as(0.200000, 0x3e4ccccd),
|
||||
float32as(0.300000, 0x3e99999a),
|
||||
float32as(0.400000, 0x3ecccccd),
|
||||
float32as(0.600000, 0x3f19999a),
|
||||
float32as(0.800000, 0x3f4ccccd),
|
||||
float32as(1.000000, 0x3f800000),
|
||||
}
|
||||
c = float64(scale[i])
|
||||
b = float64[scale[j])
|
||||
return fma(a, b, c)
|
||||
}
|
||||
|
||||
unregistered font: kCTFontPostScriptNameKey defaults
|
||||
"LucidaGrande": float64as(0.000000, 0x0)
|
||||
".LucidaGrandeUI": float64as(0.000000, 0x0)
|
||||
"STHeiti": float64as(0.240000, 0x3fceb851eb851eb8)
|
||||
"STXihei": float64as(-0.100000, 0xbfb999999999999a)
|
||||
"TimesNewRomanPSMT": float64as(0.000000, 0x0)
|
||||
|
||||
|
||||
os2table := f.Table('OS/2')
|
||||
if os2table != nil {
|
||||
if !hasWeight {
|
||||
var usWeightClass uint16
|
||||
|
||||
valid := false
|
||||
if os2table.Len() > 77 {
|
||||
b := os2table.Bytes()
|
||||
usWeightClass = uint16be(b[4:6])
|
||||
if usWeightClass > 1000 {
|
||||
weight = 0
|
||||
hasWeight = false
|
||||
} else {
|
||||
valid = true
|
||||
}
|
||||
} else {
|
||||
usWeightClass = 0
|
||||
valid = true
|
||||
}
|
||||
if valid {
|
||||
weight = CoreText_WeightOfClass(usWeightClass)
|
||||
hasWeight = true
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
unregistered font, style glossary, checks against kCTFontSubFamilyNameKey, kCTFontFullNameKey, kCTFontFamilyNameKey; case-insensitive (folded by Unicode rules) strstr()
|
||||
"ultra light": float32as(-0.800000, 0xbf4ccccd)
|
||||
"ultra black": float32as(0.750000, 0x3f400000)
|
||||
"extra light": float32as(-0.500000, 0xbf000000)
|
||||
"ultralight": float32as(-0.800000, 0xbf4ccccd)
|
||||
"ultrablack": float32as(0.750000, 0x3f400000)
|
||||
"extrablack": float32as(0.800000, 0x3f4ccccd)
|
||||
"extralight": float32as(-0.500000, 0xbf000000)
|
||||
"heavy face": float32as(0.560000, 0x3f0f5c29)
|
||||
"semi light": float32as(-0.200000, 0xbe4ccccd)
|
||||
"extra bold": float32as(0.500000, 0x3f000000)
|
||||
"ultra bold": float32as(0.700000, 0x3f333333)
|
||||
"heavyface": float32as(0.560000, 0x3f0f5c29)
|
||||
"extrabold": float32as(0.500000, 0x3f000000)
|
||||
"ultrabold": float32as(0.700000, 0x3f333333)
|
||||
"semilight": float32as(-0.200000, 0xbe4ccccd)
|
||||
"demi bold": float32as(0.250000, 0x3e800000)
|
||||
"semi bold": float32as(0.300000, 0x3e99999a)
|
||||
"demibold": float32as(0.250000, 0x3e800000)
|
||||
"semibold": float32as(0.300000, 0x3e99999a)
|
||||
"hairline": float32as(-0.700000, 0xbf333333)
|
||||
"medium": float32as(0.230000, 0x3e6b851f)
|
||||
"poster": float32as(0.800000, 0x3f4ccccd)
|
||||
"light": float32as(-0.400000, 0xbecccccd)
|
||||
"heavy": float32as(0.560000, 0x3f0f5c29)
|
||||
"extra": float32as(0.500000, 0x3f000000)
|
||||
"black": float32as(0.620000, 0x3f1eb852)
|
||||
"super": float32as(0.620000, 0x3f1eb852)
|
||||
"obese": float32as(0.850000, 0x3f59999a)
|
||||
"lite": float32as(-0.400000, 0xbecccccd)
|
||||
"book": float32as(-0.230000, 0xbe6b851f)
|
||||
"demi": float32as(0.250000, 0x3e800000)
|
||||
"semi": float32as(0.300000, 0x3e99999a)
|
||||
"thin": float32as(-0.500000, 0xbf000000)
|
||||
"bold": float32as(0.400000, 0x3ecccccd)
|
||||
"nord": float32as(0.800000, 0x3f4ccccd)
|
||||
"fat": float32as(0.750000, 0x3f400000)
|
||||
"w1": float32as(-0.700000, 0xbf333333)
|
||||
"w2": float32as(-0.500000, 0xbf000000)
|
||||
"w3": float32as(-0.230000, 0xbe6b851f)
|
||||
"w4": float32as(0.000000, 0x0)
|
||||
"w5": float32as(0.230000, 0x3e6b851f)
|
||||
"w6": float32as(0.300000, 0x3e99999a)
|
||||
"w7": float32as(0.440000, 0x3ee147ae)
|
||||
"w8": float32as(0.540000, 0x3f0a3d71)
|
||||
"w9": float32as(0.620000, 0x3f1eb852)
|
||||
|
||||
func (f *Font) ShouldEnableBoldSymbolicTrait() bool {
|
||||
if f.IsRegistered() {
|
||||
return f.ShouldEnableBoldSymbolicTraitFromRegistry()
|
||||
}
|
||||
no := f.Weight() <= float64as(0.239000, 0x3fce978d4fdf3b64)
|
||||
return !no
|
||||
}
|
|
@ -0,0 +1,160 @@
|
|||
xx pseudo-go
|
||||
|
||||
func (f *CTFont) RegistryWidth32() float32 {
|
||||
metadata visual descriptors
|
||||
{ "med", float32as(0.000000, 0x0) },
|
||||
{ "cond", float32as(-0.200000, 0xbe4ccccd) },
|
||||
{ "ext", float32as(0.200000, 0x3e4ccccd) },
|
||||
|
||||
style dictionary
|
||||
{ "Extra Compressed", float32as(-0.700000, 0xbf333333) },
|
||||
{ "Ultra Compressed", float32as(-0.700000, 0xbf333333) },
|
||||
{ "Ultra Condensed", float32as(-0.700000, 0xbf333333) },
|
||||
{ "Extra Condensed", float32as(-0.500000, 0xbf000000) },
|
||||
{ "Extra Extended", float32as(0.400000, 0x3ecccccd) },
|
||||
{ "Ext Compressed", float32as(-0.700000, 0xbf333333) },
|
||||
{ "Ultra Expanded", float32as(0.800000, 0x3f4ccccd) },
|
||||
{ "Ultra Extended", float32as(0.800000, 0x3f4ccccd) },
|
||||
{ "Extra Expanded", float32as(0.400000, 0x3ecccccd) },
|
||||
xx TODO this is weird, but correct...
|
||||
{ "Semi Condensed", float32as(-0.700000, 0xbf333333) },
|
||||
{ "Semi Condensed", float32as(-0.100000, 0xbdcccccd) },
|
||||
xx end TODO
|
||||
{ "Ext Condensed", float32as(-0.500000, 0xbf000000) },
|
||||
{ "SemiCondensed", float32as(-0.100000, 0xbdcccccd) },
|
||||
{ "ExtraExpanded", float32as(0.400000, 0x3ecccccd) },
|
||||
{ "Semi Expanded", float32as(0.100000, 0x3dcccccd) },
|
||||
{ "Semi Extended", float32as(0.100000, 0x3dcccccd) },
|
||||
{ "Ext Expanded", float32as(0.400000, 0x3ecccccd) },
|
||||
{ "Ext Extended", float32as(0.400000, 0x3ecccccd) },
|
||||
{ "SemiExpanded", float32as(0.100000, 0x3dcccccd) },
|
||||
{ "Extra Narrow", float32as(-0.500000, 0xbf000000) },
|
||||
{ "ExtraNarrow", float32as(-0.500000, 0xbf000000) },
|
||||
{ "Extra Wide", float32as(0.800000, 0x3f4ccccd) },
|
||||
{ "Ultra Cond", float32as(-0.700000, 0xbf333333) },
|
||||
{ "Compressed", float32as(-0.500000, 0xbf000000) },
|
||||
{ "Extra Cond", float32as(-0.500000, 0xbf000000) },
|
||||
{ "Semi Cond", float32as(-0.100000, 0xbdcccccd) },
|
||||
{ "Condensed", float32as(-0.200000, 0xbe4ccccd) },
|
||||
{ "ExtraWide", float32as(0.800000, 0x3f4ccccd) },
|
||||
{ "Extended", float32as(0.200000, 0x3e4ccccd) },
|
||||
{ "Expanded", float32as(0.200000, 0x3e4ccccd) },
|
||||
{ "Ext Cond", float32as(-0.500000, 0xbf000000) },
|
||||
{ "Narrow", float32as(-0.400000 , 0xbecccccd) },
|
||||
{ "Compact", float32as(-0.400000, 0xbecccccd) },
|
||||
{ "Cond", float32as(-0.200000, 0xbe4ccccd) },
|
||||
{ "Wide", float32as(0.600000, 0x3f19999a) },
|
||||
{ "Thin", float32as(-0.700000, 0xbf333333) },
|
||||
|
||||
get os2 table
|
||||
if os2table.Len() >= 78 {
|
||||
usWidthClass := uint16be(b[6:8]) - 1
|
||||
xx this handles the case where the original usWidthClass == 0
|
||||
if usWidthClass > 8 {
|
||||
panose := b[0x23] - 2
|
||||
if panose > 6 {
|
||||
xx TODO
|
||||
} else {
|
||||
switch panose {
|
||||
case 0, 1, 2:
|
||||
width = float32as(0.000000, 0x0)
|
||||
case 3:
|
||||
width = float32as(0.200000, 0x3e4ccccd)
|
||||
case 4:
|
||||
width = float32as(-0.200000, 0xbe4ccccd)
|
||||
case 5:
|
||||
width = float32as(0.400000, 0x3ecccccd)
|
||||
case 6:
|
||||
width = float32as(-0.400000, 0xbecccccd)
|
||||
}
|
||||
}
|
||||
}
|
||||
switch usWidthClass {
|
||||
case 0:
|
||||
width = float32as(-0.700000, 0xbf333333)
|
||||
case 1:
|
||||
width = float32as(-0.500000, 0xbf000000)
|
||||
case 2:
|
||||
width = float32as(-0.200000, 0xbe4ccccd)
|
||||
case 3:
|
||||
width = float32as(-0.100000, 0xbdcccccd)
|
||||
case 4:
|
||||
width = float32as(0.000000, 0x0)
|
||||
case 5:
|
||||
width = float32as(0.100000, 0x3dcccccd)
|
||||
case 6:
|
||||
width = float32as(0.400000, 0x3ecccccd)
|
||||
case 7:
|
||||
width = float32as(0.600000, 0x3f19999a)
|
||||
case 8:
|
||||
width = float32as(0.800000, 0x3f4ccccd)
|
||||
}
|
||||
}
|
||||
|
||||
headtable := f.CopyTable('head')
|
||||
if headtable != nil {
|
||||
if headtable.Len() >= 54 {
|
||||
x := b[0x2d]
|
||||
if (x & 0x20) != 0 {
|
||||
width = float32as(-0.200000, 0xbe4ccccd)
|
||||
} else if (x & 0x40) != 0 {
|
||||
width = float32as(0.200000, 0x3e4ccccd)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
xx and if all else fails
|
||||
return float32as(0.000000, 0x0)
|
||||
}
|
||||
|
||||
func (f *CTFont) Width() float64 {
|
||||
if f.IsRegistered() {
|
||||
return f.RegistryWidth()
|
||||
}
|
||||
|
||||
width := 0.0
|
||||
hasWidth := false
|
||||
|
||||
if there is an OS2 table {
|
||||
var usWidthClass uint16
|
||||
|
||||
valid := false
|
||||
if it's 78 bytes or more {
|
||||
usWidthClass = uint16be(table[6:8])
|
||||
if usWeightClass <= 10 {
|
||||
valid = true
|
||||
} else {
|
||||
valid = false
|
||||
}
|
||||
} else {
|
||||
usWidthClass = 0
|
||||
valid = true
|
||||
}
|
||||
if valid {
|
||||
ten := float64as(10.000000, 0x4024000000000000)
|
||||
negPointFive := float64as(-0.500000, 0xbfe0000000000000)
|
||||
width = (float64(usWidthClass) div ten) + negPointFive
|
||||
hasWidth = true
|
||||
}
|
||||
}
|
||||
|
||||
then there's the style glossary strings comparison:
|
||||
{ "semi condensed", float32as(-0.100000, 0xbdcccccd) },
|
||||
{ "extra expanded", float32as(0.400000, 0x3ecccccd) },
|
||||
{ "semicondensed", float32as(-0.100000, 0xbdcccccd) },
|
||||
{ "extraexpanded", float32as(0.400000, 0x3ecccccd) },
|
||||
{ "semi expanded", float32as(0.100000, 0x3dcccccd) },
|
||||
{ "semiexpanded", float32as(0.100000, 0x3dcccccd) },
|
||||
{ "extra narrow", float32as(-0.500000, 0xbf000000) },
|
||||
{ "extranarrow", float32as(-0.500000, 0xbf000000) },
|
||||
{ "extra wide", float32as(0.800000, 0x3f4ccccd) },
|
||||
{ "condensed", float32as(-0.200000, 0xbe4ccccd) },
|
||||
{ "extrawide", float32as(0.800000, 0x3f4ccccd) },
|
||||
{ "extended", float32as(0.200000, 0x3e4ccccd) },
|
||||
{ "expanded", float32as(0.200000, 0x3e4ccccd) },
|
||||
{ "narrow", float32as(-0.400000, 0xbecccccd) },
|
||||
{ "wide", float32as(0.600000, 0x3f19999a) },
|
||||
{ "thin", float32as(-0.700000, 0xbf333333) },
|
||||
|
||||
otherwise just return float64as(0.000000, 0x0)
|
||||
}
|
|
@ -0,0 +1,77 @@
|
|||
-0.100000
|
||||
style string contains "Semi Cond"
|
||||
style string contains "Semi Condensed"; unregistered fonts only (see below)
|
||||
style string contains "SemiCondensed"
|
||||
OS2 width 4
|
||||
|
||||
-0.200000
|
||||
('head' table byte 0x2d) & 0x20 != 0
|
||||
ATSD style "cond"
|
||||
panose 6
|
||||
style string contains "Cond"
|
||||
style string contains "Condensed"
|
||||
OS2 width 3
|
||||
|
||||
-0.400000
|
||||
panose 8
|
||||
style string contains "Compact"
|
||||
style string contains "Narrow"
|
||||
|
||||
-0.500000
|
||||
style string contains "Compressed"
|
||||
style string contains "Ext Cond"
|
||||
style string contains "Ext Condensed"
|
||||
style string contains "Extra Cond"
|
||||
style string contains "Extra Condensed"
|
||||
style string contains "Extra Narrow"
|
||||
style string contains "ExtraNarrow"
|
||||
OS2 width 2
|
||||
|
||||
-0.700000
|
||||
style string contains "Ext Compressed"
|
||||
style string contains "Extra Compressed"
|
||||
style string contains "Semi Condensed" (this is probably a typo, since another "Semi Condensed" with a value of -0.1 follows this in the table it comes from); registered fonts only
|
||||
style string contains "Thin"
|
||||
style string contains "Ultra Compressed"
|
||||
style string contains "Ultra Cond"
|
||||
style string contains "Ultra Condensed"
|
||||
OS2 width 1
|
||||
|
||||
0.000000
|
||||
default
|
||||
ATSD style "med"
|
||||
panose 2, 3, 4
|
||||
OS2 width 5
|
||||
|
||||
0.100000
|
||||
style string contains "Semi Expanded"
|
||||
style string contains "Semi Extended"
|
||||
style string contains "SemiExpanded"
|
||||
OS2 width 6
|
||||
|
||||
0.200000
|
||||
('head' table byte 0x2d) & 0x40 != 0
|
||||
ATSD style "ext"
|
||||
panose 5
|
||||
style string contains "Expanded"
|
||||
style string contains "Extended"
|
||||
|
||||
0.400000
|
||||
panose 7
|
||||
style string contains "Ext Expanded"
|
||||
style string contains "Ext Extended"
|
||||
style string contains "Extra Expanded"
|
||||
style string contains "Extra Extended"
|
||||
style string contains "ExtraExpanded"
|
||||
OS2 width 7
|
||||
|
||||
0.600000
|
||||
style string contains "Wide"
|
||||
OS2 width 8
|
||||
|
||||
0.800000
|
||||
style string contains "Extra Wide"
|
||||
style string contains "ExtraWide"
|
||||
style string contains "Ultra Expanded"
|
||||
style string contains "Ultra Extended"
|
||||
OS2 width 9
|
|
@ -0,0 +1,73 @@
|
|||
-0.100000 0xbdcccccd registered "Semi Cond"
|
||||
-0.100000 0xbdcccccd registered "Semi Condensed"
|
||||
-0.100000 0xbdcccccd registered "SemiCondensed"
|
||||
-0.100000 0xbdcccccd registered OS2 4
|
||||
-0.100000 0xbdcccccd unregistered "semi condensed"
|
||||
-0.100000 0xbdcccccd unregistered "semicondensed"
|
||||
-0.200000 0xbe4ccccd head[0x2d] & 0x20
|
||||
-0.200000 0xbe4ccccd metadata "cond"
|
||||
-0.200000 0xbe4ccccd panose 6
|
||||
-0.200000 0xbe4ccccd registered "Cond"
|
||||
-0.200000 0xbe4ccccd registered "Condensed"
|
||||
-0.200000 0xbe4ccccd registered OS2 3
|
||||
-0.200000 0xbe4ccccd unregistered "condensed"
|
||||
-0.400000 0xbecccccd panose 8
|
||||
-0.400000 0xbecccccd registered "Compact"
|
||||
-0.400000 0xbecccccd registered "Narrow"
|
||||
-0.400000 0xbecccccd unregistered "narrow"
|
||||
-0.500000 0xbf000000 registered "Compressed"
|
||||
-0.500000 0xbf000000 registered "Ext Cond"
|
||||
-0.500000 0xbf000000 registered "Ext Condensed"
|
||||
-0.500000 0xbf000000 registered "Extra Cond"
|
||||
-0.500000 0xbf000000 registered "Extra Condensed"
|
||||
-0.500000 0xbf000000 registered "Extra Narrow"
|
||||
-0.500000 0xbf000000 registered "ExtraNarrow"
|
||||
-0.500000 0xbf000000 registered OS2 2
|
||||
-0.500000 0xbf000000 unregistered "extra narrow"
|
||||
-0.500000 0xbf000000 unregistered "extranarrow"
|
||||
-0.700000 0xbf333333 registered "Ext Compressed"
|
||||
-0.700000 0xbf333333 registered "Extra Compressed"
|
||||
-0.700000 0xbf333333 registered "Semi Condensed"
|
||||
-0.700000 0xbf333333 registered "Thin"
|
||||
-0.700000 0xbf333333 registered "Ultra Compressed"
|
||||
-0.700000 0xbf333333 registered "Ultra Cond"
|
||||
-0.700000 0xbf333333 registered "Ultra Condensed"
|
||||
-0.700000 0xbf333333 registered OS2 1
|
||||
-0.700000 0xbf333333 unregistered "thin"
|
||||
0.000000 0x0 default
|
||||
0.000000 0x0 metadata "med"
|
||||
0.000000 0x0 panose 2, 3, 4
|
||||
0.000000 0x0 registered OS2 5
|
||||
0.000000 0x0 registered default
|
||||
0.100000 0x3dcccccd registered "Semi Expanded"
|
||||
0.100000 0x3dcccccd registered "Semi Extended"
|
||||
0.100000 0x3dcccccd registered "SemiExpanded"
|
||||
0.100000 0x3dcccccd registered OS2 6
|
||||
0.100000 0x3dcccccd unregistered "semi expanded"
|
||||
0.100000 0x3dcccccd unregistered "semiexpanded"
|
||||
0.200000 0x3e4ccccd head[0x2d] & 0x40
|
||||
0.200000 0x3e4ccccd metadata "ext"
|
||||
0.200000 0x3e4ccccd panose 5
|
||||
0.200000 0x3e4ccccd registered "Expanded"
|
||||
0.200000 0x3e4ccccd registered "Extended"
|
||||
0.200000 0x3e4ccccd unregistered "expanded"
|
||||
0.200000 0x3e4ccccd unregistered "extended"
|
||||
0.400000 0x3ecccccd panose 7
|
||||
0.400000 0x3ecccccd registered "Ext Expanded"
|
||||
0.400000 0x3ecccccd registered "Ext Extended"
|
||||
0.400000 0x3ecccccd registered "Extra Expanded"
|
||||
0.400000 0x3ecccccd registered "Extra Extended"
|
||||
0.400000 0x3ecccccd registered "ExtraExpanded"
|
||||
0.400000 0x3ecccccd registered OS2 7
|
||||
0.400000 0x3ecccccd unregistered "extra expanded"
|
||||
0.400000 0x3ecccccd unregistered "extraexpanded"
|
||||
0.600000 0x3f19999a registered "Wide"
|
||||
0.600000 0x3f19999a registered OS2 8
|
||||
0.600000 0x3f19999a unregistered "wide"
|
||||
0.800000 0x3f4ccccd registered "Extra Wide"
|
||||
0.800000 0x3f4ccccd registered "ExtraWide"
|
||||
0.800000 0x3f4ccccd registered "Ultra Expanded"
|
||||
0.800000 0x3f4ccccd registered "Ultra Extended"
|
||||
0.800000 0x3f4ccccd registered OS2 9
|
||||
0.800000 0x3f4ccccd unregistered "extra wide"
|
||||
0.800000 0x3f4ccccd unregistered "extrawide"
|
|
@ -0,0 +1,112 @@
|
|||
metadata "med": float32as(0.000000, 0x0)
|
||||
metadata "cond": float32as(-0.200000, 0xbe4ccccd)
|
||||
metadata "ext": float32as(0.200000, 0x3e4ccccd)
|
||||
|
||||
registered "Extra Compressed": float32as(-0.700000, 0xbf333333)
|
||||
registered "Ultra Compressed": float32as(-0.700000, 0xbf333333)
|
||||
registered "Ultra Condensed": float32as(-0.700000, 0xbf333333)
|
||||
registered "Extra Condensed": float32as(-0.500000, 0xbf000000)
|
||||
registered "Extra Extended": float32as(0.400000, 0x3ecccccd)
|
||||
registered "Ext Compressed": float32as(-0.700000, 0xbf333333)
|
||||
registered "Ultra Expanded": float32as(0.800000, 0x3f4ccccd)
|
||||
registered "Ultra Extended": float32as(0.800000, 0x3f4ccccd)
|
||||
registered "Extra Expanded": float32as(0.400000, 0x3ecccccd)
|
||||
registered "Semi Condensed": float32as(-0.700000, 0xbf333333)
|
||||
registered "Semi Condensed": float32as(-0.100000, 0xbdcccccd)
|
||||
registered "Ext Condensed": float32as(-0.500000, 0xbf000000)
|
||||
registered "SemiCondensed": float32as(-0.100000, 0xbdcccccd)
|
||||
registered "ExtraExpanded": float32as(0.400000, 0x3ecccccd)
|
||||
registered "Semi Expanded": float32as(0.100000, 0x3dcccccd)
|
||||
registered "Semi Extended": float32as(0.100000, 0x3dcccccd)
|
||||
registered "Ext Expanded": float32as(0.400000, 0x3ecccccd)
|
||||
registered "Ext Extended": float32as(0.400000, 0x3ecccccd)
|
||||
registered "SemiExpanded": float32as(0.100000, 0x3dcccccd)
|
||||
registered "Extra Narrow": float32as(-0.500000, 0xbf000000)
|
||||
registered "ExtraNarrow": float32as(-0.500000, 0xbf000000)
|
||||
registered "Extra Wide": float32as(0.800000, 0x3f4ccccd)
|
||||
registered "Ultra Cond": float32as(-0.700000, 0xbf333333)
|
||||
registered "Compressed": float32as(-0.500000, 0xbf000000)
|
||||
registered "Extra Cond": float32as(-0.500000, 0xbf000000)
|
||||
registered "Semi Cond": float32as(-0.100000, 0xbdcccccd)
|
||||
registered "Condensed": float32as(-0.200000, 0xbe4ccccd)
|
||||
registered "ExtraWide": float32as(0.800000, 0x3f4ccccd)
|
||||
registered "Extended": float32as(0.200000, 0x3e4ccccd)
|
||||
registered "Expanded": float32as(0.200000, 0x3e4ccccd)
|
||||
registered "Ext Cond": float32as(-0.500000, 0xbf000000)
|
||||
registered "Narrow": float32as(-0.400000 , 0xbecccccd)
|
||||
registered "Compact": float32as(-0.400000, 0xbecccccd)
|
||||
registered "Cond": float32as(-0.200000, 0xbe4ccccd)
|
||||
registered "Wide": float32as(0.600000, 0x3f19999a)
|
||||
registered "Thin": float32as(-0.700000, 0xbf333333)
|
||||
|
||||
panose 2, 3, 4: float32as(0.000000, 0x0)
|
||||
panose 5: float32as(0.200000, 0x3e4ccccd)
|
||||
panose 6: float32as(-0.200000, 0xbe4ccccd)
|
||||
panose 7: float32as(0.400000, 0x3ecccccd)
|
||||
panose 8: float32as(-0.400000, 0xbecccccd)
|
||||
|
||||
registered OS2 1: float32as(-0.700000, 0xbf333333)
|
||||
registered OS2 2: float32as(-0.500000, 0xbf000000)
|
||||
registered OS2 3: float32as(-0.200000, 0xbe4ccccd)
|
||||
registered OS2 4: float32as(-0.100000, 0xbdcccccd)
|
||||
registered OS2 5: float32as(0.000000, 0x0)
|
||||
registered OS2 6: float32as(0.100000, 0x3dcccccd)
|
||||
registered OS2 7: float32as(0.400000, 0x3ecccccd)
|
||||
registered OS2 8: float32as(0.600000, 0x3f19999a)
|
||||
registered OS2 9: float32as(0.800000, 0x3f4ccccd)
|
||||
|
||||
head[0x2d] & 0x20: float32as(-0.200000, 0xbe4ccccd)
|
||||
head[0x2d] & 0x40: float32as(0.200000, 0x3e4ccccd)
|
||||
|
||||
registered default: float32as(0.000000, 0x0)
|
||||
|
||||
func (f *CTFont) Width() float64 {
|
||||
if f.IsRegistered() {
|
||||
return f.RegistryWidth()
|
||||
}
|
||||
|
||||
width := 0.0
|
||||
hasWidth := false
|
||||
|
||||
if there is an OS2 table {
|
||||
var usWidthClass uint16
|
||||
|
||||
valid := false
|
||||
if it's 78 bytes or more {
|
||||
usWidthClass = uint16be(table[6:8])
|
||||
if usWeightClass <= 10 {
|
||||
valid = true
|
||||
} else {
|
||||
valid = false
|
||||
}
|
||||
} else {
|
||||
usWidthClass = 0
|
||||
valid = true
|
||||
}
|
||||
if valid {
|
||||
ten := float64as(10.000000, 0x4024000000000000)
|
||||
negPointFive := float64as(-0.500000, 0xbfe0000000000000)
|
||||
width = (float64(usWidthClass) div ten) + negPointFive
|
||||
hasWidth = true
|
||||
}
|
||||
}
|
||||
|
||||
then there's the style glossary strings comparison:
|
||||
unregistered "semi condensed": float32as(-0.100000, 0xbdcccccd)
|
||||
unregistered "extra expanded": float32as(0.400000, 0x3ecccccd)
|
||||
unregistered "semicondensed": float32as(-0.100000, 0xbdcccccd)
|
||||
unregistered "extraexpanded": float32as(0.400000, 0x3ecccccd)
|
||||
unregistered "semi expanded": float32as(0.100000, 0x3dcccccd)
|
||||
unregistered "semiexpanded": float32as(0.100000, 0x3dcccccd)
|
||||
unregistered "extra narrow": float32as(-0.500000, 0xbf000000)
|
||||
unregistered "extranarrow": float32as(-0.500000, 0xbf000000)
|
||||
unregistered "extra wide": float32as(0.800000, 0x3f4ccccd)
|
||||
unregistered "condensed": float32as(-0.200000, 0xbe4ccccd)
|
||||
unregistered "extrawide": float32as(0.800000, 0x3f4ccccd)
|
||||
unregistered "extended": float32as(0.200000, 0x3e4ccccd)
|
||||
unregistered "expanded": float32as(0.200000, 0x3e4ccccd)
|
||||
unregistered "narrow": float32as(-0.400000, 0xbecccccd)
|
||||
unregistered "wide": float32as(0.600000, 0x3f19999a)
|
||||
unregistered "thin": float32as(-0.700000, 0xbf333333)
|
||||
|
||||
default: float64as(0.000000, 0x0)
|
|
@ -0,0 +1,73 @@
|
|||
// 2 november 2017
|
||||
import Cocoa
|
||||
import CoreText
|
||||
|
||||
func fourccString(_ k: FourCharCode) -> String {
|
||||
var c: [UInt8] = [0, 0, 0, 0]
|
||||
c[0] = UInt8((k >> 24) & 0xFF)
|
||||
c[1] = UInt8((k >> 16) & 0xFF)
|
||||
c[2] = UInt8((k >> 8) & 0xFF)
|
||||
c[3] = UInt8(k & 0xFF)
|
||||
return String(bytes: c, encoding: .utf8)!
|
||||
}
|
||||
|
||||
var weightMin: Double = 0
|
||||
var weightMax: Double = 0
|
||||
var weightDef: Double = 0
|
||||
var weightVals: [String: Double] = [:]
|
||||
|
||||
let attrs: [String: String] = [
|
||||
kCTFontFamilyNameAttribute as String: "Skia",
|
||||
]
|
||||
let bd = CTFontDescriptorCreateWithAttributes(attrs as CFDictionary)
|
||||
let matches = CTFontDescriptorCreateMatchingFontDescriptors(bd, nil) as! [CTFontDescriptor]
|
||||
let mfont = CTFontCreateWithFontDescriptor(matches[0], 0.0, nil)
|
||||
let master = CTFontCopyVariationAxes(mfont) as! [NSDictionary]
|
||||
master.forEach { d in
|
||||
print("axis {")
|
||||
d.forEach { k, v in
|
||||
if (k as! String) == (kCTFontVariationAxisIdentifierKey as String) {
|
||||
let c = v as! FourCharCode
|
||||
print("\t\(k) \(fourccString(c))")
|
||||
} else {
|
||||
print("\t\(k) \(v)")
|
||||
}
|
||||
}
|
||||
print("}")
|
||||
if (d[kCTFontVariationAxisNameKey] as! String) == "Weight" {
|
||||
weightMin = d[kCTFontVariationAxisMinimumValueKey] as! Double
|
||||
weightMax = d[kCTFontVariationAxisMaximumValueKey] as! Double
|
||||
weightDef = d[kCTFontVariationAxisDefaultValueKey] as! Double
|
||||
}
|
||||
}
|
||||
print("")
|
||||
matches.forEach { d in
|
||||
let f = CTFontDescriptorCopyAttribute(d, kCTFontVariationAttribute) as! [FourCharCode: Double]
|
||||
let n = CTFontDescriptorCopyAttribute(d, kCTFontStyleNameAttribute) as! String
|
||||
print("\(n) {")
|
||||
f.forEach { k, v in
|
||||
print("\t\(fourccString(k)) \(v)")
|
||||
}
|
||||
print("}")
|
||||
weightVals[n] = weightDef
|
||||
if let v = f[2003265652] {
|
||||
weightVals[n] = v
|
||||
}
|
||||
}
|
||||
print("")
|
||||
weightVals.forEach { k, v in
|
||||
let basicScaled = (v - weightMin) / (weightMax - weightMin)
|
||||
print("\(k) basic scaled = \(basicScaled) (OS2 \(UInt16(basicScaled * 1000)))")
|
||||
// https://www.microsoft.com/typography/otspec/otvaroverview.htm#CSN
|
||||
var opentypeScaled: Double = 0
|
||||
if v < weightDef {
|
||||
opentypeScaled = -((weightDef - v) / (weightDef - weightMin))
|
||||
} else if v > weightDef {
|
||||
opentypeScaled = (v - weightDef) / (weightMax - weightDef)
|
||||
}
|
||||
print("\(k) opentype scaled = \(opentypeScaled)")
|
||||
}
|
||||
print("")
|
||||
print("\(String(describing: CTFontDescriptorCreateMatchingFontDescriptors(CTFontDescriptorCreateCopyWithVariation(matches[0], FourCharCode(2003265652) as CFNumber, CGFloat(weightMax)), Set([kCTFontVariationAttribute as String]) as CFSet)))")
|
||||
print("")
|
||||
print("\(CTFontCopyTable(mfont, CTFontTableTag(kCTFontTableAvar), []) != nil)")
|
|
@ -0,0 +1,58 @@
|
|||
// 2 november 2017
|
||||
package main
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
)
|
||||
|
||||
type fixed1616 uint32
|
||||
type fixed214 uint16
|
||||
|
||||
func fixed1616To214(f fixed1616) fixed214 {
|
||||
f += 0x00000002
|
||||
g := int32(f) >> 2
|
||||
return fixed214(uint32(g) & 0xFFFF)
|
||||
}
|
||||
|
||||
func (f fixed1616) In214Range() bool {
|
||||
base := int16((f >> 16) & 0xFFFF)
|
||||
return base >= -2 && base < 2
|
||||
}
|
||||
|
||||
func (f fixed1616) String() string {
|
||||
base := int16((f >> 16) & 0xFFFF)
|
||||
frac := float64(f & 0xFFFF) / 65536
|
||||
res := float64(base) + frac
|
||||
return fmt.Sprintf("%f 0x%08X", res, uint32(f))
|
||||
}
|
||||
|
||||
func (f fixed214) String() string {
|
||||
base := []int16{
|
||||
0,
|
||||
1,
|
||||
-2,
|
||||
-1,
|
||||
}[(f & 0xC000) >> 14]
|
||||
frac := float64(f & 0x3FFF) / 16384
|
||||
res := float64(base) + frac
|
||||
return fmt.Sprintf("%f 0x%04X", res, uint16(f))
|
||||
}
|
||||
|
||||
func main() {
|
||||
fmt.Println(fixed214(0x7fff))
|
||||
fmt.Println(fixed214(0x8000))
|
||||
fmt.Println(fixed214(0x4000))
|
||||
fmt.Println(fixed214(0xc000))
|
||||
fmt.Println(fixed214(0x7000))
|
||||
fmt.Println(fixed214(0x0000))
|
||||
fmt.Println(fixed214(0x0001))
|
||||
fmt.Println(fixed214(0xffff))
|
||||
|
||||
fmt.Println()
|
||||
|
||||
for i := uint64(0x00000000); i <= 0xFFFFFFFF; i++ {
|
||||
j := fixed1616(i)
|
||||
if !j.In214Range() { continue }
|
||||
fmt.Println(j, "->", fixed1616To214(j))
|
||||
}
|
||||
}
|
|
@ -0,0 +1,8 @@
|
|||
# 21 october 2017
|
||||
gawk '
|
||||
BEGIN { FS = "\t+" }
|
||||
!/float..as/ { next }
|
||||
{ i = 0; if ($1 == "") i++ }
|
||||
(NF-i) != 2 { next }
|
||||
{ print }
|
||||
' "$@"
|
|
@ -0,0 +1,10 @@
|
|||
# 21 october 2017
|
||||
gawk '
|
||||
{
|
||||
gsub(/float..as\(/, "")
|
||||
gsub(/,/, "", $(NF - 1))
|
||||
gsub(/\)$/, "")
|
||||
split($0, parts, /:/)
|
||||
print $(NF - 1) "\t" $NF "\t" parts[1]
|
||||
}
|
||||
' "$@"
|
|
@ -0,0 +1,3 @@
|
|||
# 21 october 2017
|
||||
sort -t$'\t' -k1,1 -k2,2 "$@" |
|
||||
column -t -s$'\t'
|
|
@ -0,0 +1,3 @@
|
|||
// 22 october 2017
|
||||
extern int realMain(void);
|
||||
int main(void) { return realMain(); }
|
|
@ -0,0 +1,11 @@
|
|||
unregistered OS2 0: float64as(-0.5, 0xbfe0000000000000)
|
||||
unregistered OS2 1: float64as(-0.4, 0xbfd999999999999a)
|
||||
unregistered OS2 2: float64as(-0.3, 0xbfd3333333333333)
|
||||
unregistered OS2 3: float64as(-0.2, 0xbfc999999999999a)
|
||||
unregistered OS2 4: float64as(-0.1, 0xbfb9999999999998)
|
||||
unregistered OS2 5: float64as(0, 0x0000000000000000)
|
||||
unregistered OS2 6: float64as(0.1, 0x3fb9999999999998)
|
||||
unregistered OS2 7: float64as(0.2, 0x3fc9999999999998)
|
||||
unregistered OS2 8: float64as(0.3, 0x3fd3333333333334)
|
||||
unregistered OS2 9: float64as(0.4, 0x3fd999999999999a)
|
||||
unregistered OS2 10: float64as(0.5, 0x3fe0000000000000)
|
|
@ -0,0 +1,11 @@
|
|||
-0.1 0xbfb9999999999998 unregistered OS2 4
|
||||
-0.2 0xbfc999999999999a unregistered OS2 3
|
||||
-0.3 0xbfd3333333333333 unregistered OS2 2
|
||||
-0.4 0xbfd999999999999a unregistered OS2 1
|
||||
-0.5 0xbfe0000000000000 unregistered OS2 0
|
||||
0 0x0000000000000000 unregistered OS2 5
|
||||
0.1 0x3fb9999999999998 unregistered OS2 6
|
||||
0.2 0x3fc9999999999998 unregistered OS2 7
|
||||
0.3 0x3fd3333333333334 unregistered OS2 8
|
||||
0.4 0x3fd999999999999a unregistered OS2 9
|
||||
0.5 0x3fe0000000000000 unregistered OS2 10
|
|
@ -0,0 +1,51 @@
|
|||
# 22 october 2017
|
||||
# clang -o writewidths writewidths.c writewidths.s -g -Wall -Wextra -pedantic -g
|
||||
# thanks to:
|
||||
# - http://www.idryman.org/blog/2014/12/02/writing-64-bit-assembly-on-mac-os-x/
|
||||
# - https://developer.apple.com/library/content/documentation/DeveloperTools/Reference/Assembler/060-i386_Addressing_Modes_and_Assembler_Instructions/i386_intructions.html#//apple_ref/doc/uid/TP30000825-TPXREF101
|
||||
# - https://stackoverflow.com/questions/46309041/trivial-macos-assembly-64-bit-program-has-incorrect-stack-alignment
|
||||
# - https://www.google.com/search?q=macos+implement+main+in+assembly+-nasm&oq=macos+implement+main+in+assembly+-nasm&gs_l=psy-ab.3...12877.13839.0.13988.6.6.0.0.0.0.117.407.4j1.5.0....0...1.1.64.psy-ab..1.0.0....0.et6MkokjvwA
|
||||
# - https://stackoverflow.com/questions/2529185/what-are-cfi-directives-in-gnu-assembler-gas-used-for
|
||||
|
||||
.section __DATA,__data
|
||||
|
||||
double10:
|
||||
.quad 0x4024000000000000
|
||||
doubleNeg05:
|
||||
.quad 0xbfe0000000000000
|
||||
|
||||
fmt:
|
||||
.asciz "unregistered OS2 %ld:\tfloat64as(%g, 0x%016lx)\n"
|
||||
|
||||
.section __TEXT,__text
|
||||
.globl _realMain
|
||||
_realMain:
|
||||
pushq %rbp
|
||||
movq %rsp, %rbp
|
||||
addq $8, %rsp
|
||||
|
||||
xorq %rcx, %rcx
|
||||
loop:
|
||||
pushq %rcx
|
||||
# the code from core text
|
||||
movzwl %cx, %ecx
|
||||
xorps %xmm0, %xmm0
|
||||
cvtsi2sdl %ecx, %xmm0
|
||||
divsd double10(%rip), %xmm0
|
||||
addsd doubleNeg05(%rip), %xmm0
|
||||
# end core text code
|
||||
popq %rcx
|
||||
pushq %rcx
|
||||
movd %xmm0, %rdx
|
||||
movzwq %cx, %rsi
|
||||
leaq fmt(%rip), %rdi
|
||||
callq _printf
|
||||
popq %rcx
|
||||
incw %cx
|
||||
cmpw $10, %cx
|
||||
jbe loop
|
||||
|
||||
xorq %rax, %rax
|
||||
subq $8, %rsp
|
||||
popq %rbp
|
||||
ret
|
|
@ -0,0 +1 @@
|
|||
the function passed to mainsteps must not return until uiQuit itself has been called; otherwise the results are undefined
|
|
@ -0,0 +1,136 @@
|
|||
// 25 june 2018
|
||||
#include <gtk/gtk.h>
|
||||
|
||||
GtkWidget *mainwin;
|
||||
GtkWidget *vbox;
|
||||
GtkWidget *hbox;
|
||||
GtkWidget *startProgress;
|
||||
GtkWidget *startTable;
|
||||
GtkWidget *progressbar;
|
||||
GtkWidget *scrolledWindow;
|
||||
GtkListStore *model;
|
||||
GtkWidget *treeview;
|
||||
GtkWidget *hbox2;
|
||||
|
||||
static gboolean pulseProgress(gpointer data)
|
||||
{
|
||||
gtk_progress_bar_pulse(GTK_PROGRESS_BAR(progressbar));
|
||||
return TRUE;
|
||||
}
|
||||
|
||||
static void onStartProgressClicked(GtkButton *button, gpointer data)
|
||||
{
|
||||
gtk_widget_set_sensitive(startProgress, FALSE);
|
||||
g_timeout_add(100, pulseProgress, NULL);
|
||||
}
|
||||
|
||||
gboolean pbarStarted = FALSE;
|
||||
gint pbarValue;
|
||||
|
||||
static void pbarDataFunc(GtkTreeViewColumn *col, GtkCellRenderer *r, GtkTreeModel *m, GtkTreeIter *iter, gpointer data)
|
||||
{
|
||||
if (!pbarStarted) {
|
||||
g_object_set(r,
|
||||
"pulse", -1,
|
||||
"value", 0,
|
||||
NULL);
|
||||
return;
|
||||
}
|
||||
pbarValue++;
|
||||
if (pbarValue == G_MAXINT)
|
||||
pbarValue = 1;
|
||||
g_object_set(r, "pulse", pbarValue, NULL);
|
||||
}
|
||||
|
||||
static gboolean pulseTable(gpointer data)
|
||||
{
|
||||
GtkTreePath *path;
|
||||
GtkTreeIter iter;
|
||||
|
||||
path = gtk_tree_path_new_from_indices(0, -1);
|
||||
gtk_tree_model_get_iter(GTK_TREE_MODEL(model), &iter, path);
|
||||
gtk_tree_model_row_changed(GTK_TREE_MODEL(model), path, &iter);
|
||||
gtk_tree_path_free(path);
|
||||
return TRUE;
|
||||
}
|
||||
|
||||
static void onStartTableClicked(GtkButton *button, gpointer data)
|
||||
{
|
||||
pbarStarted = TRUE;
|
||||
pbarValue = 0;
|
||||
|
||||
gtk_widget_set_sensitive(startTable, FALSE);
|
||||
g_timeout_add(100, pulseTable, NULL);
|
||||
}
|
||||
|
||||
static gboolean onClosing(GtkWidget *win, GdkEvent *e, gpointer data)
|
||||
{
|
||||
gtk_main_quit();
|
||||
return FALSE;
|
||||
}
|
||||
|
||||
int main(void)
|
||||
{
|
||||
GtkTreeIter iter;
|
||||
GtkTreeViewColumn *col;
|
||||
GtkCellRenderer *r;
|
||||
|
||||
gtk_init(NULL, NULL);
|
||||
|
||||
mainwin = gtk_window_new(GTK_WINDOW_TOPLEVEL);
|
||||
g_signal_connect(mainwin, "delete-event", G_CALLBACK(onClosing), NULL);
|
||||
|
||||
vbox = gtk_box_new(GTK_ORIENTATION_VERTICAL, 12);
|
||||
gtk_container_set_border_width(GTK_CONTAINER(vbox), 12);
|
||||
gtk_container_add(GTK_CONTAINER(mainwin), vbox);
|
||||
|
||||
hbox = gtk_box_new(GTK_ORIENTATION_HORIZONTAL, 6);
|
||||
gtk_widget_set_halign(hbox, GTK_ALIGN_CENTER);
|
||||
gtk_container_add(GTK_CONTAINER(vbox), hbox);
|
||||
|
||||
startProgress = gtk_button_new_with_label("Start Progress Bar");
|
||||
g_signal_connect(startProgress, "clicked", G_CALLBACK(onStartProgressClicked), NULL);
|
||||
gtk_container_add(GTK_CONTAINER(hbox), startProgress);
|
||||
|
||||
startTable = gtk_button_new_with_label("Start Table Cell Renderer");
|
||||
g_signal_connect(startTable, "clicked", G_CALLBACK(onStartTableClicked), NULL);
|
||||
gtk_container_add(GTK_CONTAINER(hbox), startTable);
|
||||
|
||||
progressbar = gtk_progress_bar_new();
|
||||
gtk_container_add(GTK_CONTAINER(vbox), progressbar);
|
||||
|
||||
scrolledWindow = gtk_scrolled_window_new(NULL, NULL);
|
||||
gtk_scrolled_window_set_shadow_type(GTK_SCROLLED_WINDOW(scrolledWindow), GTK_SHADOW_IN);
|
||||
gtk_widget_set_vexpand(scrolledWindow, TRUE);
|
||||
gtk_container_add(GTK_CONTAINER(vbox), scrolledWindow);
|
||||
|
||||
model = gtk_list_store_new(1, G_TYPE_INT);
|
||||
gtk_list_store_append(model, &iter);
|
||||
gtk_list_store_set(model, &iter,
|
||||
0, 0,
|
||||
-1);
|
||||
|
||||
treeview = gtk_tree_view_new_with_model(GTK_TREE_MODEL(model));
|
||||
gtk_container_add(GTK_CONTAINER(scrolledWindow), treeview);
|
||||
|
||||
col = gtk_tree_view_column_new();
|
||||
gtk_tree_view_column_set_resizable(col, TRUE);
|
||||
gtk_tree_view_column_set_title(col, "Column");
|
||||
gtk_tree_view_append_column(GTK_TREE_VIEW(treeview), col);
|
||||
|
||||
r = gtk_cell_renderer_progress_new();
|
||||
gtk_tree_view_column_pack_start(col, r, TRUE);
|
||||
gtk_tree_view_column_set_cell_data_func(col, r, pbarDataFunc, NULL, NULL);
|
||||
|
||||
hbox2 = gtk_box_new(GTK_ORIENTATION_HORIZONTAL, 6);
|
||||
gtk_widget_set_halign(hbox2, GTK_ALIGN_CENTER);
|
||||
gtk_container_add(GTK_CONTAINER(vbox), hbox2);
|
||||
|
||||
gtk_container_add(GTK_CONTAINER(hbox2), gtk_button_new_with_label("These buttons"));
|
||||
gtk_container_add(GTK_CONTAINER(hbox2), gtk_button_new_with_label("do nothing"));
|
||||
gtk_container_add(GTK_CONTAINER(hbox2), gtk_button_new_with_label("when clicked"));
|
||||
|
||||
gtk_widget_show_all(mainwin);
|
||||
gtk_main();
|
||||
return 0;
|
||||
}
|
|
@ -0,0 +1,907 @@
|
|||
// 9 october 2018
|
||||
#define UNICODE
|
||||
#define _UNICODE
|
||||
#define STRICT
|
||||
#define STRICT_TYPED_ITEMIDS
|
||||
#define WINVER 0x0600 /* from Microsoft's winnls.h */
|
||||
#define _WIN32_WINNT 0x0600
|
||||
#define _WIN32_WINDOWS 0x0600 /* from Microsoft's pdh.h */
|
||||
#define _WIN32_IE 0x0700
|
||||
#define NTDDI_VERSION 0x06000000
|
||||
#include <windows.h>
|
||||
#include <commctrl.h>
|
||||
#include <uxtheme.h>
|
||||
#include <vsstyle.h>
|
||||
#include <vssym32.h>
|
||||
#include <windowsx.h>
|
||||
#include <stdio.h>
|
||||
#include <stdlib.h>
|
||||
|
||||
// cl winbuttonexplorertheme.cpp -MT -link user32.lib kernel32.lib gdi32.lib comctl32.lib uxtheme.lib msimg32.lib windows.res
|
||||
|
||||
void diele(const char *func)
|
||||
{
|
||||
DWORD le;
|
||||
|
||||
le = GetLastError();
|
||||
fprintf(stderr, "%s: %I32u\n", func, le);
|
||||
exit(EXIT_FAILURE);
|
||||
}
|
||||
|
||||
void diehr(const char *func, HRESULT hr)
|
||||
{
|
||||
fprintf(stderr, "%s: 0x%08I32X\n", func, hr);
|
||||
exit(EXIT_FAILURE);
|
||||
}
|
||||
|
||||
// TODO if we merge this into libui proper, this will need to be deduplicated
|
||||
static inline HRESULT lastErrorToHRESULT(DWORD lastError)
|
||||
{
|
||||
if (lastError == 0)
|
||||
return E_FAIL;
|
||||
return HRESULT_FROM_WIN32(lastError);
|
||||
}
|
||||
|
||||
HINSTANCE hInstance;
|
||||
HWND leftButtons[5];
|
||||
HWND rightButtons[3];
|
||||
|
||||
class commandModuleStyleParams {
|
||||
public:
|
||||
virtual int partID_CMOD_MODULEBACKGROUND(void) const = 0;
|
||||
virtual int partID_CMOD_TASKBUTTON(void) const = 0;
|
||||
virtual int partID_CMOD_SPLITBUTTONLEFT(void) const = 0;
|
||||
virtual int partID_CMOD_SPLITBUTTONRIGHT(void) const = 0;
|
||||
virtual int partID_CMOD_MENUGLYPH(void) const = 0;
|
||||
virtual int partID_CMOD_OVERFLOWGLYPH(void) const = 0;
|
||||
|
||||
virtual int stateID_CMODS_NORMAL(void) const = 0;
|
||||
virtual int stateID_CMODS_HOT(void) const = 0;
|
||||
virtual int stateID_CMODS_PRESSED(void) const = 0;
|
||||
virtual int stateID_CMODS_KEYFOCUSED(void) const = 0;
|
||||
virtual int stateID_CMODS_NEARHOT(void) const = 0;
|
||||
|
||||
virtual HRESULT backgroundGradientColors(HTHEME theme, COLORREF *colors) const = 0;
|
||||
virtual HRESULT buttonTextColor(HTHEME theme, UINT uItemState, COLORREF *color) const = 0;
|
||||
virtual BOOL buttonTextShadowed(UINT uItemState) const = 0;
|
||||
|
||||
virtual int folderBarMarginsLeftDIP(void) const = 0;
|
||||
virtual int folderBarMarginsTopDIP(void) const = 0;
|
||||
virtual int folderBarMarginsRightDIP(void) const = 0;
|
||||
virtual int folderBarMarginsBottomDIP(void) const = 0;
|
||||
|
||||
virtual int buttonMarginsXDIP(void) const = 0;
|
||||
virtual int buttonMarginsYDIP(void) const = 0;
|
||||
virtual int buttonTextArrowSeparationXDIP(void) const = 0;
|
||||
};
|
||||
|
||||
class commandModuleStyleParamsVista : public commandModuleStyleParams {
|
||||
public:
|
||||
virtual int partID_CMOD_MODULEBACKGROUND(void) const { return 1; }
|
||||
virtual int partID_CMOD_TASKBUTTON(void) const { return 2; }
|
||||
virtual int partID_CMOD_SPLITBUTTONLEFT(void) const { return 3; }
|
||||
virtual int partID_CMOD_SPLITBUTTONRIGHT(void) const { return 4; }
|
||||
virtual int partID_CMOD_MENUGLYPH(void) const { return 5; }
|
||||
virtual int partID_CMOD_OVERFLOWGLYPH(void) const { return 6; }
|
||||
|
||||
virtual int stateID_CMODS_NORMAL(void) const { return 1; }
|
||||
virtual int stateID_CMODS_HOT(void) const { return 2; }
|
||||
virtual int stateID_CMODS_PRESSED(void) const { return 3; }
|
||||
virtual int stateID_CMODS_KEYFOCUSED(void) const { return 4; }
|
||||
virtual int stateID_CMODS_NEARHOT(void) const { return 5; }
|
||||
|
||||
virtual HRESULT backgroundGradientColors(HTHEME theme, COLORREF *colors) const
|
||||
{
|
||||
if (colors == NULL)
|
||||
return E_POINTER;
|
||||
#define argb(a, r, g, b) ((((COLORREF) ((BYTE) (a))) << 24) | RGB(r, g, b))
|
||||
colors[0] = argb(255, 4, 80, 130);
|
||||
colors[1] = argb(255, 17, 101, 132);
|
||||
colors[2] = argb(255, 29, 121, 134);
|
||||
return S_OK;
|
||||
}
|
||||
|
||||
virtual HRESULT buttonTextColor(HTHEME theme, UINT uItemState, COLORREF *color) const
|
||||
{
|
||||
if (color == NULL)
|
||||
return E_POINTER;
|
||||
*color = GetSysColor(COLOR_WINDOW);
|
||||
if ((uItemState & CDIS_DISABLED) != 0)
|
||||
*color = argb(255, 161, 204, 210);
|
||||
#undef argb
|
||||
return S_OK;
|
||||
}
|
||||
|
||||
virtual BOOL buttonTextShadowed(UINT uItemState) const
|
||||
{
|
||||
return (uItemState & CDIS_DISABLED) == 0;
|
||||
}
|
||||
|
||||
virtual int folderBarMarginsLeftDIP(void) const { return 3; }
|
||||
virtual int folderBarMarginsTopDIP(void) const { return 2; }
|
||||
virtual int folderBarMarginsRightDIP(void) const { return 3; }
|
||||
virtual int folderBarMarginsBottomDIP(void) const { return 3; }
|
||||
|
||||
virtual int buttonMarginsXDIP(void) const { return 6; }
|
||||
virtual int buttonMarginsYDIP(void) const { return 5; }
|
||||
virtual int buttonTextArrowSeparationXDIP(void) const { return 3; }
|
||||
};
|
||||
|
||||
class commandModuleStyleParams7 : public commandModuleStyleParams {
|
||||
virtual int partID_CMOD_MODULEBACKGROUND(void) const { return 1; }
|
||||
virtual int partID_CMOD_TASKBUTTON(void) const { return 3; }
|
||||
virtual int partID_CMOD_SPLITBUTTONLEFT(void) const { return 4; }
|
||||
virtual int partID_CMOD_SPLITBUTTONRIGHT(void) const { return 5; }
|
||||
virtual int partID_CMOD_MENUGLYPH(void) const { return 6; }
|
||||
virtual int partID_CMOD_OVERFLOWGLYPH(void) const { return 7; }
|
||||
|
||||
virtual int stateID_CMODS_NORMAL(void) const { return 1; }
|
||||
virtual int stateID_CMODS_HOT(void) const { return 2; }
|
||||
virtual int stateID_CMODS_PRESSED(void) const { return 3; }
|
||||
virtual int stateID_CMODS_KEYFOCUSED(void) const { return 4; }
|
||||
/*TODO verify this*/virtual int stateID_CMODS_NEARHOT(void) const { return 5; }
|
||||
|
||||
virtual HRESULT backgroundGradientColors(HTHEME theme, COLORREF *colors) const
|
||||
{
|
||||
HRESULT hr;
|
||||
|
||||
if (colors == NULL)
|
||||
return E_POINTER;
|
||||
hr = GetThemeColor(theme,
|
||||
2, 0,
|
||||
TMT_GRADIENTCOLOR1, colors + 0);
|
||||
if (hr != S_OK)
|
||||
return hr;
|
||||
hr = GetThemeColor(theme,
|
||||
2, 0,
|
||||
TMT_GRADIENTCOLOR2, colors + 1);
|
||||
if (hr != S_OK)
|
||||
return hr;
|
||||
return GetThemeColor(theme,
|
||||
2, 0,
|
||||
TMT_GRADIENTCOLOR3, colors + 2);
|
||||
}
|
||||
|
||||
virtual HRESULT buttonTextColor(HTHEME theme, UINT uItemState, COLORREF *color) const
|
||||
{
|
||||
int state;
|
||||
|
||||
if (color == NULL)
|
||||
return E_POINTER;
|
||||
state = 1;
|
||||
if ((uItemState & CDIS_DISABLED) != 0)
|
||||
state = 6;
|
||||
// TODO check if 3 is the correct part ID for all button types
|
||||
return GetThemeColor(theme,
|
||||
3, state,
|
||||
TMT_TEXTCOLOR, color);
|
||||
}
|
||||
|
||||
virtual BOOL buttonTextShadowed(UINT uItemState) const
|
||||
{
|
||||
return FALSE;
|
||||
}
|
||||
|
||||
virtual int folderBarMarginsLeftDIP(void) const { return 3; }
|
||||
virtual int folderBarMarginsTopDIP(void) const { return 2; }
|
||||
virtual int folderBarMarginsRightDIP(void) const { return 9; }
|
||||
virtual int folderBarMarginsBottomDIP(void) const { return 3; }
|
||||
|
||||
virtual int buttonMarginsXDIP(void) const { return 13; }
|
||||
virtual int buttonMarginsYDIP(void) const { return 5; }
|
||||
virtual int buttonTextArrowSeparationXDIP(void) const { return 1; }
|
||||
};
|
||||
|
||||
// all coordinates are in client space
|
||||
struct buttonMetrics {
|
||||
SIZE fittingSize;
|
||||
int baseX;
|
||||
int baseY;
|
||||
int dpiX;
|
||||
int dpiY;
|
||||
BOOL hasText;
|
||||
SIZE textSize;
|
||||
BOOL hasArrow;
|
||||
SIZE arrowSize;
|
||||
};
|
||||
|
||||
struct buttonRects {
|
||||
RECT clientRect;
|
||||
RECT textRect;
|
||||
RECT arrowRect;
|
||||
};
|
||||
|
||||
class commandModuleStyle {
|
||||
public:
|
||||
virtual HRESULT drawFolderBar(commandModuleStyleParams *p, HDC dc, RECT *rcWindow, RECT *rcPaint) const = 0;
|
||||
virtual HRESULT buttonMetrics(commandModuleStyleParams *p, HWND button, HDC dc, struct buttonMetrics *m) const = 0;
|
||||
virtual HRESULT buttonRects(commandModuleStyleParams *p, HWND button, struct buttonMetrics *m, struct buttonRects *r) const = 0;
|
||||
virtual HRESULT drawButton(commandModuleStyleParams *p, HWND button, HDC dc, UINT uItemState, RECT *rcPaint) const = 0;
|
||||
};
|
||||
|
||||
class commandModuleStyleThemed : public commandModuleStyle {
|
||||
HTHEME theme;
|
||||
HTHEME textstyleTheme;
|
||||
public:
|
||||
commandModuleStyleThemed(HTHEME theme, HTHEME textstyleTheme)
|
||||
{
|
||||
this->theme = theme;
|
||||
this->textstyleTheme = textstyleTheme;
|
||||
}
|
||||
|
||||
virtual HRESULT drawFolderBar(commandModuleStyleParams *p, HDC dc, RECT *rcWindow, RECT *rcPaint) const
|
||||
{
|
||||
COLORREF colors[3];
|
||||
TRIVERTEX vertices[4];
|
||||
static GRADIENT_RECT gr[2] = {
|
||||
{ 0, 1 },
|
||||
{ 2, 3 },
|
||||
};
|
||||
HRESULT hr;
|
||||
|
||||
hr = p->backgroundGradientColors(this->theme, colors);
|
||||
if (hr != S_OK)
|
||||
return hr;
|
||||
|
||||
vertices[0].x = rcWindow->left;
|
||||
vertices[0].y = rcWindow->top;
|
||||
vertices[0].Red = ((COLOR16) GetRValue(colors[0])) << 8;
|
||||
vertices[0].Green = ((COLOR16) GetGValue(colors[0])) << 8;
|
||||
vertices[0].Blue = ((COLOR16) GetBValue(colors[0])) << 8;
|
||||
vertices[0].Alpha = ((COLOR16) LOBYTE(colors[0] >> 24)) << 8;
|
||||
|
||||
vertices[1].x = (rcWindow->right - rcWindow->left) / 2;
|
||||
vertices[1].y = rcWindow->bottom;
|
||||
vertices[1].Red = ((COLOR16) GetRValue(colors[1])) << 8;
|
||||
vertices[1].Green = ((COLOR16) GetGValue(colors[1])) << 8;
|
||||
vertices[1].Blue = ((COLOR16) GetBValue(colors[1])) << 8;
|
||||
vertices[1].Alpha = ((COLOR16) LOBYTE(colors[1] >> 24)) << 8;
|
||||
|
||||
vertices[2] = vertices[1];
|
||||
vertices[2].y = rcWindow->top;
|
||||
|
||||
vertices[3].x = rcWindow->right;
|
||||
vertices[3].y = rcWindow->bottom;
|
||||
vertices[3].Red = ((COLOR16) GetRValue(colors[2])) << 8;
|
||||
vertices[3].Green = ((COLOR16) GetGValue(colors[2])) << 8;
|
||||
vertices[3].Blue = ((COLOR16) GetBValue(colors[2])) << 8;
|
||||
vertices[3].Alpha = ((COLOR16) LOBYTE(colors[2] >> 24)) << 8;
|
||||
|
||||
if (GradientFill(dc, vertices, 4, (PVOID) gr, 2, GRADIENT_FILL_RECT_H) == FALSE)
|
||||
return lastErrorToHRESULT(GetLastError());
|
||||
return DrawThemeBackground(this->theme, dc,
|
||||
p->partID_CMOD_MODULEBACKGROUND(), 0,
|
||||
rcWindow, rcPaint);
|
||||
}
|
||||
|
||||
#define hasNonsplitArrow(button) ((button) == leftButtons[0] || (button) == leftButtons[1] || (button) == leftButtons[2])
|
||||
|
||||
#define dlgUnitsToX(dlg, baseX) MulDiv((dlg), (baseX), 4)
|
||||
#define dlgUnitsToY(dlg, baseY) MulDiv((dlg), (baseY), 8)
|
||||
// TODO verify the parameter order
|
||||
#define dipsToX(dip, dpiX) MulDiv((dip), (dpiX), 96)
|
||||
#define dipsToY(dip, dpiY) MulDiv((dip), (dpiY), 96)
|
||||
|
||||
// TODO for win7: the sizes are correct (according to UI Automation) but they don't visually match?
|
||||
virtual HRESULT buttonMetrics(commandModuleStyleParams *p, HWND button, HDC dc, struct buttonMetrics *m) const
|
||||
{
|
||||
BOOL releaseDC;
|
||||
TEXTMETRICW tm;
|
||||
RECT r;
|
||||
int minStdButtonHeight;
|
||||
HRESULT hr;
|
||||
|
||||
if (m == NULL)
|
||||
return E_POINTER;
|
||||
|
||||
releaseDC = FALSE;
|
||||
if (dc == NULL) {
|
||||
dc = GetDC(button);
|
||||
if (dc == NULL)
|
||||
return lastErrorToHRESULT(GetLastError());
|
||||
releaseDC = TRUE;
|
||||
}
|
||||
|
||||
ZeroMemory(&tm, sizeof (TEXTMETRICW));
|
||||
hr = GetThemeTextMetrics(this->textstyleTheme, dc,
|
||||
TEXT_BODYTEXT, 0,
|
||||
&tm);
|
||||
if (hr != S_OK)
|
||||
goto fail;
|
||||
hr = GetThemeTextExtent(this->textstyleTheme, dc,
|
||||
TEXT_BODYTEXT, 0,
|
||||
L"ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz", 52, 0,
|
||||
NULL, &r);
|
||||
if (hr != S_OK)
|
||||
goto fail;
|
||||
m->baseX = (int) (((r.right - r.left) / 26 + 1) / 2);
|
||||
m->baseY = (int) tm.tmHeight;
|
||||
m->dpiX = GetDeviceCaps(dc, LOGPIXELSX);
|
||||
m->dpiY = GetDeviceCaps(dc, LOGPIXELSY);
|
||||
|
||||
m->fittingSize.cx = 0;
|
||||
m->fittingSize.cy = 0;
|
||||
|
||||
m->hasText = TRUE;
|
||||
if (m->hasText) {
|
||||
LRESULT n;
|
||||
WCHAR *buf;
|
||||
LOGFONTW lf;
|
||||
|
||||
n = SendMessageW(button, WM_GETTEXTLENGTH, 0, 0);
|
||||
buf = new WCHAR[n + 1];
|
||||
GetWindowTextW(button, buf, n + 1);
|
||||
hr = GetThemeTextExtent(this->textstyleTheme, dc,
|
||||
TEXT_BODYTEXT, 0,
|
||||
buf, n, DT_CENTER,
|
||||
NULL, &r);
|
||||
delete[] buf;
|
||||
if (hr != S_OK)
|
||||
goto fail;
|
||||
m->textSize.cx = r.right - r.left;
|
||||
m->textSize.cy = r.bottom - r.top;
|
||||
m->fittingSize.cx += m->textSize.cx;
|
||||
m->fittingSize.cy += m->textSize.cy;
|
||||
|
||||
// dui70.dll adds this to the width when "overhang" is enabled, and it seems to be enabled for our cases, but I can't tell what conditions it's enabled for...
|
||||
// and yes, it seems to be using the raw lfHeight value here :/
|
||||
// TODO find the right TMT constant
|
||||
hr = GetThemeFont(this->textstyleTheme, dc,
|
||||
4, 0,
|
||||
TMT_FONT, &lf);
|
||||
if (hr != S_OK)
|
||||
goto fail;
|
||||
m->fittingSize.cx += 2 * (abs(lf.lfHeight) / 6);
|
||||
}
|
||||
|
||||
m->hasArrow = hasNonsplitArrow(button);
|
||||
if (m->hasArrow) {
|
||||
// TS_MIN returns 1x1 and TS_DRAW returns 0x0, so...
|
||||
hr = GetThemePartSize(theme, dc,
|
||||
p->partID_CMOD_MENUGLYPH(), 0,
|
||||
NULL, TS_TRUE, &(m->arrowSize));
|
||||
if (hr != S_OK)
|
||||
goto fail;
|
||||
m->fittingSize.cx += m->arrowSize.cx;
|
||||
// TODO I don't think dui70.dll takes this into consideration...
|
||||
if (m->fittingSize.cy < m->arrowSize.cy)
|
||||
m->fittingSize.cy = m->arrowSize.cy;
|
||||
m->fittingSize.cx += dipsToX(p->buttonTextArrowSeparationXDIP(), m->dpiX);
|
||||
}
|
||||
|
||||
m->fittingSize.cx += dipsToX(p->buttonMarginsXDIP(), m->dpiX) * 2;
|
||||
m->fittingSize.cy += dipsToY(p->buttonMarginsYDIP(), m->dpiY) * 2;
|
||||
|
||||
// and dui70.dll seems to do a variant of this but for text buttons only...
|
||||
minStdButtonHeight = dlgUnitsToY(14, m->baseY);
|
||||
if (m->fittingSize.cy < minStdButtonHeight)
|
||||
m->fittingSize.cy = minStdButtonHeight;
|
||||
|
||||
hr = S_OK;
|
||||
fail:
|
||||
if (releaseDC)
|
||||
// TODO when migrating this to libui, this will need to be renamed to indicate we are intentionally ignoring errors
|
||||
ReleaseDC(button, dc);
|
||||
return hr;
|
||||
};
|
||||
|
||||
// TODO check errors
|
||||
virtual HRESULT buttonRects(commandModuleStyleParams *p, HWND button, struct buttonMetrics *m, struct buttonRects *r) const
|
||||
{
|
||||
if (r == NULL)
|
||||
return E_POINTER;
|
||||
|
||||
GetClientRect(button, &(r->clientRect));
|
||||
|
||||
if (m->hasText)
|
||||
r->textRect = r->clientRect;
|
||||
|
||||
if (m->hasArrow) {
|
||||
r->arrowRect = r->clientRect;
|
||||
r->arrowRect.left = r->arrowRect.right;
|
||||
r->arrowRect.left -= dipsToX(p->buttonMarginsXDIP(), m->dpiX);
|
||||
r->arrowRect.right = r->arrowRect.left;
|
||||
r->arrowRect.left -= m->arrowSize.cx;
|
||||
r->arrowRect.top += ((r->arrowRect.bottom - r->arrowRect.top) - m->arrowSize.cy) / 2;
|
||||
r->arrowRect.bottom = r->arrowRect.top + m->arrowSize.cy;
|
||||
|
||||
if (m->hasText) {
|
||||
r->textRect.right = r->arrowRect.left - dipsToX(p->buttonTextArrowSeparationXDIP(), m->dpiX);
|
||||
r->textRect.right += dipsToX(p->buttonMarginsXDIP(), m->dpiX);
|
||||
}
|
||||
}
|
||||
|
||||
return S_OK;
|
||||
}
|
||||
|
||||
// TODO check errors fully
|
||||
virtual HRESULT drawButton(commandModuleStyleParams *p, HWND button, HDC dc, UINT uItemState, RECT *rcPaint) const
|
||||
{
|
||||
struct buttonMetrics m;
|
||||
struct buttonRects r;
|
||||
int part, state;
|
||||
HRESULT hr;
|
||||
|
||||
hr = this->buttonMetrics(p, button, dc, &m);
|
||||
if (hr != S_OK)
|
||||
return hr;
|
||||
hr = this->buttonRects(p, button, &m, &r);
|
||||
if (hr != S_OK)
|
||||
return hr;
|
||||
|
||||
part = p->partID_CMOD_TASKBUTTON();
|
||||
state = p->stateID_CMODS_NORMAL();
|
||||
if ((uItemState & CDIS_FOCUS) != 0)
|
||||
state = p->stateID_CMODS_KEYFOCUSED();
|
||||
if ((uItemState & CDIS_HOT) != 0)
|
||||
state = p->stateID_CMODS_HOT();
|
||||
if ((uItemState & CDIS_SELECTED) != 0)
|
||||
state = p->stateID_CMODS_PRESSED();
|
||||
hr = DrawThemeParentBackground(button, dc, rcPaint);
|
||||
if (hr != S_OK)
|
||||
return hr;
|
||||
hr = DrawThemeBackground(this->theme, dc,
|
||||
part, state,
|
||||
&(r.clientRect), rcPaint);
|
||||
if (hr != S_OK)
|
||||
return hr;
|
||||
|
||||
if (m.hasText) {
|
||||
int textState;
|
||||
COLORREF textColor;
|
||||
LRESULT n;
|
||||
WCHAR *buf;
|
||||
DTTOPTS dttopts;
|
||||
|
||||
hr = p->buttonTextColor(this->theme, uItemState, &textColor);
|
||||
if (hr != S_OK)
|
||||
return hr;
|
||||
n = SendMessageW(button, WM_GETTEXTLENGTH, 0, 0);
|
||||
buf = new WCHAR[n + 1];
|
||||
GetWindowTextW(button, buf, n + 1);
|
||||
ZeroMemory(&dttopts, sizeof (DTTOPTS));
|
||||
dttopts.dwSize = sizeof (DTTOPTS);
|
||||
dttopts.dwFlags = DTT_TEXTCOLOR;
|
||||
dttopts.crText = textColor;
|
||||
hr = DrawThemeTextEx(this->textstyleTheme, dc,
|
||||
TEXT_BODYTEXT, 0,
|
||||
buf, n, DT_CENTER | DT_VCENTER | DT_SINGLELINE,
|
||||
&(r.textRect), &dttopts);
|
||||
delete[] buf;
|
||||
if (hr != S_OK)
|
||||
return hr;
|
||||
}
|
||||
|
||||
if (m.hasArrow) {
|
||||
// TODO verify the state ID on all platforms
|
||||
hr = DrawThemeBackground(this->theme, dc,
|
||||
p->partID_CMOD_MENUGLYPH(), 0,
|
||||
&(r.arrowRect), rcPaint);
|
||||
if (hr != S_OK)
|
||||
return hr;
|
||||
}
|
||||
|
||||
return S_OK;
|
||||
}
|
||||
};
|
||||
|
||||
#if 0
|
||||
// TODO check errors
|
||||
void drawExplorerChevron(HTHEME theme, HDC dc, HWND rebar, WPARAM band, RECT *rcPaint)
|
||||
{
|
||||
REBARBANDINFOW rbi;
|
||||
RECT r;
|
||||
int state;
|
||||
|
||||
ZeroMemory(&rbi, sizeof (REBARBANDINFOW));
|
||||
rbi.cbSize = sizeof (REBARBANDINFOW);
|
||||
rbi.fMask = RBBIM_CHILD | RBBIM_CHEVRONLOCATION | RBBIM_CHEVRONSTATE;
|
||||
SendMessageW(rebar, RB_GETBANDINFOW, band, (LPARAM) (&rbi));
|
||||
if ((rbi.uChevronState & STATE_SYSTEM_INVISIBLE) != 0)
|
||||
return;
|
||||
state = 1;
|
||||
// TODO check if this is correct
|
||||
if ((rbi.uChevronState & STATE_SYSTEM_FOCUSED) != 0)
|
||||
state = 4;
|
||||
if ((rbi.uChevronState & STATE_SYSTEM_HOTTRACKED) != 0)
|
||||
state = 2;
|
||||
if ((rbi.uChevronState & STATE_SYSTEM_PRESSED) != 0)
|
||||
state = 3;
|
||||
r = rbi.rcChevronLocation;
|
||||
// TODO commctrl.h says this should be correct for the chevron rect, but it's not?
|
||||
// MapWindowRect(rbi.hwndChild, rebar, &r);
|
||||
DrawThemeBackground(theme, dc,
|
||||
3, state,
|
||||
&r, rcPaint);
|
||||
DrawThemeBackground(theme, dc,
|
||||
7, 1,
|
||||
&r, rcPaint);
|
||||
}
|
||||
#endif
|
||||
|
||||
HICON shieldIcon;
|
||||
HICON applicationIcon;
|
||||
HICON helpIcon;
|
||||
HIMAGELIST rightList;
|
||||
|
||||
HTHEME theme = NULL;
|
||||
HTHEME textstyleTheme = NULL;
|
||||
const char *which = "7";
|
||||
commandModuleStyle *cms = NULL;
|
||||
commandModuleStyleParams *cmsp = NULL;
|
||||
HIMAGELIST dropdownArrowList = NULL;
|
||||
|
||||
static struct {
|
||||
const WCHAR *text;
|
||||
BOOL dropdown;
|
||||
} leftbarButtons[] = {
|
||||
{ L"Organize", TRUE },
|
||||
{ L"Include in library", TRUE },
|
||||
{ L"Share with", TRUE },
|
||||
{ L"Burn", FALSE },
|
||||
{ L"New folder", FALSE },
|
||||
};
|
||||
|
||||
// TODO check errors
|
||||
LRESULT drawExplorerButton(NMCUSTOMDRAW *nm)
|
||||
{
|
||||
HRESULT hr;
|
||||
|
||||
if (nm->dwDrawStage != CDDS_PREPAINT)
|
||||
return CDRF_DODEFAULT;
|
||||
hr = cms->drawButton(cmsp, nm->hdr.hwndFrom,
|
||||
nm->hdc, nm->uItemState, &(nm->rc));
|
||||
if (hr != S_OK)
|
||||
return CDRF_DODEFAULT;
|
||||
return CDRF_SKIPDEFAULT;
|
||||
}
|
||||
|
||||
void onWM_CREATE(HWND hwnd)
|
||||
{
|
||||
int buttonx, buttony;
|
||||
int i;
|
||||
|
||||
buttonx = 5;
|
||||
buttony = 5;
|
||||
for (i = 0; i < 5; i++) {
|
||||
// TODO split buttons don't support arrow navigation?
|
||||
leftButtons[i] = CreateWindowExW(0,
|
||||
L"BUTTON", leftbarButtons[i].text,
|
||||
WS_CHILD | WS_VISIBLE | WS_TABSTOP | BS_PUSHBUTTON,
|
||||
buttonx, buttony,
|
||||
150, 30,
|
||||
hwnd, (HMENU) (100 + i), hInstance, NULL);
|
||||
if (leftButtons[i] == NULL)
|
||||
diele("CreateWindowExW(L\"BUTTON\")");
|
||||
buttonx += 150;
|
||||
}
|
||||
}
|
||||
|
||||
// TODO check errors
|
||||
void updateTheme(HWND hwnd)
|
||||
{
|
||||
if (cmsp != NULL) {
|
||||
delete cmsp;
|
||||
cmsp = NULL;
|
||||
}
|
||||
if (cms != NULL) {
|
||||
delete cms;
|
||||
cms = NULL;
|
||||
}
|
||||
if (textstyleTheme != NULL) {
|
||||
CloseThemeData(textstyleTheme);
|
||||
textstyleTheme = NULL;
|
||||
}
|
||||
if (theme != NULL) {
|
||||
CloseThemeData(theme);
|
||||
theme = NULL;
|
||||
}
|
||||
|
||||
theme = OpenThemeData(hwnd, L"CommandModule");
|
||||
textstyleTheme = OpenThemeData(hwnd, L"TEXTSTYLE");
|
||||
cms = new commandModuleStyleThemed(theme, textstyleTheme);
|
||||
if (strcmp(which, "vista") == 0)
|
||||
cmsp = new commandModuleStyleParamsVista;
|
||||
else
|
||||
cmsp = new commandModuleStyleParams7;
|
||||
}
|
||||
|
||||
void repositionButtons(HWND hwnd)
|
||||
{
|
||||
HDWP dwp;
|
||||
int buttonx, buttony;
|
||||
HDC dc;
|
||||
int dpiX;
|
||||
int dpiY;
|
||||
struct buttonMetrics m;
|
||||
int i;
|
||||
|
||||
dc = GetDC(hwnd);
|
||||
if (dc == NULL)
|
||||
diele("GetDC()");
|
||||
dpiX = GetDeviceCaps(dc, LOGPIXELSX);
|
||||
dpiY = GetDeviceCaps(dc, LOGPIXELSY);
|
||||
// TODO check error
|
||||
ReleaseDC(hwnd, dc);
|
||||
|
||||
dwp = BeginDeferWindowPos(5);
|
||||
if (dwp == NULL)
|
||||
diele("BeginDeferWindowPos()");
|
||||
buttonx = dipsToX(cmsp->folderBarMarginsLeftDIP(), dpiX);
|
||||
buttony = dipsToY(cmsp->folderBarMarginsTopDIP(), dpiY);
|
||||
for (i = 0; i < 5; i++) {
|
||||
cms->buttonMetrics(cmsp, leftButtons[i], NULL, &m);
|
||||
dwp = DeferWindowPos(dwp, leftButtons[i], NULL,
|
||||
buttonx, buttony, m.fittingSize.cx, m.fittingSize.cy,
|
||||
SWP_NOACTIVATE | SWP_NOOWNERZORDER | SWP_NOZORDER);
|
||||
if (dwp == NULL)
|
||||
diele("DeferWindowPos()");
|
||||
buttonx += m.fittingSize.cx;
|
||||
}
|
||||
if (EndDeferWindowPos(dwp) == 0)
|
||||
diele("EndDeferWindowPos()");
|
||||
}
|
||||
|
||||
// TODO check errors
|
||||
void folderBarRect(HWND hwnd, HDC dc, RECT *r)
|
||||
{
|
||||
int dpiX;
|
||||
int dpiY;
|
||||
RECT child;
|
||||
int i;
|
||||
|
||||
dpiX = GetDeviceCaps(dc, LOGPIXELSX);
|
||||
dpiY = GetDeviceCaps(dc, LOGPIXELSY);
|
||||
|
||||
GetClientRect(hwnd, r);
|
||||
r->right -= r->left;
|
||||
r->left = 0;
|
||||
r->top = 0;
|
||||
r->bottom = 0;
|
||||
|
||||
for (i = 0; i < 5; i++) {
|
||||
GetWindowRect(leftButtons[i], &child);
|
||||
if (r->bottom < (child.bottom - child.top))
|
||||
r->bottom = (child.bottom - child.top);
|
||||
}
|
||||
|
||||
r->bottom += dipsToY(cmsp->folderBarMarginsTopDIP(), dpiY);
|
||||
r->bottom += dipsToY(cmsp->folderBarMarginsBottomDIP(), dpiY);
|
||||
}
|
||||
|
||||
#if 0
|
||||
// TODO check errors
|
||||
void handleEvents(HWND hwnd, WPARAM wParam)
|
||||
{
|
||||
LRESULT n;
|
||||
const WCHAR *selRebar = NULL, *selToolbar = NULL;
|
||||
WCHAR *bufRebar = NULL, *bufToolbar = NULL;
|
||||
BOOL changeRebar = FALSE, changeToolbar = FALSE;
|
||||
BOOL invalidate = FALSE;
|
||||
WPARAM check;
|
||||
|
||||
switch (wParam) {
|
||||
case MAKEWPARAM(300, CBN_SELCHANGE):
|
||||
n = SendMessageW(rebarCombo, CB_GETCURSEL, 0, 0);
|
||||
selRebar = rebarThemes[n];
|
||||
changeRebar = TRUE;
|
||||
break;
|
||||
case MAKEWPARAM(301, CBN_SELCHANGE):
|
||||
n = SendMessageW(toolbarCombo, CB_GETCURSEL, 0, 0);
|
||||
selToolbar = toolbarThemes[n];
|
||||
changeToolbar = TRUE;
|
||||
break;
|
||||
case MAKEWPARAM(200, BN_CLICKED):
|
||||
drawmode = 0;
|
||||
n = SendMessageW(rebarCombo, WM_GETTEXTLENGTH, 0, 0);
|
||||
bufRebar = new WCHAR[n + 1];
|
||||
GetWindowTextW(rebarCombo, bufRebar, n + 1);
|
||||
n = SendMessageW(toolbarCombo, WM_GETTEXTLENGTH, 0, 0);
|
||||
bufToolbar = new WCHAR[n + 1];
|
||||
GetWindowTextW(toolbarCombo, bufToolbar, n + 1);
|
||||
selRebar = bufRebar;
|
||||
selToolbar = bufToolbar;
|
||||
changeRebar = TRUE;
|
||||
changeToolbar = TRUE;
|
||||
break;
|
||||
case MAKEWPARAM(201, BN_CLICKED):
|
||||
drawmode = 1;
|
||||
invalidate = TRUE;
|
||||
break;
|
||||
case MAKEWPARAM(302, BN_CLICKED):
|
||||
ShowWindow(leftbar, SW_HIDE);
|
||||
check = BST_CHECKED;
|
||||
if (SendMessage(toolbarTransparentCheckbox, BM_GETCHECK, 0, 0) == BST_CHECKED)
|
||||
check = BST_UNCHECKED;
|
||||
SendMessage(toolbarTransparentCheckbox, BM_SETCHECK, check, 0);
|
||||
if (check == BST_CHECKED)
|
||||
SendMessageW(leftbar, TB_SETSTYLE, 0, toolbarStyles | TBSTYLE_LIST | TBSTYLE_TRANSPARENT);
|
||||
else
|
||||
SendMessageW(leftbar, TB_SETSTYLE, 0, toolbarStyles | TBSTYLE_LIST);
|
||||
ShowWindow(leftbar, SW_SHOW);
|
||||
break;
|
||||
case MAKEWPARAM(202, BN_CLICKED):
|
||||
SetFocus(leftbar);
|
||||
break;
|
||||
}
|
||||
if (changeRebar) {
|
||||
if (selRebar != NULL && wcscmp(selRebar, L"NULL") == 0)
|
||||
selRebar = NULL;
|
||||
if (selRebar != NULL && *selRebar != L'\0')
|
||||
SendMessageW(rebar, RB_SETWINDOWTHEME, 0, (LPARAM) selRebar);
|
||||
else
|
||||
SetWindowTheme(rebar, selRebar, selRebar);
|
||||
invalidate = TRUE;
|
||||
}
|
||||
if (changeToolbar) {
|
||||
if (selToolbar != NULL && wcscmp(selToolbar, L"NULL") == 0)
|
||||
selToolbar = NULL;
|
||||
if (selToolbar != NULL && *selToolbar != L'\0') {
|
||||
SendMessageW(leftbar, TB_SETWINDOWTHEME, 0, (LPARAM) selToolbar);
|
||||
SendMessageW(rightbar, TB_SETWINDOWTHEME, 0, (LPARAM) selToolbar);
|
||||
} else {
|
||||
SetWindowTheme(leftbar, selToolbar, selToolbar);
|
||||
SetWindowTheme(rightbar, selToolbar, selToolbar);
|
||||
}
|
||||
invalidate = TRUE;
|
||||
}
|
||||
if (invalidate)
|
||||
InvalidateRect(hwnd, NULL, TRUE);
|
||||
if (bufRebar != NULL)
|
||||
delete[] bufRebar;
|
||||
if (bufToolbar != NULL)
|
||||
delete[] bufToolbar;
|
||||
}
|
||||
#endif
|
||||
|
||||
LRESULT CALLBACK wndproc(HWND hwnd, UINT uMsg, WPARAM wParam, LPARAM lParam)
|
||||
{
|
||||
HDC dc;
|
||||
PAINTSTRUCT ps;
|
||||
NMHDR *nm = (NMHDR *) lParam;
|
||||
int i;
|
||||
|
||||
switch (uMsg) {
|
||||
case WM_CREATE:
|
||||
onWM_CREATE(hwnd);
|
||||
updateTheme(hwnd);
|
||||
repositionButtons(hwnd);
|
||||
break;
|
||||
case WM_CLOSE:
|
||||
PostQuitMessage(0);
|
||||
break;
|
||||
case WM_SIZE:
|
||||
repositionButtons(hwnd);
|
||||
// TODO check errors
|
||||
InvalidateRect(hwnd, NULL, TRUE);
|
||||
break;
|
||||
case WM_THEMECHANGED:
|
||||
updateTheme(hwnd);
|
||||
repositionButtons(hwnd);
|
||||
break;
|
||||
case WM_PAINT:
|
||||
// TODO check errors
|
||||
dc = BeginPaint(hwnd, &ps);
|
||||
{RECT w;
|
||||
folderBarRect(hwnd, dc, &w);
|
||||
cms->drawFolderBar(cmsp, dc, &w, &(ps.rcPaint));}
|
||||
EndPaint(hwnd, &ps);
|
||||
return 0;
|
||||
case WM_PRINTCLIENT:
|
||||
{RECT w, paint;
|
||||
folderBarRect(hwnd, (HDC) wParam, &w);
|
||||
GetClientRect(hwnd,&paint);
|
||||
cms->drawFolderBar(cmsp, (HDC) wParam, &w, &w);}
|
||||
return 0;
|
||||
#if 0
|
||||
case WM_COMMAND:
|
||||
handleEvents(hwnd, wParam);
|
||||
break;
|
||||
#endif
|
||||
case WM_NOTIFY:
|
||||
switch (nm->code) {
|
||||
case NM_CUSTOMDRAW:
|
||||
for (i = 0; i < 5; i++)
|
||||
if (nm->hwndFrom == leftButtons[i])
|
||||
return drawExplorerButton((NMCUSTOMDRAW *) nm);
|
||||
break;
|
||||
}
|
||||
break;
|
||||
}
|
||||
return DefWindowProcW(hwnd, uMsg, wParam, lParam);
|
||||
}
|
||||
|
||||
EXTERN_C IMAGE_DOS_HEADER __ImageBase;
|
||||
|
||||
int main(int argc, char *argv[])
|
||||
{
|
||||
STARTUPINFOW si;
|
||||
int nCmdShow;
|
||||
INITCOMMONCONTROLSEX icc;
|
||||
HICON hDefaultIcon;
|
||||
HCURSOR hDefaultCursor;
|
||||
WNDCLASSW wc;
|
||||
HWND mainwin;
|
||||
MSG msg;
|
||||
HRESULT hr;
|
||||
|
||||
if (argc > 1)
|
||||
which = argv[1];
|
||||
|
||||
hInstance = (HINSTANCE) (&__ImageBase);
|
||||
nCmdShow = SW_SHOWDEFAULT;
|
||||
GetStartupInfoW(&si);
|
||||
if ((si.dwFlags & STARTF_USESHOWWINDOW) != 0)
|
||||
nCmdShow = si.wShowWindow;
|
||||
|
||||
ZeroMemory(&icc, sizeof (INITCOMMONCONTROLSEX));
|
||||
icc.dwSize = sizeof (INITCOMMONCONTROLSEX);
|
||||
icc.dwICC = ICC_STANDARD_CLASSES | ICC_BAR_CLASSES | ICC_COOL_CLASSES;
|
||||
if (InitCommonControlsEx(&icc) == 0)
|
||||
diele("InitCommonControlsEx()");
|
||||
|
||||
hDefaultIcon = LoadIconW(NULL, IDI_APPLICATION);
|
||||
if (hDefaultIcon == NULL)
|
||||
diele("LoadIconW(IDI_APPLICATION)");
|
||||
hDefaultCursor = LoadCursorW(NULL, IDC_ARROW);
|
||||
if (hDefaultCursor == NULL)
|
||||
diele("LoadCursorW(IDC_ARROW)");
|
||||
|
||||
hr = LoadIconMetric(NULL, IDI_SHIELD, LIM_SMALL, &shieldIcon);
|
||||
if (hr != S_OK)
|
||||
diehr("LoadIconMetric(IDI_SHIELD)", hr);
|
||||
hr = LoadIconMetric(NULL, IDI_APPLICATION, LIM_SMALL, &applicationIcon);
|
||||
if (hr != S_OK)
|
||||
diehr("LoadIconMetric(IDI_APPLICATION)", hr);
|
||||
hr = LoadIconMetric(NULL, IDI_QUESTION, LIM_SMALL, &helpIcon);
|
||||
if (hr != S_OK)
|
||||
diehr("LoadIconMetric(IDI_QUESTION)", hr);
|
||||
rightList = ImageList_Create(GetSystemMetrics(SM_CXSMICON), GetSystemMetrics(SM_CYSMICON),
|
||||
ILC_COLOR32, 0, 3);
|
||||
if (rightList == NULL)
|
||||
diele("ImageList_Create()");
|
||||
if (ImageList_ReplaceIcon(rightList, -1, shieldIcon) == -1)
|
||||
diele("ImageList_ReplaceIcon(IDI_SHIELD)");
|
||||
if (ImageList_ReplaceIcon(rightList, -1, applicationIcon) == -1)
|
||||
diele("ImageList_ReplaceIcon(IDI_APPLICATION)");
|
||||
if (ImageList_ReplaceIcon(rightList, -1, helpIcon) == -1)
|
||||
diele("ImageList_ReplaceIcon(IDI_QUESTION)");
|
||||
|
||||
ZeroMemory(&wc, sizeof (WNDCLASSW));
|
||||
wc.lpszClassName = L"mainwin";
|
||||
wc.lpfnWndProc = wndproc;
|
||||
wc.hInstance = hInstance;
|
||||
wc.hIcon = hDefaultIcon;
|
||||
wc.hCursor = hDefaultCursor;
|
||||
wc.hbrBackground = (HBRUSH) (COLOR_BTNFACE + 1);
|
||||
if (RegisterClassW(&wc) == 0)
|
||||
diele("RegisterClassW()");
|
||||
|
||||
mainwin = CreateWindowExW(0,
|
||||
L"mainwin", L"Main Window",
|
||||
WS_OVERLAPPEDWINDOW,
|
||||
CW_USEDEFAULT, CW_USEDEFAULT,
|
||||
CW_USEDEFAULT, CW_USEDEFAULT,
|
||||
NULL, NULL, hInstance, NULL);
|
||||
if (mainwin == NULL)
|
||||
diele("CreateWindowExW(L\"mainwin\")");
|
||||
|
||||
ShowWindow(mainwin, nCmdShow);
|
||||
if (UpdateWindow(mainwin) == 0)
|
||||
diele("UpdateWindow()");
|
||||
|
||||
for (;;) {
|
||||
int res;
|
||||
|
||||
res = GetMessageW(&msg, NULL, 0, 0);
|
||||
if (res < 0)
|
||||
diele("GetMessageW()");
|
||||
if (res == 0)
|
||||
break;
|
||||
if (IsDialogMessageW(mainwin, &msg) == 0) {
|
||||
TranslateMessage(&msg);
|
||||
DispatchMessageW(&msg);
|
||||
}
|
||||
}
|
||||
return 0;
|
||||
}
|
|
@ -0,0 +1,676 @@
|
|||
// 9 october 2018
|
||||
#define UNICODE
|
||||
#define _UNICODE
|
||||
#define STRICT
|
||||
#define STRICT_TYPED_ITEMIDS
|
||||
#define WINVER 0x0600 /* from Microsoft's winnls.h */
|
||||
#define _WIN32_WINNT 0x0600
|
||||
#define _WIN32_WINDOWS 0x0600 /* from Microsoft's pdh.h */
|
||||
#define _WIN32_IE 0x0700
|
||||
#define NTDDI_VERSION 0x06000000
|
||||
#include <windows.h>
|
||||
#include <commctrl.h>
|
||||
#include <uxtheme.h>
|
||||
#include <vsstyle.h>
|
||||
#include <vssym32.h>
|
||||
#include <windowsx.h>
|
||||
#include <stdio.h>
|
||||
#include <stdlib.h>
|
||||
|
||||
// cl winrebarexplorertheme.cpp -MT -link user32.lib kernel32.lib gdi32.lib comctl32.lib uxtheme.lib msimg32.lib windows.res
|
||||
|
||||
void diele(const char *func)
|
||||
{
|
||||
DWORD le;
|
||||
|
||||
le = GetLastError();
|
||||
fprintf(stderr, "%s: %I32u\n", func, le);
|
||||
exit(EXIT_FAILURE);
|
||||
}
|
||||
|
||||
void diehr(const char *func, HRESULT hr)
|
||||
{
|
||||
fprintf(stderr, "%s: 0x%08I32X\n", func, hr);
|
||||
exit(EXIT_FAILURE);
|
||||
}
|
||||
|
||||
HINSTANCE hInstance;
|
||||
HWND rebar;
|
||||
HWND leftbar;
|
||||
HWND rightbar;
|
||||
HWND rebarCombo;
|
||||
HWND toolbarCombo;
|
||||
HWND toolbarTransparentCheckbox;
|
||||
|
||||
HICON shieldIcon;
|
||||
HICON applicationIcon;
|
||||
HICON helpIcon;
|
||||
HIMAGELIST rightList;
|
||||
|
||||
#define toolbarStyles (WS_CHILD | CCS_NODIVIDER | CCS_NOPARENTALIGN | CCS_NORESIZE | TBSTYLE_FLAT)
|
||||
|
||||
static struct {
|
||||
const WCHAR *text;
|
||||
BOOL dropdown;
|
||||
} leftbarButtons[] = {
|
||||
{ L"Organize", TRUE },
|
||||
{ L"Include in library", TRUE },
|
||||
{ L"Share with", TRUE },
|
||||
{ L"Burn", FALSE },
|
||||
{ L"New folder", FALSE },
|
||||
};
|
||||
|
||||
// TODO check errors
|
||||
// TODO extract colors from the theme
|
||||
void drawExplorerBackground(HTHEME theme, HDC dc, RECT *rcWindow, RECT *rcPaint)
|
||||
{
|
||||
static TRIVERTEX vertices[] = {
|
||||
{ 0, 0, 4 << 8, 80 << 8, 130 << 8, 255 << 8 },
|
||||
{ 0, 0, 17 << 8, 101 << 8, 132 << 8, 255 << 8 },
|
||||
{ 0, 0, 17 << 8, 101 << 8, 132 << 8, 255 << 8 },
|
||||
{ 0, 0, 29 << 8, 121 << 8, 134 << 8, 255 << 8 },
|
||||
};
|
||||
static GRADIENT_RECT gr[2] = {
|
||||
{ 0, 1 },
|
||||
{ 2, 3 },
|
||||
};
|
||||
|
||||
vertices[0].x = rcPaint->left;
|
||||
vertices[0].y = 0;
|
||||
vertices[1].x = rcPaint->right;
|
||||
vertices[1].y = (rcWindow->bottom - rcWindow->top) / 2;
|
||||
vertices[2].x = rcPaint->left;
|
||||
vertices[2].y = (rcWindow->bottom - rcWindow->top) / 2;
|
||||
vertices[3].x = rcPaint->right;
|
||||
vertices[3].y = rcWindow->bottom - rcWindow->top;
|
||||
GradientFill(dc, vertices, 4, (PVOID) gr, 2, GRADIENT_FILL_RECT_V);
|
||||
DrawThemeBackground(theme, dc,
|
||||
1, 0,
|
||||
rcWindow, rcPaint);
|
||||
}
|
||||
|
||||
// TODO check errors
|
||||
void drawExplorerChevron(HTHEME theme, HDC dc, HWND rebar, WPARAM band, RECT *rcPaint)
|
||||
{
|
||||
REBARBANDINFOW rbi;
|
||||
RECT r;
|
||||
int state;
|
||||
|
||||
ZeroMemory(&rbi, sizeof (REBARBANDINFOW));
|
||||
rbi.cbSize = sizeof (REBARBANDINFOW);
|
||||
rbi.fMask = RBBIM_CHILD | RBBIM_CHEVRONLOCATION | RBBIM_CHEVRONSTATE;
|
||||
SendMessageW(rebar, RB_GETBANDINFOW, band, (LPARAM) (&rbi));
|
||||
if ((rbi.uChevronState & STATE_SYSTEM_INVISIBLE) != 0)
|
||||
return;
|
||||
state = 1;
|
||||
// TODO check if this is correct
|
||||
if ((rbi.uChevronState & STATE_SYSTEM_FOCUSED) != 0)
|
||||
state = 4;
|
||||
if ((rbi.uChevronState & STATE_SYSTEM_HOTTRACKED) != 0)
|
||||
state = 2;
|
||||
if ((rbi.uChevronState & STATE_SYSTEM_PRESSED) != 0)
|
||||
state = 3;
|
||||
r = rbi.rcChevronLocation;
|
||||
// TODO commctrl.h says this should be correct for the chevron rect, but it's not?
|
||||
// MapWindowRect(rbi.hwndChild, rebar, &r);
|
||||
DrawThemeBackground(theme, dc,
|
||||
3, state,
|
||||
&r, rcPaint);
|
||||
DrawThemeBackground(theme, dc,
|
||||
7, 1,
|
||||
&r, rcPaint);
|
||||
}
|
||||
|
||||
// TODO check errors
|
||||
LRESULT customDrawExplorerRebar(NMCUSTOMDRAW *nm)
|
||||
{
|
||||
HTHEME theme;
|
||||
RECT r;
|
||||
|
||||
if (nm->dwDrawStage != CDDS_PREPAINT)
|
||||
return CDRF_DODEFAULT;
|
||||
theme = OpenThemeData(nm->hdr.hwndFrom, L"CommandModule");
|
||||
GetClientRect(nm->hdr.hwndFrom, &r);
|
||||
drawExplorerBackground(theme, nm->hdc, &r, &(nm->rc));
|
||||
// TODO dwItemSpec is often invalid?!
|
||||
drawExplorerChevron(theme, nm->hdc, nm->hdr.hwndFrom, 0, &(nm->rc));
|
||||
CloseThemeData(theme);
|
||||
return CDRF_SKIPDEFAULT;
|
||||
}
|
||||
|
||||
// TODO check errors
|
||||
LRESULT customDrawExplorerToolbar(NMTBCUSTOMDRAW *nm)
|
||||
{
|
||||
HWND toolbar, rebar;
|
||||
WPARAM itemIndex;
|
||||
TBBUTTON tbb;
|
||||
HTHEME theme;
|
||||
RECT r;
|
||||
int part, state;
|
||||
|
||||
toolbar = nm->nmcd.hdr.hwndFrom;
|
||||
switch (nm->nmcd.dwDrawStage) {
|
||||
case CDDS_PREPAINT:
|
||||
theme = OpenThemeData(toolbar, L"CommandModule");
|
||||
rebar = GetParent(toolbar);
|
||||
GetWindowRect(rebar, &r);
|
||||
MapWindowRect(NULL, toolbar, &r);
|
||||
drawExplorerBackground(theme, nm->nmcd.hdc, &r, &(nm->nmcd.rc));
|
||||
CloseThemeData(theme);
|
||||
return CDRF_NOTIFYITEMDRAW;
|
||||
case CDDS_ITEMPREPAINT:
|
||||
itemIndex = (WPARAM) SendMessageW(toolbar, TB_COMMANDTOINDEX, nm->nmcd.dwItemSpec, 0);
|
||||
ZeroMemory(&tbb, sizeof (TBBUTTON));
|
||||
SendMessageW(toolbar, TB_GETBUTTON, itemIndex, (LPARAM) (&tbb));
|
||||
theme = OpenThemeData(toolbar, L"CommandModule");
|
||||
part = 3;
|
||||
if ((tbb.fsStyle & BTNS_DROPDOWN) != 0)
|
||||
part = 4;
|
||||
state = 1;
|
||||
// TODO this doesn't work; both keyboard and mouse are listed as HOT
|
||||
if ((nm->nmcd.uItemState & CDIS_FOCUS) != 0)
|
||||
state = 4;
|
||||
if ((nm->nmcd.uItemState & CDIS_HOT) != 0)
|
||||
state = 2;
|
||||
if ((nm->nmcd.uItemState & CDIS_SELECTED) != 0)
|
||||
state = 3;
|
||||
SendMessageW(toolbar, TB_GETITEMRECT, itemIndex, (LPARAM) (&r));
|
||||
DrawThemeBackground(theme, nm->nmcd.hdc,
|
||||
3, state,
|
||||
&r, &(nm->nmcd.rc));
|
||||
CloseThemeData(theme);
|
||||
return TBCDRF_NOBACKGROUND;
|
||||
}
|
||||
return CDRF_DODEFAULT;
|
||||
}
|
||||
|
||||
static struct {
|
||||
const WCHAR *text;
|
||||
LRESULT (*handleRebar)(NMCUSTOMDRAW *nm);
|
||||
LRESULT (*handleToolbar)(NMTBCUSTOMDRAW *nm);
|
||||
} drawmodes[] = {
|
||||
{ L"SetWindowTheme()", NULL, NULL },
|
||||
{ L"Custom Draw Explorer", customDrawExplorerRebar, customDrawExplorerToolbar },
|
||||
{ NULL, NULL },
|
||||
};
|
||||
|
||||
int drawmode = 0;
|
||||
|
||||
static const WCHAR *rebarThemes[] = {
|
||||
L"NULL",
|
||||
L"",
|
||||
L"AlternateRebar",
|
||||
L"BrowserTabBar",
|
||||
L"Communications",
|
||||
L"Default",
|
||||
L"ExplorerBar",
|
||||
L"Help",
|
||||
L"InactiveNavbar",
|
||||
L"InactiveNavbarComposited",
|
||||
L"ITBarBase",
|
||||
L"MaxInactiveNavbar",
|
||||
L"MaxInactiveNavbarComposited",
|
||||
L"MaxNavbar",
|
||||
L"MaxNavbarComposited",
|
||||
L"Media",
|
||||
L"Navbar",
|
||||
L"NavbarBase",
|
||||
L"NavbarComposited",
|
||||
L"NavbarNonTopmost",
|
||||
L"Rebar",
|
||||
L"RebarStyle",
|
||||
L"TaskBar",
|
||||
L"TaskBarComposited",
|
||||
NULL,
|
||||
};
|
||||
|
||||
static WCHAR *toolbarThemes[] = {
|
||||
L"NULL",
|
||||
L"",
|
||||
L"Alternate",
|
||||
L"BB",
|
||||
L"BBComposited",
|
||||
L"Communications",
|
||||
L"ExplorerMenu",
|
||||
L"Go",
|
||||
L"GoComposited",
|
||||
L"InactiveBB",
|
||||
L"InactiveBBComposited",
|
||||
L"InactiveGo",
|
||||
L"InactiveGoComposited",
|
||||
L"InfoPaneToolbar",
|
||||
L"LVPopup",
|
||||
L"LVPopupBottom",
|
||||
L"MaxBB",
|
||||
L"MaxBBComposited",
|
||||
L"MaxGo",
|
||||
L"MaxGoComposited",
|
||||
L"MaxInactiveBB",
|
||||
L"MaxInactiveBBComposited",
|
||||
L"MaxInactiveGo",
|
||||
L"MaxInactiveGoComposited",
|
||||
L"Media",
|
||||
L"Placesbar",
|
||||
L"SearchButton",
|
||||
L"SearchButtonComposited",
|
||||
L"StartMenu",
|
||||
L"TaskBar",
|
||||
L"TaskBarComposited",
|
||||
L"TaskBarVert",
|
||||
L"TaskBarVertComposited",
|
||||
L"Toolbar",
|
||||
L"ToolbarStyle",
|
||||
L"TrayNotify",
|
||||
L"TrayNotifyComposited",
|
||||
NULL,
|
||||
};
|
||||
|
||||
// TODO toolbarThemes
|
||||
|
||||
void onWM_CREATE(HWND hwnd)
|
||||
{
|
||||
TBBUTTON tbb[5];
|
||||
RECT btnrect;
|
||||
DWORD tbbtnsize;
|
||||
LONG tbsizex, tbsizey;
|
||||
REBARBANDINFOW rbi;
|
||||
HWND button;
|
||||
LONG buttonx, buttony;
|
||||
LONG combox, comboy;
|
||||
int i;
|
||||
|
||||
rebar = CreateWindowExW(0,
|
||||
REBARCLASSNAMEW, NULL,
|
||||
WS_CHILD | WS_VISIBLE | WS_CLIPSIBLINGS | WS_CLIPCHILDREN | CCS_NODIVIDER | CCS_TOP | RBS_FIXEDORDER,
|
||||
0, 0, 0, 0,
|
||||
hwnd, (HMENU) 100, hInstance, NULL);
|
||||
if (rebar == NULL)
|
||||
diele("CreateWindowExW(REBARCLASSNAMEW)");
|
||||
|
||||
leftbar = CreateWindowExW(0,
|
||||
TOOLBARCLASSNAMEW, NULL,
|
||||
toolbarStyles | TBSTYLE_LIST | TBSTYLE_TRANSPARENT,
|
||||
0, 0, 0, 0,
|
||||
hwnd, (HMENU) 101, hInstance, NULL);
|
||||
if (leftbar == NULL)
|
||||
diele("CreateWindowExW(TOOLBARCLASSNAMEW) leftbar");
|
||||
SendMessageW(leftbar, TB_BUTTONSTRUCTSIZE, sizeof (TBBUTTON), 0);
|
||||
// I_IMAGENONE causes the button text to be left-aligned; don't use it
|
||||
// if (SendMessageW(leftbar, TB_SETBITMAPSIZE, 0, 0) == FALSE)
|
||||
// diele("TB_SETBITMAPSIZE");
|
||||
SendMessageW(leftbar, TB_SETEXTENDEDSTYLE, 0, TBSTYLE_EX_DRAWDDARROWS | TBSTYLE_EX_HIDECLIPPEDBUTTONS | TBSTYLE_EX_MIXEDBUTTONS);
|
||||
// TODO this *should* be DIPs...
|
||||
// TODO figure out where the *2 is documented
|
||||
// SendMessageW(leftbar, TB_SETPADDING, 0, MAKELPARAM(6 * 2, 5 * 2));
|
||||
ZeroMemory(tbb, 5 * sizeof (TBBUTTON));
|
||||
for (i = 0; i < 5; i++) {
|
||||
tbb[i].iBitmap = 0;
|
||||
tbb[i].idCommand = i;
|
||||
tbb[i].fsState = TBSTATE_ENABLED;
|
||||
tbb[i].fsStyle = BTNS_AUTOSIZE | BTNS_BUTTON | BTNS_NOPREFIX | BTNS_SHOWTEXT;
|
||||
if (leftbarButtons[i].dropdown)
|
||||
tbb[i].fsStyle |= BTNS_DROPDOWN | BTNS_WHOLEDROPDOWN;
|
||||
tbb[i].iString = (INT_PTR) (leftbarButtons[i].text);
|
||||
}
|
||||
if (SendMessageW(leftbar, TB_ADDBUTTONSW, 5, (LPARAM) tbb) == FALSE)
|
||||
diele("TB_ADDBUTTONSW");
|
||||
|
||||
tbsizex = 0;
|
||||
for (i = 0; i < 5; i++) {
|
||||
if (SendMessageW(leftbar, TB_GETITEMRECT, (WPARAM) i, (LPARAM) (&btnrect)) == FALSE)
|
||||
diele("TB_GETITEMRECT");
|
||||
tbsizex += btnrect.right - btnrect.left;
|
||||
}
|
||||
tbbtnsize = (DWORD) SendMessageW(leftbar, TB_GETBUTTONSIZE, 0, 0);
|
||||
tbsizey = HIWORD(tbbtnsize);
|
||||
|
||||
ZeroMemory(&rbi, sizeof (REBARBANDINFOW));
|
||||
rbi.cbSize = sizeof (REBARBANDINFOW);
|
||||
rbi.fMask = RBBIM_CHILD | RBBIM_STYLE | RBBIM_SIZE | RBBIM_CHILDSIZE | RBBIM_IDEALSIZE | RBBIM_ID;
|
||||
rbi.fStyle = RBBS_NOGRIPPER | RBBS_CHILDEDGE | RBBS_USECHEVRON | RBBS_HIDETITLE;
|
||||
rbi.hwndChild = leftbar;
|
||||
rbi.cx = tbsizex;
|
||||
rbi.cyChild = tbsizey;
|
||||
rbi.cxMinChild = 0;
|
||||
rbi.cyMinChild = tbsizey;
|
||||
rbi.cxIdeal = tbsizex;
|
||||
rbi.wID = 0;
|
||||
if (SendMessageW(rebar, RB_INSERTBANDW, (WPARAM) (-1), (LPARAM) (&rbi)) == 0)
|
||||
diele("RB_INSERTBANDW leftbar");
|
||||
|
||||
rightbar = CreateWindowExW(0,
|
||||
TOOLBARCLASSNAMEW, NULL,
|
||||
toolbarStyles | TBSTYLE_TRANSPARENT,
|
||||
0, 0, 0, 0,
|
||||
hwnd, (HMENU) 102, hInstance, NULL);
|
||||
if (rightbar == NULL)
|
||||
diele("CreateWindowExW(TOOLBARCLASSNAMEW) rightbar");
|
||||
SendMessageW(rightbar, TB_BUTTONSTRUCTSIZE, sizeof (TBBUTTON), 0);
|
||||
SendMessageW(rightbar, TB_SETIMAGELIST, 0, (LPARAM) rightList);
|
||||
SendMessageW(rightbar, TB_SETEXTENDEDSTYLE, 0, TBSTYLE_EX_DRAWDDARROWS);
|
||||
// TODO this *should* be DIPs...
|
||||
// TODO figure out where the *2 is documented
|
||||
// SendMessageW(rightbar, TB_SETPADDING, 0, MAKELPARAM(6 * 2, 5 * 2));
|
||||
ZeroMemory(tbb, 5 * sizeof (TBBUTTON));
|
||||
tbb[0].iBitmap = 0;
|
||||
tbb[0].idCommand = 0;
|
||||
tbb[0].fsState = TBSTATE_ENABLED;
|
||||
tbb[0].fsStyle = BTNS_AUTOSIZE | BTNS_BUTTON | BTNS_DROPDOWN;
|
||||
tbb[1].iBitmap = 1;
|
||||
tbb[1].idCommand = 1;
|
||||
tbb[1].fsState = TBSTATE_ENABLED;
|
||||
tbb[1].fsStyle = BTNS_AUTOSIZE | BTNS_BUTTON;
|
||||
tbb[2].iBitmap = 2;
|
||||
tbb[2].idCommand = 2;
|
||||
tbb[2].fsState = TBSTATE_ENABLED;
|
||||
tbb[2].fsStyle = BTNS_AUTOSIZE | BTNS_BUTTON;
|
||||
if (SendMessageW(rightbar, TB_ADDBUTTONSW, 3, (LPARAM) tbb) == FALSE)
|
||||
diele("TB_ADDBUTTONSW");
|
||||
// TODO check error
|
||||
// TODO figure out why this works here but not elsewhere
|
||||
// SendMessageW(rightbar, TB_SETBUTTONSIZE, 0, 0);
|
||||
|
||||
tbsizex = 0;
|
||||
for (i = 0; i < 3; i++) {
|
||||
if (SendMessageW(rightbar, TB_GETITEMRECT, (WPARAM) i, (LPARAM) (&btnrect)) == FALSE)
|
||||
diele("TB_GETITEMRECT");
|
||||
tbsizex += btnrect.right - btnrect.left;
|
||||
}
|
||||
tbbtnsize = (DWORD) SendMessageW(rightbar, TB_GETBUTTONSIZE, 0, 0);
|
||||
tbsizey = HIWORD(tbbtnsize);
|
||||
|
||||
ZeroMemory(&rbi, sizeof (REBARBANDINFOW));
|
||||
rbi.cbSize = sizeof (REBARBANDINFOW);
|
||||
rbi.fMask = RBBIM_CHILD | RBBIM_STYLE | RBBIM_SIZE | RBBIM_CHILDSIZE | RBBIM_ID;
|
||||
rbi.fStyle = RBBS_NOGRIPPER | RBBS_HIDETITLE;
|
||||
rbi.hwndChild = rightbar;
|
||||
rbi.cx = tbsizex;
|
||||
rbi.cyChild = tbsizey;
|
||||
rbi.cxMinChild = tbsizex;
|
||||
rbi.cyMinChild = tbsizey;
|
||||
rbi.wID = 1;
|
||||
if (SendMessageW(rebar, RB_INSERTBANDW, (WPARAM) (-1), (LPARAM) (&rbi)) == 0)
|
||||
diele("RB_INSERTBANDW rightbar");
|
||||
|
||||
buttonx = 10;
|
||||
buttony = 40;
|
||||
#define buttonwid 200
|
||||
#define buttonht 25
|
||||
for (i = 0; drawmodes[i].text != NULL; i++) {
|
||||
button = CreateWindowExW(0,
|
||||
L"BUTTON", drawmodes[i].text,
|
||||
WS_CHILD | WS_VISIBLE | BS_PUSHBUTTON,
|
||||
buttonx, buttony,
|
||||
buttonwid, buttonht,
|
||||
hwnd, (HMENU) (200 + i), hInstance, NULL);
|
||||
if (button == NULL)
|
||||
diele("CreateWIndowExW(L\"BUTTON\")");
|
||||
if (i == 0) {
|
||||
combox = buttonx + buttonwid + 5;
|
||||
comboy = buttony;
|
||||
}
|
||||
buttony += buttonht + 5;
|
||||
}
|
||||
button = CreateWindowExW(0,
|
||||
L"BUTTON", L"Give Toolbar Focus",
|
||||
WS_CHILD | WS_VISIBLE | BS_PUSHBUTTON,
|
||||
buttonx, buttony,
|
||||
buttonwid, buttonht,
|
||||
hwnd, (HMENU) (200 + i), hInstance, NULL);
|
||||
if (button == NULL)
|
||||
diele("CreateWIndowExW(L\"BUTTON\")");
|
||||
rebarCombo = CreateWindowExW(WS_EX_CLIENTEDGE,
|
||||
L"COMBOBOX", L"",
|
||||
WS_CHILD | WS_VISIBLE | CBS_DROPDOWN,
|
||||
combox, comboy,
|
||||
buttonwid, buttonht,
|
||||
hwnd, (HMENU) 300, hInstance, NULL);
|
||||
if (rebarCombo == NULL)
|
||||
diele("CreateWindowExW(L\"COMBOBOX\")");
|
||||
for (i = 0; rebarThemes[i] != NULL; i++)
|
||||
// TODO check error
|
||||
SendMessageW(rebarCombo, CB_ADDSTRING, 0, (LPARAM) (rebarThemes[i]));
|
||||
comboy += buttonht + 5;
|
||||
toolbarCombo = CreateWindowExW(WS_EX_CLIENTEDGE,
|
||||
L"COMBOBOX", L"",
|
||||
WS_CHILD | WS_VISIBLE | CBS_DROPDOWN,
|
||||
combox, comboy,
|
||||
buttonwid, buttonht,
|
||||
hwnd, (HMENU) 301, hInstance, NULL);
|
||||
if (toolbarCombo == NULL)
|
||||
diele("CreateWindowExW(L\"COMBOBOX\")");
|
||||
for (i = 0; toolbarThemes[i] != NULL; i++)
|
||||
// TODO check error
|
||||
SendMessageW(toolbarCombo, CB_ADDSTRING, 0, (LPARAM) (toolbarThemes[i]));
|
||||
comboy += buttonht + 5;
|
||||
toolbarTransparentCheckbox = CreateWindowExW(0,
|
||||
L"BUTTON", L"Transparent toolbar",
|
||||
WS_CHILD | WS_VISIBLE | BS_CHECKBOX,
|
||||
combox, comboy,
|
||||
buttonwid, buttonht,
|
||||
hwnd, (HMENU) 302, hInstance, NULL);
|
||||
if (toolbarTransparentCheckbox == NULL)
|
||||
diele("CreateWindowExW(L\"BUTTON\")");
|
||||
SendMessage(toolbarTransparentCheckbox, BM_SETCHECK, BST_CHECKED, 0);
|
||||
}
|
||||
|
||||
// TODO it seems like I shouldn't have to do this?
|
||||
void repositionRebar(HWND hwnd)
|
||||
{
|
||||
RECT win, rb;
|
||||
|
||||
if (GetClientRect(hwnd, &win) == 0)
|
||||
diele("GetClientRect()");
|
||||
if (GetWindowRect(rebar, &rb) == 0)
|
||||
diele("GetWindowRect()");
|
||||
if (SetWindowPos(rebar, NULL,
|
||||
0, 0, win.right - win.left, rb.bottom - rb.top,
|
||||
SWP_NOACTIVATE | SWP_NOMOVE | SWP_NOOWNERZORDER | SWP_NOZORDER) == 0)
|
||||
diele("SetWindowPos()");
|
||||
}
|
||||
|
||||
// TODO check errors
|
||||
void handleEvents(HWND hwnd, WPARAM wParam)
|
||||
{
|
||||
LRESULT n;
|
||||
const WCHAR *selRebar = NULL, *selToolbar = NULL;
|
||||
WCHAR *bufRebar = NULL, *bufToolbar = NULL;
|
||||
BOOL changeRebar = FALSE, changeToolbar = FALSE;
|
||||
BOOL invalidate = FALSE;
|
||||
WPARAM check;
|
||||
|
||||
switch (wParam) {
|
||||
case MAKEWPARAM(300, CBN_SELCHANGE):
|
||||
n = SendMessageW(rebarCombo, CB_GETCURSEL, 0, 0);
|
||||
selRebar = rebarThemes[n];
|
||||
changeRebar = TRUE;
|
||||
break;
|
||||
case MAKEWPARAM(301, CBN_SELCHANGE):
|
||||
n = SendMessageW(toolbarCombo, CB_GETCURSEL, 0, 0);
|
||||
selToolbar = toolbarThemes[n];
|
||||
changeToolbar = TRUE;
|
||||
break;
|
||||
case MAKEWPARAM(200, BN_CLICKED):
|
||||
drawmode = 0;
|
||||
n = SendMessageW(rebarCombo, WM_GETTEXTLENGTH, 0, 0);
|
||||
bufRebar = new WCHAR[n + 1];
|
||||
GetWindowTextW(rebarCombo, bufRebar, n + 1);
|
||||
n = SendMessageW(toolbarCombo, WM_GETTEXTLENGTH, 0, 0);
|
||||
bufToolbar = new WCHAR[n + 1];
|
||||
GetWindowTextW(toolbarCombo, bufToolbar, n + 1);
|
||||
selRebar = bufRebar;
|
||||
selToolbar = bufToolbar;
|
||||
changeRebar = TRUE;
|
||||
changeToolbar = TRUE;
|
||||
break;
|
||||
case MAKEWPARAM(201, BN_CLICKED):
|
||||
drawmode = 1;
|
||||
invalidate = TRUE;
|
||||
break;
|
||||
case MAKEWPARAM(302, BN_CLICKED):
|
||||
ShowWindow(leftbar, SW_HIDE);
|
||||
check = BST_CHECKED;
|
||||
if (SendMessage(toolbarTransparentCheckbox, BM_GETCHECK, 0, 0) == BST_CHECKED)
|
||||
check = BST_UNCHECKED;
|
||||
SendMessage(toolbarTransparentCheckbox, BM_SETCHECK, check, 0);
|
||||
if (check == BST_CHECKED)
|
||||
SendMessageW(leftbar, TB_SETSTYLE, 0, toolbarStyles | TBSTYLE_LIST | TBSTYLE_TRANSPARENT);
|
||||
else
|
||||
SendMessageW(leftbar, TB_SETSTYLE, 0, toolbarStyles | TBSTYLE_LIST);
|
||||
ShowWindow(leftbar, SW_SHOW);
|
||||
break;
|
||||
case MAKEWPARAM(202, BN_CLICKED):
|
||||
SetFocus(leftbar);
|
||||
break;
|
||||
}
|
||||
if (changeRebar) {
|
||||
if (selRebar != NULL && wcscmp(selRebar, L"NULL") == 0)
|
||||
selRebar = NULL;
|
||||
if (selRebar != NULL && *selRebar != L'\0')
|
||||
SendMessageW(rebar, RB_SETWINDOWTHEME, 0, (LPARAM) selRebar);
|
||||
else
|
||||
SetWindowTheme(rebar, selRebar, selRebar);
|
||||
invalidate = TRUE;
|
||||
}
|
||||
if (changeToolbar) {
|
||||
if (selToolbar != NULL && wcscmp(selToolbar, L"NULL") == 0)
|
||||
selToolbar = NULL;
|
||||
if (selToolbar != NULL && *selToolbar != L'\0') {
|
||||
SendMessageW(leftbar, TB_SETWINDOWTHEME, 0, (LPARAM) selToolbar);
|
||||
SendMessageW(rightbar, TB_SETWINDOWTHEME, 0, (LPARAM) selToolbar);
|
||||
} else {
|
||||
SetWindowTheme(leftbar, selToolbar, selToolbar);
|
||||
SetWindowTheme(rightbar, selToolbar, selToolbar);
|
||||
}
|
||||
invalidate = TRUE;
|
||||
}
|
||||
if (invalidate)
|
||||
InvalidateRect(hwnd, NULL, TRUE);
|
||||
if (bufRebar != NULL)
|
||||
delete[] bufRebar;
|
||||
if (bufToolbar != NULL)
|
||||
delete[] bufToolbar;
|
||||
}
|
||||
|
||||
LRESULT CALLBACK wndproc(HWND hwnd, UINT uMsg, WPARAM wParam, LPARAM lParam)
|
||||
{
|
||||
NMHDR *nm = (NMHDR *) lParam;
|
||||
|
||||
switch (uMsg) {
|
||||
case WM_CREATE:
|
||||
onWM_CREATE(hwnd);
|
||||
break;
|
||||
case WM_CLOSE:
|
||||
PostQuitMessage(0);
|
||||
break;
|
||||
case WM_SIZE:
|
||||
repositionRebar(hwnd);
|
||||
break;
|
||||
case WM_COMMAND:
|
||||
handleEvents(hwnd, wParam);
|
||||
break;
|
||||
case WM_NOTIFY:
|
||||
switch (nm->code) {
|
||||
case NM_CUSTOMDRAW:
|
||||
if (drawmode == 0)
|
||||
break;
|
||||
if (nm->hwndFrom == rebar)
|
||||
return (*(drawmodes[drawmode].handleRebar))((NMCUSTOMDRAW *) nm);
|
||||
else if (nm->hwndFrom == leftbar || nm->hwndFrom == rightbar)
|
||||
return (*(drawmodes[drawmode].handleToolbar))((NMTBCUSTOMDRAW *) nm);
|
||||
break;
|
||||
}
|
||||
break;
|
||||
}
|
||||
return DefWindowProcW(hwnd, uMsg, wParam, lParam);
|
||||
}
|
||||
|
||||
EXTERN_C IMAGE_DOS_HEADER __ImageBase;
|
||||
|
||||
int main(void)
|
||||
{
|
||||
STARTUPINFOW si;
|
||||
int nCmdShow;
|
||||
INITCOMMONCONTROLSEX icc;
|
||||
HICON hDefaultIcon;
|
||||
HCURSOR hDefaultCursor;
|
||||
WNDCLASSW wc;
|
||||
HWND mainwin;
|
||||
MSG msg;
|
||||
HRESULT hr;
|
||||
|
||||
hInstance = (HINSTANCE) (&__ImageBase);
|
||||
nCmdShow = SW_SHOWDEFAULT;
|
||||
GetStartupInfoW(&si);
|
||||
if ((si.dwFlags & STARTF_USESHOWWINDOW) != 0)
|
||||
nCmdShow = si.wShowWindow;
|
||||
|
||||
ZeroMemory(&icc, sizeof (INITCOMMONCONTROLSEX));
|
||||
icc.dwSize = sizeof (INITCOMMONCONTROLSEX);
|
||||
icc.dwICC = ICC_STANDARD_CLASSES | ICC_BAR_CLASSES | ICC_COOL_CLASSES;
|
||||
if (InitCommonControlsEx(&icc) == 0)
|
||||
diele("InitCommonControlsEx()");
|
||||
|
||||
hDefaultIcon = LoadIconW(NULL, IDI_APPLICATION);
|
||||
if (hDefaultIcon == NULL)
|
||||
diele("LoadIconW(IDI_APPLICATION)");
|
||||
hDefaultCursor = LoadCursorW(NULL, IDC_ARROW);
|
||||
if (hDefaultCursor == NULL)
|
||||
diele("LoadCursorW(IDC_ARROW)");
|
||||
|
||||
hr = LoadIconMetric(NULL, IDI_SHIELD, LIM_SMALL, &shieldIcon);
|
||||
if (hr != S_OK)
|
||||
diehr("LoadIconMetric(IDI_SHIELD)", hr);
|
||||
hr = LoadIconMetric(NULL, IDI_APPLICATION, LIM_SMALL, &applicationIcon);
|
||||
if (hr != S_OK)
|
||||
diehr("LoadIconMetric(IDI_APPLICATION)", hr);
|
||||
hr = LoadIconMetric(NULL, IDI_QUESTION, LIM_SMALL, &helpIcon);
|
||||
if (hr != S_OK)
|
||||
diehr("LoadIconMetric(IDI_QUESTION)", hr);
|
||||
rightList = ImageList_Create(GetSystemMetrics(SM_CXSMICON), GetSystemMetrics(SM_CYSMICON),
|
||||
ILC_COLOR32, 0, 3);
|
||||
if (rightList == NULL)
|
||||
diele("ImageList_Create()");
|
||||
if (ImageList_ReplaceIcon(rightList, -1, shieldIcon) == -1)
|
||||
diele("ImageList_ReplaceIcon(IDI_SHIELD)");
|
||||
if (ImageList_ReplaceIcon(rightList, -1, applicationIcon) == -1)
|
||||
diele("ImageList_ReplaceIcon(IDI_APPLICATION)");
|
||||
if (ImageList_ReplaceIcon(rightList, -1, helpIcon) == -1)
|
||||
diele("ImageList_ReplaceIcon(IDI_QUESTION)");
|
||||
|
||||
ZeroMemory(&wc, sizeof (WNDCLASSW));
|
||||
wc.lpszClassName = L"mainwin";
|
||||
wc.lpfnWndProc = wndproc;
|
||||
wc.hInstance = hInstance;
|
||||
wc.hIcon = hDefaultIcon;
|
||||
wc.hCursor = hDefaultCursor;
|
||||
wc.hbrBackground = (HBRUSH) (COLOR_BTNFACE + 1);
|
||||
if (RegisterClassW(&wc) == 0)
|
||||
diele("RegisterClassW()");
|
||||
|
||||
mainwin = CreateWindowExW(0,
|
||||
L"mainwin", L"Main Window",
|
||||
WS_OVERLAPPEDWINDOW,
|
||||
CW_USEDEFAULT, CW_USEDEFAULT,
|
||||
CW_USEDEFAULT, CW_USEDEFAULT,
|
||||
NULL, NULL, hInstance, NULL);
|
||||
if (mainwin == NULL)
|
||||
diele("CreateWindowExW(L\"mainwin\")");
|
||||
|
||||
ShowWindow(mainwin, nCmdShow);
|
||||
if (UpdateWindow(mainwin) == 0)
|
||||
diele("UpdateWindow()");
|
||||
|
||||
for (;;) {
|
||||
int res;
|
||||
|
||||
res = GetMessageW(&msg, NULL, 0, 0);
|
||||
if (res < 0)
|
||||
diele("GetMessageW()");
|
||||
if (res == 0)
|
||||
break;
|
||||
TranslateMessage(&msg);
|
||||
DispatchMessageW(&msg);
|
||||
}
|
||||
return 0;
|
||||
}
|
|
@ -0,0 +1,41 @@
|
|||
TODO clean this up
|
||||
|
||||
TODO note that you -fvisibility=hidden means nothing in static libraries, hence this (confirmed on OS X)
|
||||
|
||||
In general, all names that begin with "ui" and are followed by a capital letter and all names htat begin with "uipriv" and are followed by a capita lletter are reserved by libui. This applies even in C++, where name mangling may affect the actual names in the object file.
|
||||
|
||||
# Reserved names; for users
|
||||
|
||||
All reserved names in libui are defined by a prefix followed by any uppercase letter in ASCII. The bullet lists before list those prefixes.
|
||||
|
||||
Global-scope identifiers of any form (variables, constant names, functions, structure names, union names, C++ class names, enum type names, enum value names, C++ namespace names, GObject class and interface struct names, and Objective-C class and protocol name identifiers) and macro names:
|
||||
|
||||
- `ui`
|
||||
- `uipriv`
|
||||
|
||||
GObject and Objective-C class, interface, and protocol name strings, in the form they take in their respective runtime memory (e.g. when passed to `g_type_from_name()` and `NSClassFromString()`, respectively):
|
||||
|
||||
- `uipriv`
|
||||
|
||||
Objective-C method names:
|
||||
|
||||
- `initWithUipriv`
|
||||
- `initWithFrame:uipriv` (TODO probably worth removing)
|
||||
- `uipriv`
|
||||
- `isUipriv` (for compatibility with KVO and `@property` statements)
|
||||
- `setUipriv` (for compatibility with KVO and `@property` statements)
|
||||
|
||||
Objective-C ivar names:
|
||||
|
||||
- `uipriv`
|
||||
- `_uipriv` (for compatibility with KVO and `@property` statements)
|
||||
|
||||
Objective-C property names:
|
||||
|
||||
- `uipriv`
|
||||
|
||||
TODO GObject macros (in libui's source code), properties, and signals
|
||||
|
||||
# Developer notes
|
||||
|
||||
TODO
|
|
@ -0,0 +1,3 @@
|
|||
you should never need to use these functions
|
||||
they are provided only for the cases when ABSOLUTELY NECESSARY
|
||||
the operating system may ignore your requests, for instance, if you are giving it invalid numbers or a size too small to fit; this is all system-defined
|
|
@ -0,0 +1,190 @@
|
|||
_UI_ENUM(uiAttribute) {
|
||||
uiAttributeFamily,
|
||||
uiAttributeSize, // use Double
|
||||
uiAttributeWeight,
|
||||
uiAttributeItalic,
|
||||
uiAttributeStretch,
|
||||
uiAttributeColor, // use R, G, B, A
|
||||
uiAttributeBackground, // use R, G, B, A
|
||||
|
||||
// TODO kerning amount
|
||||
// OS X: kCTKernAttributeName
|
||||
// > 0: farther (TODO from advance or standard kerning?)
|
||||
// == 0: no kerning
|
||||
// < 0: closer (TODO same)
|
||||
// undefined: standard kerning
|
||||
// Pango: pango_attr_letter_spacing_new()
|
||||
// parameter meaning unspecified
|
||||
// Windows: requires Platform Update, SetLetterSpacing()
|
||||
// parameter meaning unspecified
|
||||
|
||||
uiAttributeUnderline, // enum uiDrawUnderlineStyle
|
||||
// TODO what is the color in the case we don't specify it, black or the text color?
|
||||
uiAttributeUnderlineColor, // enum uiDrawUnderlineColor
|
||||
|
||||
// TODO kCTSuperscriptAttributeName vs below
|
||||
// all it does is set the below attribute so
|
||||
|
||||
// TODO kCTBaselineClassAttributeName, kCTBaselineInfoAttributeName, kCTBaselineReferenceInfoAttributeName
|
||||
|
||||
// TODO strikethroughs? (pango yes, directwrite yes, os x no)
|
||||
// TODO baseline offsets? (pango yes)
|
||||
// TODO size scales? (pango yes)
|
||||
// TODO fallbacks (pango: enable or disable)
|
||||
|
||||
// TODO document that this will also enable language-specific font features (TODO on DirectWrite too?)
|
||||
// TODO document that this should be strict BCP 47 form (A-Z, a-z, 0-9, and -) for maximum compatibility
|
||||
uiAttributeLanguage, // BCP 47 string
|
||||
|
||||
// These attributes represent typographic features. Each feature
|
||||
// is a separate attribute, to make composition easier. The
|
||||
// availability of for each attribute are defined by the font; the
|
||||
// default values are defined by the font and/or by the OS.
|
||||
//
|
||||
// A note about features whose parameter is an enumeration:
|
||||
// OS X defines typographic features using the AAT specification
|
||||
// and converts to OpenType internally when needed, whereas
|
||||
// other platforms use OpenType directly. OpenType is less
|
||||
// precise about what each enumeration value means than AAT
|
||||
// is, so enumeration values do not necessarily represent what
|
||||
// OS X expects with all fonts. In cases where they do, libui
|
||||
// provides an enumeration type to use. Otherwise, the AAT
|
||||
// enumeration values are provided in comments for
|
||||
// documentation purposes.
|
||||
|
||||
// TODO kAllTypographicFeaturesType
|
||||
|
||||
// AAT calls these "common ligatures"
|
||||
uiAttributeStandardLigatures, // 0 = off, 1 = on
|
||||
uiAttributeRequiredLigatures, // 0 = off, 1 = on
|
||||
// AAT calls these "rare ligatures"
|
||||
uiAttributeDiscretionaryLigatures, // 0 = off, 1 = on
|
||||
uiAttributeContextualLigatures, // 0 = off, 1 = on
|
||||
uiAttributeHistoricalLigatures, // 0 = off, 1 = on
|
||||
|
||||
// TODO uiAttributeCursiveConnection, // 0 = none, 1 = some, 2 = all
|
||||
|
||||
uiAttributeUnicase, // 0 = off, 1 = on
|
||||
|
||||
// TODO uiAttributeLinguisticRearrangement, // 0 = off, 1 = on
|
||||
|
||||
// TODO rename this
|
||||
uiAttributeNumberSpacings, // enum uiAttributeNumberSpacing
|
||||
|
||||
// TODO kSmartSwashType, falt and jalt
|
||||
|
||||
// TODO kDiacriticsType
|
||||
|
||||
uiAttributeSuperscripts, // enum uiAttributeSuperscript
|
||||
|
||||
uiAttributeFractionForms, // enum uiAttributeFractionForm
|
||||
|
||||
uiAttributeSlashedZero, // 0 = off, 1 = on
|
||||
|
||||
uiAttributeMathematicalGreek, // 0 = off, 1 = on
|
||||
|
||||
// AAT defines the following values:
|
||||
// 0 = none
|
||||
// 1 = dingbats
|
||||
// 2 = pi characters
|
||||
// 3 = fleurons
|
||||
// 4 = decorative borders
|
||||
// 5 = international symbols
|
||||
// 6 = mathematical symbols
|
||||
// OpenType says alphanumeric characters must(? TODO) have one form each and the bullet character U+2022 (•) can have many
|
||||
uiAttributeOrnamentalForms, // an integer from 0 to a font-specified upper bound
|
||||
// TODO provide a function to get the upper bound?
|
||||
|
||||
// AAT calls this "character alternatives" and defines the
|
||||
// following values:
|
||||
// 0 = none
|
||||
// OpenType calls this "access all alternates".
|
||||
// TODO doesn't OpenType do the same about 0?
|
||||
uiAttributeSpecificCharacterForm, // an integer from 0 to a font-specified upper bound
|
||||
// TODO provide a function to get the upper bound?
|
||||
|
||||
uiAttributeTitlingCapitalForms, // 0 = off, 1 = on
|
||||
|
||||
// AAT calls these "character shapes"
|
||||
uiAttributeHanCharacterForms, // enum uiAttributeHanCharacterForm
|
||||
|
||||
// OpenType calls these "old-style"
|
||||
uiAttributeLowercaseNumbers, // 0 = off, 1 = on
|
||||
|
||||
// TODO kTextSpacingType
|
||||
// see kKanaSpacingType below
|
||||
|
||||
uiAttributeHanjaToHangul, // 0 = off, 1 = on
|
||||
|
||||
// AAT defines the following values:
|
||||
// 0 = none
|
||||
// 1 = box
|
||||
// 2 = rounded box
|
||||
// 3 = circle
|
||||
// 4 = inverted circle
|
||||
// 5 = parentheses
|
||||
// 6 = period
|
||||
// 7 = roman numeral
|
||||
// 8 = diamond
|
||||
// 9 = inverted box
|
||||
// 10 = inverted rounded box
|
||||
// TODO rename to AnnotatedForms?
|
||||
uiAttributeAnnotatedGlyphForms, // an integer from 0 to a font-specified upper bound
|
||||
// TODO provide a function to get the upper bound?
|
||||
|
||||
// TODO kKanaSpacingType
|
||||
// TODO kIdeographicSpacingType
|
||||
// can they be provided independently of kTextSpacingType? Core Text doesn't seem to
|
||||
|
||||
// TODO kUnicodeDecompositionType
|
||||
|
||||
uiAttributeRubyKanaForms, // 0 = off, 1 = on
|
||||
|
||||
// TODO kCJKVerticalRomanPlacementType
|
||||
// this is 'valt' in OpenType but I don't know if I want to make it selectable or not
|
||||
|
||||
uiAttributeCJKRomansToItalics, // 0 = off, 1 = on
|
||||
|
||||
// AAT calls this "case-sensitive layout"
|
||||
uiAttributeCaseSensitiveForms, // 0 = off, 1 = on
|
||||
// AAT: this is called "case-sensitive spacing"
|
||||
uiAttributeCapitalSpacing, // 0 = off, 1 = on
|
||||
|
||||
uiAttributeAlternateHorizontalKana, // 0 = off, 1 = on
|
||||
uiAttributeAlternateVerticalKana, // 0 = off, 1 = on
|
||||
|
||||
// TODO "Alternate"? unify all this
|
||||
// TODO document that these are guaranteed to be consecutive
|
||||
uiAttributeStylisticAlternate1, // 0 = off, 1 = on
|
||||
uiAttributeStylisticAlternate2, // 0 = off, 1 = on
|
||||
uiAttributeStylisticAlternate3, // 0 = off, 1 = on
|
||||
uiAttributeStylisticAlternate4, // 0 = off, 1 = on
|
||||
uiAttributeStylisticAlternate5, // 0 = off, 1 = on
|
||||
uiAttributeStylisticAlternate6, // 0 = off, 1 = on
|
||||
uiAttributeStylisticAlternate7, // 0 = off, 1 = on
|
||||
uiAttributeStylisticAlternate8, // 0 = off, 1 = on
|
||||
uiAttributeStylisticAlternate9, // 0 = off, 1 = on
|
||||
uiAttributeStylisticAlternate10, // 0 = off, 1 = on
|
||||
uiAttributeStylisticAlternate11, // 0 = off, 1 = on
|
||||
uiAttributeStylisticAlternate12, // 0 = off, 1 = on
|
||||
uiAttributeStylisticAlternate13, // 0 = off, 1 = on
|
||||
uiAttributeStylisticAlternate14, // 0 = off, 1 = on
|
||||
uiAttributeStylisticAlternate15, // 0 = off, 1 = on
|
||||
uiAttributeStylisticAlternate16, // 0 = off, 1 = on
|
||||
uiAttributeStylisticAlternate17, // 0 = off, 1 = on
|
||||
uiAttributeStylisticAlternate18, // 0 = off, 1 = on
|
||||
uiAttributeStylisticAlternate19, // 0 = off, 1 = on
|
||||
uiAttributeStylisticAlternate20, // 0 = off, 1 = on
|
||||
|
||||
uiAttributeContextualAlternates, // 0 = off, 1 = on
|
||||
uiAttributeSwashes, // 0 = off, 1 = on
|
||||
uiAttributeContextualSwashes, // 0 = off, 1 = on
|
||||
|
||||
uiAttributeLowercaseCapForms, // enum uiAttributeCapForm
|
||||
uiAttributeUppercaseCapForms, // enum uiAttributeCapForm
|
||||
|
||||
// TODO kCJKRomanSpacingType
|
||||
|
||||
// TODO uiAttributeSystem, (this might not be doable with DirectWrite)
|
||||
// TODO uiAttributeCustom,
|
||||
};
|
|
@ -0,0 +1 @@
|
|||
Removed because proper support on OS X doesn't come until 10.9 unless we use a font with an ltag table; none of the fonts I have come with ltag tables (none of the fonts on OS X do, or at least don't come with a sr entry in their ltag table, and OpenType has replaced ltag with what appears to be custom sub-tables of the GPOS and GSUB tables.)
|
|
@ -0,0 +1,19 @@
|
|||
struct fontParams {
|
||||
uiDrawFontDescriptor desc;
|
||||
uint16_t featureTypes[maxFeatures];
|
||||
uint16_t featureSelectors[maxFeatures];
|
||||
size_t nFeatures;
|
||||
const char *language;
|
||||
};
|
||||
|
||||
|
||||
// locale identifiers are specified as BCP 47: https://developer.apple.com/reference/corefoundation/cflocale?language=objc
|
||||
case uiAttributeLanguage:
|
||||
// LONGTERM FUTURE when we move to 10.9, switch to using kCTLanguageAttributeName
|
||||
ensureFontInRange(p, start, end);
|
||||
adjustFontInRange(p, start, end, ^(struct fontParams *fp) {
|
||||
fp->language = (const char *) (spec->Value);
|
||||
});
|
||||
break;
|
||||
|
||||
desc = fontdescAppendFeatures(desc, fp->featureTypes, fp->featureSelectors, fp->nFeatures, fp->language);
|
|
@ -0,0 +1,9 @@
|
|||
PangoLanguage *lang;
|
||||
|
||||
// language strings are specified as BCP 47: https://developer.gnome.org/pango/1.30/pango-Scripts-and-Languages.html#pango-language-from-string https://www.ietf.org/rfc/rfc3066.txt
|
||||
case uiAttributeLanguage:
|
||||
lang = pango_language_from_string((const char *) (spec->Value));
|
||||
addattr(p, start, end,
|
||||
pango_attr_language_new(lang));
|
||||
// lang *cannot* be freed
|
||||
break;
|
|
@ -0,0 +1,10 @@
|
|||
WCHAR *localeName;
|
||||
|
||||
// locale names are specified as BCP 47: https://msdn.microsoft.com/en-us/library/windows/desktop/dd373814(v=vs.85).aspx https://www.ietf.org/rfc/rfc4646.txt
|
||||
case uiAttributeLanguage:
|
||||
localeName = toUTF16((char *) (spec->Value));
|
||||
hr = p->layout->SetLocaleName(localeName, range);
|
||||
if (hr != S_OK)
|
||||
logHRESULT(L"error applying locale name attribute", hr);
|
||||
uiFree(localeName);
|
||||
break;
|
|
@ -0,0 +1,2 @@
|
|||
case uiAttributeLanguage:
|
||||
return asciiStringsEqualCaseFold((char *) (attr->spec.Value), (char *) (spec->Value));
|
|
@ -0,0 +1,27 @@
|
|||
before "or any combination of the above"
|
||||
|
||||
// thanks to https://twitter.com/codeman38/status/831924064012886017
|
||||
next = "\xD0\xB1\xD0\xB3\xD0\xB4\xD0\xBF\xD1\x82";
|
||||
uiAttributedStringAppendUnattributed(attrstr, "multiple languages (compare ");
|
||||
start = uiAttributedStringLen(attrstr);
|
||||
end = start + strlen(next);
|
||||
uiAttributedStringAppendUnattributed(attrstr, next);
|
||||
spec.Type = uiAttributeItalic;
|
||||
spec.Value = uiDrawTextItalicItalic;
|
||||
uiAttributedStringSetAttribute(attrstr, &spec, start, end);
|
||||
spec.Type = uiAttributeLanguage;
|
||||
spec.Value = (uintptr_t) "ru";
|
||||
uiAttributedStringSetAttribute(attrstr, &spec, start, end);
|
||||
uiAttributedStringAppendUnattributed(attrstr, " to ");
|
||||
start = uiAttributedStringLen(attrstr);
|
||||
end = start + strlen(next);
|
||||
uiAttributedStringAppendUnattributed(attrstr, next);
|
||||
spec.Type = uiAttributeItalic;
|
||||
spec.Value = uiDrawTextItalicItalic;
|
||||
uiAttributedStringSetAttribute(attrstr, &spec, start, end);
|
||||
spec.Type = uiAttributeLanguage;
|
||||
spec.Value = (uintptr_t) "sr";
|
||||
uiAttributedStringSetAttribute(attrstr, &spec, start, end);
|
||||
uiAttributedStringAppendUnattributed(attrstr, " \xE2\x80\x94 may require changing the font)");
|
||||
|
||||
uiAttributedStringAppendUnattributed(attrstr, ", ");
|
|
@ -0,0 +1,112 @@
|
|||
// note: this doesn't work for languages; we have to parse the ltag table
|
||||
|
||||
// fortunately features that aren't supported are simply ignored, so we can copy them all in
|
||||
// LONGTERM FUTURE when we switch to 10.9, the language parameter won't be needed anymore
|
||||
// LONGTERM FUTURE and on 10.10 we can use OpenType tags directly!
|
||||
CTFontDescriptorRef fontdescAppendFeatures(CTFontDescriptorRef desc, const uint16_t *types, const uint16_t *selectors, size_t n, const char *language)
|
||||
{
|
||||
CTFontDescriptorRef new;
|
||||
CFMutableArrayRef outerArray;
|
||||
CFDictionaryRef innerDict;
|
||||
CFNumberRef numType, numSelector;
|
||||
const void *keys[2], *values[2];
|
||||
size_t i;
|
||||
CFArrayRef languages;
|
||||
CFIndex il, nl;
|
||||
CFStringRef curlang;
|
||||
char d[2];
|
||||
|
||||
outerArray = CFArrayCreateMutable(NULL, 0, &kCFTypeArrayCallBacks);
|
||||
if (outerArray == NULL) {
|
||||
// TODO
|
||||
}
|
||||
keys[0] = kCTFontFeatureTypeIdentifierKey;
|
||||
keys[1] = kCTFontFeatureSelectorIdentifierKey;
|
||||
for (i = 0; i < n; i++) {
|
||||
numType = CFNumberCreate(NULL, kCFNumberSInt16Type,
|
||||
(const SInt16 *) (types + i));
|
||||
numSelector = CFNumberCreate(NULL, kCFNumberSInt16Type,
|
||||
(const SInt16 *) (selectors + i));
|
||||
values[0] = numType;
|
||||
values[1] = numSelector;
|
||||
innerDict = CFDictionaryCreate(NULL,
|
||||
keys, values, 2,
|
||||
// TODO are these correct?
|
||||
&kCFCopyStringDictionaryKeyCallBacks,
|
||||
&kCFTypeDictionaryValueCallBacks);
|
||||
if (innerDict == NULL) {
|
||||
// TODO
|
||||
}
|
||||
CFArrayAppendValue(outerArray, innerDict);
|
||||
CFRelease(innerDict);
|
||||
CFRelease(numSelector);
|
||||
CFRelease(numType);
|
||||
}
|
||||
|
||||
// now we have to take care of the language
|
||||
if (language != NULL) {
|
||||
languages = CTFontDescriptorCopyAttribute(desc, kCTFontLanguagesAttribute);
|
||||
if (languages != NULL) {
|
||||
nl = CFArrayGetCount(languages);
|
||||
d[0] = language[0];
|
||||
if (d[0] >= 'A' && d[0] <= 'Z')
|
||||
d[0] += 'a' - 'A';
|
||||
d[1] = language[1];
|
||||
if (d[1] >= 'A' && d[1] <= 'Z')
|
||||
d[1] += 'a' - 'A';
|
||||
for (il = 0; il < nl; il++) {
|
||||
char c[2];
|
||||
|
||||
curlang = (CFStringRef) CFArrayGetValueAtIndex(languages, il);
|
||||
// TODO check for failure
|
||||
CFStringGetBytes(curlang, CFRangeMake(0, 2),
|
||||
kCFStringEncodingUTF8, 0, false,
|
||||
(UInt8 *) c, 2, NULL);
|
||||
if (c[0] >= 'A' && c[0] <= 'Z')
|
||||
c[0] += 'a' - 'A';
|
||||
if (c[1] >= 'A' && c[1] <= 'Z')
|
||||
c[1] += 'a' - 'A';
|
||||
if (c[0] == d[0] && c[1] == d[1])
|
||||
break;
|
||||
}
|
||||
if (il != nl) {
|
||||
uint16_t typ;
|
||||
|
||||
typ = kLanguageTagType;
|
||||
il++;
|
||||
numType = CFNumberCreate(NULL, kCFNumberSInt16Type,
|
||||
(const SInt16 *) (&typ));
|
||||
numSelector = CFNumberCreate(NULL, kCFNumberCFIndexType,
|
||||
&il);
|
||||
values[0] = numType;
|
||||
values[1] = numSelector;
|
||||
innerDict = CFDictionaryCreate(NULL,
|
||||
keys, values, 2,
|
||||
// TODO are these correct?
|
||||
&kCFCopyStringDictionaryKeyCallBacks,
|
||||
&kCFTypeDictionaryValueCallBacks);
|
||||
if (innerDict == NULL) {
|
||||
// TODO
|
||||
}
|
||||
CFArrayAppendValue(outerArray, innerDict);
|
||||
CFRelease(innerDict);
|
||||
CFRelease(numSelector);
|
||||
CFRelease(numType);
|
||||
}
|
||||
CFRelease(languages);
|
||||
}
|
||||
}
|
||||
|
||||
keys[0] = kCTFontFeatureSettingsAttribute;
|
||||
values[0] = outerArray;
|
||||
innerDict = CFDictionaryCreate(NULL,
|
||||
keys, values, 1,
|
||||
// TODO are these correct?
|
||||
&kCFCopyStringDictionaryKeyCallBacks,
|
||||
&kCFTypeDictionaryValueCallBacks);
|
||||
CFRelease(outerArray);
|
||||
new = CTFontDescriptorCreateCopyWithAttributes(desc, innerDict);
|
||||
CFRelease(desc);
|
||||
CFRelease(innerDict);
|
||||
return new;
|
||||
}
|
|
@ -0,0 +1,5 @@
|
|||
after UnderlineColor, before feature tags
|
||||
|
||||
// TODO document that this will also enable language-specific font features (TODO on DirectWrite too?)
|
||||
// TODO document that this should be strict BCP 47 form (A-Z, a-z, 0-9, and -) for maximum compatibility
|
||||
uiAttributeLanguage, // BCP 47 string
|
|
@ -0,0 +1,25 @@
|
|||
= attributed strings
|
||||
attribute lengths are rounded to complete unicode codepoints
|
||||
zero-length attributes are elided
|
||||
consecutive attributes of the same type and value are merged
|
||||
overlapping attributes of different types do not split each other
|
||||
overlapping attributes of the same type but different values do split
|
||||
empty string is allowed
|
||||
empty string cannot have attributes
|
||||
font family names are case-insensitive both in attributes and in descriptors
|
||||
attributes are unique throughout a Unicode codepoint, not just to UTF-8 bytes
|
||||
define what "it is an error" means in the case of uiFreeAttribute() and all uiAttributeValue() functions and constructors
|
||||
does uiAttributeFamily() return a normalized string
|
||||
should uiNewAttributeBackground() be renamed to uiNewAttributeBackgroundColor() and likewise for the type constant
|
||||
should underline colors just ignore non-custom component arguments
|
||||
should any color getter function accept a NULL pointer
|
||||
what should uiAttributeUnderlineColor() do if the color type isn't Custom but the other pointers are non-NULL
|
||||
should uiOpenTypeFeaturesGet() accept a NULL value pointer
|
||||
what happens if uiOpenTypeFeaturesForEach() is given a NULl function pointer
|
||||
should FeaturesAttribute be changed to OpenTypeFeaturesAttribute and likewise for the type enum
|
||||
should uiNewFeaturesAttribute() accept NULL
|
||||
should uiNewFamilyAttribute() accept NULL
|
||||
it is an error in ForEach too
|
||||
invalid values for uiDrawTextAlign
|
||||
empty text layouts have one line
|
||||
TODO figure out what to do if any field (particularly the font family name) in uiFontDescriptor is unset
|
|
@ -0,0 +1,115 @@
|
|||
// 27 february 2018
|
||||
#ifndef TODO_TEST
|
||||
#error TODO this is where libui itself goes
|
||||
#endif
|
||||
#include <inttypes.h>
|
||||
#include "testing.h"
|
||||
|
||||
#include <stddef.h>
|
||||
#include <stdint.h>
|
||||
#include <stdlib.h>
|
||||
#include <string.h>
|
||||
typedef struct uiOpenTypeFeatures uiOpenTypeFeatures;
|
||||
typedef int uiForEach;
|
||||
enum { uiForEachContinue, uiForEachStop };
|
||||
typedef uiForEach (*uiOpenTypeFeaturesForEachFunc)(const uiOpenTypeFeatures *otf, char a, char b, char c, char d, uint32_t value, void *data);
|
||||
#define uiprivNew(x) ((x *) malloc(sizeof (x)))
|
||||
#define uiprivAlloc(x,y) malloc(x)
|
||||
#define uiprivRealloc(x,y,z) realloc(x,y)
|
||||
#define uiprivFree free
|
||||
#include "opentype.c"
|
||||
|
||||
static void freeOpenType(void *otf)
|
||||
{
|
||||
uiFreeOpenTypeFeatures((uiOpenTypeFeatures *) otf);
|
||||
}
|
||||
|
||||
testingTest(OpenTypeFeaturesAddGet)
|
||||
{
|
||||
uiOpenTypeFeatures *otf;
|
||||
int got;
|
||||
uint32_t value;
|
||||
|
||||
otf = uiNewOpenTypeFeatures();
|
||||
testingTDefer(t, freeOpenType, otf);
|
||||
uiOpenTypeFeaturesAdd(otf, 'a', 'b', 'c', 'd', 12345);
|
||||
got = uiOpenTypeFeaturesGet(otf, 'a', 'b', 'c', 'd', &value);
|
||||
|
||||
if (!got)
|
||||
testingTErrorf(t, "uiOpenTypeFeaturesGet() failed to get feature we added");
|
||||
else if (value != 12345)
|
||||
testingTErrorf(t, "feature abcd: got %" PRIu32 ", want 12345", value);
|
||||
}
|
||||
|
||||
testingTest(OpenTypeFeaturesRemove)
|
||||
{
|
||||
uiOpenTypeFeatures *otf;
|
||||
uint32_t value;
|
||||
|
||||
otf = uiNewOpenTypeFeatures();
|
||||
testingTDefer(t, freeOpenType, otf);
|
||||
uiOpenTypeFeaturesAdd(otf, 'a', 'b', 'c', 'd', 12345);
|
||||
uiOpenTypeFeaturesRemove(otf, 'a', 'b', 'c', 'd');
|
||||
|
||||
if (uiOpenTypeFeaturesGet(otf, 'a', 'b', 'c', 'd', &value))
|
||||
testingTErrorf(t, "uiOpenTypeFeaturesGet() succeeded in getting deleted feature; value %" PRIu32, value);
|
||||
}
|
||||
|
||||
testingTest(OpenTypeFeaturesCloneAdd)
|
||||
{
|
||||
uiOpenTypeFeatures *otf, *otf2;
|
||||
uint32_t value;
|
||||
|
||||
otf = uiNewOpenTypeFeatures();
|
||||
testingTDefer(t, freeOpenType, otf);
|
||||
uiOpenTypeFeaturesAdd(otf, 'a', 'b', 'c', 'd', 12345);
|
||||
otf2 = uiOpenTypeFeaturesClone(otf);
|
||||
testingTDefer(t, freeOpenType, otf2);
|
||||
uiOpenTypeFeaturesAdd(otf2, 'q', 'w', 'e', 'r', 56789);
|
||||
|
||||
if (uiOpenTypeFeaturesGet(otf, 'q', 'w', 'e', 'r', &value))
|
||||
testingTErrorf(t, "uiOpenTypeFeaturesGet() on original succeeded in getting feature added to clone; value %" PRIu32, value);
|
||||
}
|
||||
|
||||
testingTest(OpenTypeFeaturesCloneModify)
|
||||
{
|
||||
uiOpenTypeFeatures *otf, *otf2;
|
||||
uint32_t value;
|
||||
|
||||
otf = uiNewOpenTypeFeatures();
|
||||
testingTDefer(t, freeOpenType, otf);
|
||||
uiOpenTypeFeaturesAdd(otf, 'a', 'b', 'c', 'd', 12345);
|
||||
otf2 = uiOpenTypeFeaturesClone(otf);
|
||||
testingTDefer(t, freeOpenType, otf2);
|
||||
uiOpenTypeFeaturesAdd(otf2, 'a', 'b', 'c', 'd', 56789);
|
||||
|
||||
uiOpenTypeFeaturesGet(otf, 'a', 'b', 'c', 'd', &value);
|
||||
if (value != 12345)
|
||||
testingTErrorf(t, "uiOpenTypeFeaturesGet() on original: got %" PRIu32 ", want 12345", value);
|
||||
uiOpenTypeFeaturesGet(otf2, 'a', 'b', 'c', 'd', &value);
|
||||
if (value != 56789)
|
||||
testingTErrorf(t, "uiOpenTypeFeaturesGet() on clone: got %" PRIu32 ", want 56789", value);
|
||||
}
|
||||
|
||||
testingTest(OpenTypeFeaturesCloneRemove)
|
||||
{
|
||||
uiOpenTypeFeatures *otf, *otf2;
|
||||
uint32_t value;
|
||||
|
||||
otf = uiNewOpenTypeFeatures();
|
||||
testingTDefer(t, freeOpenType, otf);
|
||||
uiOpenTypeFeaturesAdd(otf, 'a', 'b', 'c', 'd', 12345);
|
||||
otf2 = uiOpenTypeFeaturesClone(otf);
|
||||
testingTDefer(t, freeOpenType, otf2);
|
||||
uiOpenTypeFeaturesRemove(otf2, 'a', 'b', 'c', 'd');
|
||||
|
||||
if (uiOpenTypeFeaturesGet(otf2, 'a', 'b', 'c', 'd', &value))
|
||||
testingTErrorf(t, "uiOpenTypeFeaturesGet() on clone succeeded in getting feature removed from clone; value %" PRIu32, value);
|
||||
if (!uiOpenTypeFeaturesGet(otf, 'a', 'b', 'c', 'd', &value))
|
||||
testingTErrorf(t, "uiOpenTypeFeaturesGet() on original failed to get feature removed from clone");
|
||||
}
|
||||
|
||||
int main(void)
|
||||
{
|
||||
return testingMain();
|
||||
}
|
|
@ -0,0 +1,137 @@
|
|||
// 27 february 2018
|
||||
|
||||
// TODO
|
||||
// - https://blogs.msdn.microsoft.com/oldnewthing/20181107-00/?p=100155 https://blogs.msdn.microsoft.com/oldnewthing/20181108-00/?p=100165 https://blogs.msdn.microsoft.com/oldnewthing/20181109-00/?p=100175
|
||||
// - also in the above: note the unspecified order of data in the sub-segments...
|
||||
|
||||
#ifndef testingprivIncludeGuard_testing_h
|
||||
#define testingprivIncludeGuard_testing_h
|
||||
|
||||
#include <stdarg.h>
|
||||
|
||||
#undef testingprivBadLanguageVersion
|
||||
#ifdef __cplusplus
|
||||
// TODO https://stackoverflow.com/questions/2324658/how-to-determine-the-version-of-the-c-standard-used-by-the-compiler implies this won't do with C++0x-era compilers, and https://wiki.apache.org/stdcxx/C++0xCompilerSupport doesn't talk about va_copy() so a simple version check for the C99 preprocessor may be wrong...
|
||||
// TODO what if __cplusplus is blank (maybe only in that case, since IIRC C++98 requires __cplusplus to have a value)?
|
||||
#if __cplusplus < 201103L
|
||||
#define testingprivBadLanguageVersion
|
||||
#endif
|
||||
#elif !defined(__STDC_VERSION__)
|
||||
#define testingprivBadLanguageVersion
|
||||
#elif __STDC_VERSION__ < 199901L
|
||||
#define testingprivBadLanguageVersion
|
||||
#endif
|
||||
#ifdef testingprivBadLanguageVersion
|
||||
#error sorry, TODO requires either C99 or C++11; cannot continue
|
||||
#endif
|
||||
|
||||
#ifdef __cplusplus
|
||||
extern "C" {
|
||||
#endif
|
||||
|
||||
#ifdef __cplusplus
|
||||
#define testingprivMkScaffold(name) \
|
||||
static inline void testingprivScaffold ## name(testingT *t) \
|
||||
{ \
|
||||
bool failedNow = false, skippedNow = false; \
|
||||
try { name(t); } \
|
||||
catch (testingprivFailNowException e) { failedNow = true; } \
|
||||
catch (testingprivSkipNowException e) { skippedNow = true; } \
|
||||
/* TODO see if we should catch other exceptions too */ \
|
||||
/* don't call these in the catch blocks as they call longjmp() */ \
|
||||
if (failedNow) testingprivTDoFailNow(t); \
|
||||
if (skippedNow) testingprivTDoSkipNow(t); \
|
||||
}
|
||||
#else
|
||||
#define testingprivMkScaffold(name) \
|
||||
static inline void testingprivScaffold ## name(testingT *t) { name(t); }
|
||||
#endif
|
||||
|
||||
// references:
|
||||
// - https://gitlab.gnome.org/GNOME/glib/blob/master/glib/gconstructor.h
|
||||
// - https://gitlab.gnome.org/GNOME/glib/blob/master/gio/glib-compile-resources.c
|
||||
// - https://msdn.microsoft.com/en-us/library/bb918180.aspx
|
||||
#if defined(__cplusplus)
|
||||
#define testingprivMkCtor(name, reg) \
|
||||
static reg ## Class testingprivCtor ## name(#name, testingprivScaffold ## name);
|
||||
#elif defined(__GNUC__)
|
||||
#define testingprivMkCtor(name, reg) \
|
||||
__attribute__((constructor)) static void testingprivCtor ## name(void) { reg(#name, testingprivScaffold ## name); }
|
||||
#elif defined(_MSC_VER)
|
||||
#define testingprivMkCtorPrototype(name, reg) \
|
||||
static int name(void) testingprivCtor ## name(void) { reg(#name, testingprivScaffold ## name); return 0; } \
|
||||
__pragma(section(".CRT$XCU",read)) \
|
||||
__declspec(allocate(".CRT$XCU")) static int (*testingprivCtorPtr ## name)(void) = testingprivCtor ## name;
|
||||
#elif defined(__SUNPRO_C)
|
||||
#define testingprivMkCtor(name, reg) \
|
||||
_Pragma("init(testingprivCtor" #name ")") static void testingprivCtor ## name(void) { reg(#name, testingprivScaffold ## name); }
|
||||
#else
|
||||
#error unknown compiler for making constructors in C; cannot continue
|
||||
#endif
|
||||
|
||||
#define testingTest(Name) \
|
||||
void Test ## Name(testingT *t); \
|
||||
testingprivMkScaffold(Test ## Name) \
|
||||
testingprivMkCtor(Test ## Name, testingprivRegisterTest) \
|
||||
void Test ## Name(testingT *t)
|
||||
|
||||
extern int testingMain(void);
|
||||
|
||||
typedef struct testingT testingT;
|
||||
#define testingTLogf(t, ...) \
|
||||
testingprivExpand(testingprivTLogfThen((void), t, __VA_ARGS__))
|
||||
#define testingTLogvf(t, format, ap) \
|
||||
testingprivTLogvfThen((void), t, format, ap)
|
||||
#define testingTErrorf(t, ...) \
|
||||
testingprivExpand(testingprivTLogfThen(testingTFail, t, __VA_ARGS__))
|
||||
#define testingTErrorvf(t, format, ap) \
|
||||
testingprivTLogvfThen(testingTFail, t, format, ap)
|
||||
#define testingTFatalf(t, ...) \
|
||||
testingprivExpand(testingprivTLogfThen(testingTFailNow, t, __VA_ARGS__))
|
||||
#define testingTFatalvf(t, format, ap) \
|
||||
testingprivTLogvfThen(testingTFailNow, t, format, ap)
|
||||
#define testingTSkipf(t, ...) \
|
||||
testingprivExpand(testingprivTLogfThen(testingTSkipNow, t, __VA_ARGS__))
|
||||
#define testingTSkipvf(t, format, ap) \
|
||||
testingprivTLogvfThen(testingTSkipNow, t, format, ap)
|
||||
extern void testingTFail(testingT *t);
|
||||
#ifdef __cplusplus
|
||||
#define testingTFailNow(t) (throw testingprivFailNowException())
|
||||
#define testingTSkipNow(t) (throw testingprivSkipNowException())
|
||||
#else
|
||||
#define testingTFailNow(t) (testingprivTDoFailNow(t))
|
||||
#define testingTSkipNow(t) (testingprivTDoSkipNow(t))
|
||||
#endif
|
||||
// TODO should the defered function also have t passed to it?
|
||||
extern void testingTDefer(testingT *t, void (*f)(void *data), void *data);
|
||||
|
||||
// TODO IEEE 754 helpers
|
||||
// references:
|
||||
// - https://www.sourceware.org/ml/libc-alpha/2009-04/msg00005.html
|
||||
// - https://gcc.gnu.org/onlinedocs/cpp/Common-Predefined-Macros.html
|
||||
// - https://stackoverflow.com/questions/5085533/is-a-c-preprocessor-identical-to-a-c-preprocessor
|
||||
|
||||
// TODO should __LINE__ arguments use intmax_t or uintmax_t instead of int?
|
||||
extern void testingprivRegisterTest(const char *, void (*)(testingT *));
|
||||
// see https://stackoverflow.com/questions/32399191/va-args-expansion-using-msvc
|
||||
#define testingprivExpand(x) x
|
||||
#define testingprivTLogfThen(then, t, ...) ((testingprivTLogfFull(t, __FILE__, __LINE__, __VA_ARGS__)), (then(t)))
|
||||
#define testingprivTLogvfThen(then, t, format, ap) ((testingprivTLogvfFull(t, __FILE__, __LINE__, format, ap)), (then(t)))
|
||||
extern void testingprivTLogfFull(testingT *, const char *, int, const char *, ...);
|
||||
extern void testingprivTLogvfFull(testingT *, const char *, int, const char *, va_list);
|
||||
extern void testingprivTDoFailNow(testingT *);
|
||||
extern void testingprivTDoSkipNow(testingT *);
|
||||
|
||||
#ifdef __cplusplus
|
||||
}
|
||||
namespace {
|
||||
class testingprivFailNowException {};
|
||||
class testingprivSkipNowException {};
|
||||
class testingprivRegisterTestClass {
|
||||
public:
|
||||
testingprivRegisterTestClass(const char *name, void (*f)(testingT *)) { testingprivRegisterTest(name, f); }
|
||||
};
|
||||
}
|
||||
#endif
|
||||
|
||||
#endif
|
|
@ -0,0 +1,144 @@
|
|||
// 27 february 2018
|
||||
#include <stdio.h>
|
||||
#include <stdlib.h>
|
||||
#include <setjmp.h>
|
||||
#include "testing.h"
|
||||
|
||||
#define testingprivNew(T) ((T *) malloc(sizeof (T)))
|
||||
|
||||
struct defer {
|
||||
void (*f)(void *);
|
||||
void *data;
|
||||
struct defer *next;
|
||||
};
|
||||
|
||||
struct testingT {
|
||||
const char *name;
|
||||
void (*f)(testingT *);
|
||||
int failed;
|
||||
int skipped;
|
||||
jmp_buf returnNowBuf;
|
||||
struct defer *defers;
|
||||
int defersRun;
|
||||
testingT *next;
|
||||
};
|
||||
|
||||
static testingT *tests = NULL;
|
||||
|
||||
void testingprivRegisterTest(const char *name, void (*f)(testingT *))
|
||||
{
|
||||
testingT *t;
|
||||
|
||||
t = testingprivNew(testingT);
|
||||
t->name = name;
|
||||
t->f = f;
|
||||
t->failed = 0;
|
||||
t->skipped = 0;
|
||||
t->defers = NULL;
|
||||
t->defersRun = 0;
|
||||
// TODO add in the order called
|
||||
t->next = tests;
|
||||
tests = t;
|
||||
}
|
||||
|
||||
static void runDefers(testingT *t)
|
||||
{
|
||||
struct defer *d;
|
||||
|
||||
if (t->defersRun)
|
||||
return;
|
||||
t->defersRun = 1;
|
||||
for (d = t->defers; d != NULL; d = d->next)
|
||||
(*(d->f))(d->data);
|
||||
}
|
||||
|
||||
int testingMain(void)
|
||||
{
|
||||
testingT *t;
|
||||
int anyFailed;
|
||||
const char *status;
|
||||
|
||||
// TODO see if this should run if all tests are skipped
|
||||
if (tests == NULL) {
|
||||
fprintf(stderr, "warning: no tests to run\n");
|
||||
// imitate Go here (TODO confirm this)
|
||||
return 0;
|
||||
}
|
||||
|
||||
anyFailed = 0;
|
||||
for (t = tests; t != NULL; t = t->next) {
|
||||
printf("=== RUN %s\n", t->name);
|
||||
if (setjmp(t->returnNowBuf) == 0)
|
||||
(*(t->f))(t);
|
||||
runDefers(t);
|
||||
status = "PASS";
|
||||
if (t->failed) {
|
||||
status = "FAIL";
|
||||
anyFailed = 1;
|
||||
} else if (t->skipped)
|
||||
// note that failed overrides skipped
|
||||
status = "SKIP";
|
||||
printf("--- %s: %s (%s)\n", status, t->name, "TODO");
|
||||
}
|
||||
|
||||
if (anyFailed) {
|
||||
printf("FAIL\n");
|
||||
return 1;
|
||||
}
|
||||
printf("PASS\n");
|
||||
return 0;
|
||||
}
|
||||
|
||||
void testingprivTLogfFull(testingT *t, const char *file, int line, const char *format, ...)
|
||||
{
|
||||
va_list ap;
|
||||
|
||||
va_start(ap, format);
|
||||
testingprivTLogvfFull(t, file, line, format, ap);
|
||||
va_end(ap);
|
||||
}
|
||||
|
||||
void testingprivTLogvfFull(testingT *t, const char *file, int line, const char *format, va_list ap)
|
||||
{
|
||||
// TODO extract filename from file
|
||||
printf("\t%s:%d: ", file, line);
|
||||
// TODO split into lines separated by \n\t\t and trimming trailing empty lines
|
||||
vprintf(format, ap);
|
||||
printf("\n");
|
||||
}
|
||||
|
||||
void testingTFail(testingT *t)
|
||||
{
|
||||
t->failed = 1;
|
||||
}
|
||||
|
||||
static void returnNow(testingT *t)
|
||||
{
|
||||
// run defers before calling longjmp() just to be safe
|
||||
runDefers(t);
|
||||
longjmp(t->returnNowBuf, 1);
|
||||
}
|
||||
|
||||
void testingprivTDoFailNow(testingT *t)
|
||||
{
|
||||
testingTFail(t);
|
||||
returnNow(t);
|
||||
}
|
||||
|
||||
void testingprivTDoSkipNow(testingT *t)
|
||||
{
|
||||
t->skipped = 1;
|
||||
returnNow(t);
|
||||
}
|
||||
|
||||
void testingTDefer(testingT *t, void (*f)(void *data), void *data)
|
||||
{
|
||||
struct defer *d;
|
||||
|
||||
d = testingprivNew(struct defer);
|
||||
d->f = f;
|
||||
d->data = data;
|
||||
// add to the head of the list so defers are run in reverse order of how they were added
|
||||
d->next = t->defers;
|
||||
t->defers = d;
|
||||
}
|
|
@ -0,0 +1,19 @@
|
|||
Proper vertical text support in uiDrawTextLayout was removed because DirectWrite doesn't add this until Windows 8.1 (unless I drop IDWriteTextLayout and do the script analysis myself; TODO consider this possibility).
|
||||
|
||||
On OS X, setting the vertical forms attribute stacks non-vertical scripts in vertical text (rotates each individual glyph) with Core Text, whereas everything else — including Cocoa's text system — rotates entire non-vertical strings. Not sure what to do about this except manually detect which characters to apply the attribute to:
|
||||
http://www.unicode.org/notes/tn22/RobustVerticalLayout.pdf
|
||||
http://www.unicode.org/Public/vertical/revision-17/VerticalOrientation-17.txt
|
||||
|
||||
In addition, with Core Text, the vertical forms attribute vertically centers the vertical glyphs on the bhorizontal baseline, rather than flush with the text. Using the baseline class attribute doesn't seem to work.
|
||||
TODO investigate kCJKVerticalRomanPlacementType
|
||||
|
||||
If readded, this will need to be a layout-wide setting, not a per-character setting. Pango works right this way; the current Pango code doesn't seem to work.
|
||||
|
||||
More links:
|
||||
https://www.w3.org/TR/2012/NOTE-jlreq-20120403/#line-composition
|
||||
https://www.w3.org/TR/REC-CSS2/notes.html
|
||||
|
||||
TODO indicate where in the attributes.c file that block of code should go (or drop it entirely for the reasons listed above)
|
||||
TODO same for ui.h
|
||||
|
||||
TODO vertical carets
|
|
@ -0,0 +1,19 @@
|
|||
case uiAttributeVerticalForms:
|
||||
if (spec->Value != 0) {
|
||||
CFAttributedStringSetAttribute(p->mas, range, kCTVerticalFormsAttributeName, kCFBooleanTrue);
|
||||
// CFAttributedStringSetAttribute(p->mas, range, kCTBaselineClassAttributeName, kCTBaselineClassRoman);
|
||||
// CFAttributedStringSetAttribute(p->mas, range, kCTBaselineClassAttributeName, kCTBaselineClassIdeographicCentered);
|
||||
// CFAttributedStringSetAttribute(p->mas, range, kCTBaselineClassAttributeName, kCTBaselineClassIdeographicLow);
|
||||
// CFAttributedStringSetAttribute(p->mas, range, kCTBaselineClassAttributeName, kCTBaselineClassIdeographicHigh);
|
||||
// CFAttributedStringSetAttribute(p->mas, range, kCTBaselineClassAttributeName, kCTBaselineClassHanging);
|
||||
// CFAttributedStringSetAttribute(p->mas, range, kCTBaselineClassAttributeName, kCTBaselineClassMath);
|
||||
} else {
|
||||
CFAttributedStringSetAttribute(p->mas, range, kCTVerticalFormsAttributeName, kCFBooleanFalse);
|
||||
// CFAttributedStringSetAttribute(p->mas, range, kCTBaselineClassAttributeName, kCTBaselineClassRoman);
|
||||
// CFAttributedStringSetAttribute(p->mas, range, kCTBaselineClassAttributeName, kCTBaselineClassIdeographicCentered);
|
||||
// CFAttributedStringSetAttribute(p->mas, range, kCTBaselineClassAttributeName, kCTBaselineClassIdeographicLow);
|
||||
// CFAttributedStringSetAttribute(p->mas, range, kCTBaselineClassAttributeName, kCTBaselineClassIdeographicHigh);
|
||||
// CFAttributedStringSetAttribute(p->mas, range, kCTBaselineClassAttributeName, kCTBaselineClassHanging);
|
||||
// CFAttributedStringSetAttribute(p->mas, range, kCTBaselineClassAttributeName, kCTBaselineClassMath);
|
||||
}
|
||||
break;
|
|
@ -0,0 +1,9 @@
|
|||
PangoGravity gravity;
|
||||
|
||||
case uiAttributeVerticalForms:
|
||||
gravity = PANGO_GRAVITY_SOUTH;
|
||||
if (spec->Value != 0)
|
||||
gravity = PANGO_GRAVITY_EAST;
|
||||
addattr(p, start, end,
|
||||
pango_attr_gravity_new(gravity));
|
||||
break;
|
|
@ -0,0 +1,15 @@
|
|||
uint32_t vertval;
|
||||
|
||||
case uiAttributeVerticalForms:
|
||||
// LONGTERM 8 and/or 8.1 add other methods for vertical text
|
||||
op.p = p;
|
||||
op.start = start;
|
||||
op.end = end;
|
||||
vertval = 0;
|
||||
if (spec->Value != 0)
|
||||
vertval = 1;
|
||||
doOpenType("vert", vertval, &op);
|
||||
doOpenType("vrt2", vertval, &op);
|
||||
doOpenType("vkrn", vertval, &op);
|
||||
doOpenType("vrtr", vertval, &op);
|
||||
break;
|
|
@ -0,0 +1,2 @@
|
|||
case uiAttributeVerticalForms:
|
||||
return boolsEqual(attr, spec);
|
|
@ -0,0 +1,18 @@
|
|||
next = "vertical glyph forms";
|
||||
start = uiAttributedStringLen(attrstr);
|
||||
end = start + strlen(next);
|
||||
uiAttributedStringAppendUnattributed(attrstr, next);
|
||||
spec.Type = uiAttributeVerticalForms;
|
||||
spec.Value = 1;
|
||||
uiAttributedStringSetAttribute(attrstr, &spec, start, end);
|
||||
uiAttributedStringAppendUnattributed(attrstr, " (which you can draw rotated for proper vertical text; for instance, ");
|
||||
next = "\xE3\x81\x82\xE3\x81\x84\xE3\x81\x86\xE3\x81\x88\xE3\x81\x8A";
|
||||
start = uiAttributedStringLen(attrstr);
|
||||
end = start + strlen(next);
|
||||
uiAttributedStringAppendUnattributed(attrstr, next);
|
||||
spec.Type = uiAttributeVerticalForms;
|
||||
spec.Value = 1;
|
||||
uiAttributedStringSetAttribute(attrstr, &spec, start, end);
|
||||
uiAttributedStringAppendUnattributed(attrstr, ")");
|
||||
|
||||
uiAttributedStringAppendUnattributed(attrstr, ", ");
|
|
@ -0,0 +1,2 @@
|
|||
// TODO rename to uiAttributeVertical?
|
||||
uiAttributeVerticalForms, // 0 = off, 1 = on
|
|
@ -0,0 +1,25 @@
|
|||
https://twitter.com/OS2World/status/983822011389620224
|
||||
Hi. I recommend you today to start with OS/2 Warp 4.52 or ArcaOS ( The tools are almost the same of Warp 3). EDM/2 is a good place to start: http://www.edm2.com
|
||||
https://twitter.com/OS2World/status/983822594465034240
|
||||
There is also an RPM with OS/2 Software (https://www.arcanoae.com/resources/downloadables/arca-noae-package-manager/ …), and you can get from it some parts of the OS/2 Toolkit. If you want to develop drivers you require the OS/2 Device Driver Kit. (that is more complex). You can also develop in Qt4 and compile things with gcc.
|
||||
http://www.edm2.com/index.php/Main_Page
|
||||
https://www.arcanoae.com/resources/downloadables/arca-noae-package-manager/
|
||||
http://www.edm2.com/index.php/IBM_OS/2_Toolkit_Documentation
|
||||
https://www.os2world.com/forum/index.php?topic=953.0
|
||||
https://www.google.com/search?client=firefox-b-1&ei=QIPPWurPLs7OwAK65K24BA&q=%22OS%2F2+Toolkit%22+site%3Aamazon.com&oq=%22OS%2F2+Toolkit%22+site%3AAmazon.com&gs_l=psy-ab.3...160814.161293.0.161540.2.2.0.0.0.0.128.252.0j2.2.0....0...1c.1.64.psy-ab..0.0.0....0.itT9Og6hC5c
|
||||
http://www.edm2.com/index.php/List_of_Presentation_Manager_Articles
|
||||
https://www.ecsoft2.org/
|
||||
http://www.edm2.com/index.php/Cairo
|
||||
http://www.edm2.com/index.php/Doodle
|
||||
http://www.edm2.com/index.php/Workplace_Shell_Toolkit
|
||||
http://wpstk.netlabs.org/en/site/index.xml
|
||||
https://en.wikipedia.org/wiki/Workplace_Shell
|
||||
https://www.google.com/search?q=OS2+alphablending&ie=utf-8&oe=utf-8&client=firefox-b-1
|
||||
http://www.edm2.com/index.php/List_of_Multimedia_Articles
|
||||
alphablending:
|
||||
http://www.osnews.com/story/369/Review-eComStation-OS2-1.0/page3/
|
||||
http://halfos.ru/documentation/33-os2-api-documentation/67-opengl-os2-developer-reference-guide.html
|
||||
http://www.altools.com/ALTools/ALSee/ALSee-Image-Viewer.aspx
|
||||
http://www.os2voice.org/vnewsarc/bn2007122.html
|
||||
http://www.mozillazine.org/talkback.html?article=194
|
||||
https://books.google.com/books?id=9cpU5uYCzq4C&pg=PA202&lpg=PA202&dq=%22OS/2%22+alphablending&source=bl&ots=uatEop2jAL&sig=HAa_ofQSKsk6-8tBR6YZ6MRJG_0&hl=en&sa=X&ved=0ahUKEwiDq5HukLbaAhUk8IMKHR7aCw4Q6AEIWTAI#v=onepage&q=%22OS%2F2%22%20alphablending&f=false
|
|
@ -0,0 +1,76 @@
|
|||
multi-platform {
|
||||
https://docs.microsoft.com/en-us/azure/devops/pipelines/get-started-multiplatform?view=azure-devops
|
||||
}
|
||||
|
||||
microsoft-hosted agents {
|
||||
https://docs.microsoft.com/en-us/azure/devops/pipelines/agents/hosted?view=azure-devops&tabs=yaml#use-a-microsoft-hosted-agent
|
||||
https://docs.microsoft.com/en-us/azure/devops/pipelines/agents/v2-windows?view=azure-devops
|
||||
https://github.com/Microsoft/azure-pipelines-image-generation/blob/master/images/win/Vs2017-Server2016-Readme.md
|
||||
https://github.com/Microsoft/azure-pipelines-image-generation/blob/master/images/win/Vs2015-Server2012R2-Readme.md
|
||||
}
|
||||
|
||||
maximum number of processors (for ninja -j) {
|
||||
msbuild supports this with the -m option natively; meanwhile:
|
||||
https://www.gnu.org/software/coreutils/manual/html_node/nproc-invocation.html
|
||||
}
|
||||
|
||||
potentially useful tasks {
|
||||
https://docs.microsoft.com/en-us/azure/devops/pipelines/tasks/utility/publish-pipeline-artifact?view=azure-devops
|
||||
https://docs.microsoft.com/en-us/azure/devops/pipelines/tasks/build/visual-studio-build?view=azure-devops
|
||||
https://docs.microsoft.com/en-us/azure/devops/pipelines/tasks/build/xcode?view=azure-devops
|
||||
https://docs.microsoft.com/en-us/azure/devops/pipelines/tasks/utility/github-release?view=azure-devops
|
||||
https://docs.microsoft.com/en-us/azure/devops/extend/develop/add-build-task?view=azure-devops
|
||||
}
|
||||
|
||||
meson CI reference {
|
||||
https://github.com/mesonbuild/meson/blob/master/docs/markdown/Continuous-Integration.md
|
||||
}
|
||||
|
||||
job templates, which will clean up our pipelines {
|
||||
https://docs.microsoft.com/en-us/azure/devops/pipelines/process/templates?view=azure-devops
|
||||
}
|
||||
|
||||
migrating from travis {
|
||||
https://docs.microsoft.com/en-us/azure/devops/pipelines/migrate/from-travis?view=azure-devops
|
||||
}
|
||||
|
||||
32-bit {
|
||||
https://github.com/numpy/numpy/issues/12856
|
||||
https://github.com/numpy/numpy/pull/12863/files
|
||||
https://github.com/numpy/numpy/blob/master/azure-pipelines.yml (also explains what the @s mean; thanks guys)
|
||||
}
|
||||
|
||||
using visual studio tools {
|
||||
https://docs.microsoft.com/en-us/dotnet/framework/tools/developer-command-prompt-for-vs
|
||||
https://docs.microsoft.com/en-us/cpp/build/building-on-the-command-line?view=vs-2019#developer_command_file_locations
|
||||
}
|
||||
|
||||
OLD using visual studio tools; also some docker {
|
||||
https://github.com/mesonbuild/meson/blob/master/ci/azure-steps.yml
|
||||
https://github.com/reactiveui/Akavache/blob/master/azure-pipelines.yml
|
||||
https://github.com/vector-of-bool/CMakeCM/blob/master/azure-pipelines.yml
|
||||
https://docs.microsoft.com/en-us/visualstudio/install/advanced-build-tools-container?view=vs-2019
|
||||
https://docs.microsoft.com/en-us/visualstudio/install/build-tools-container?view=vs-2019
|
||||
https://devblogs.microsoft.com/cppblog/using-msvc-in-a-docker-container-for-your-c-projects/
|
||||
https://devblogs.microsoft.com/cppblog/finding-the-visual-c-compiler-tools-in-visual-studio-2017/
|
||||
}
|
||||
|
||||
reference {
|
||||
https://docs.microsoft.com/en-us/azure/devops/pipelines/yaml-schema?view=azure-devops&tabs=schema#task
|
||||
https://docs.microsoft.com/en-us/azure/devops/pipelines/apps/windows/cpp?view=azure-devops
|
||||
https://docs.microsoft.com/en-us/azure/devops/pipelines/languages/python?view=azure-devops
|
||||
https://docs.microsoft.com/en-us/azure/devops/pipelines/process/conditions?view=azure-devops&tabs=yaml
|
||||
}
|
||||
|
||||
C++ binary compatibility and shared libraries (TODO split into its own notes file) {
|
||||
https://docs.microsoft.com/en-us/cpp/porting/binary-compat-2015-2017?view=vs-2017
|
||||
}
|
||||
|
||||
others I forgot why I had them open {
|
||||
https://github.com/Microsoft/cpprestsdk/blob/master/azure-pipelines.yml (32-bit?)
|
||||
https://docs.microsoft.com/en-us/azure/devops/release-notes/2018/sprint-142-update (I forget, maybe also 32-bit?)
|
||||
}
|
||||
|
||||
others that might not be relevant {
|
||||
https://github.com/Microsoft/azure-pipelines-agent/blob/master/docs/start/envubuntu.md has instructions on how to build the agent tool itself
|
||||
}
|
|
@ -0,0 +1 @@
|
|||
UWP has this (TODO check its implementation to see if it matches ours) https://docs.microsoft.com/en-us/uwp/api/windows.ui.viewmanagement.uisettings#Windows_UI_ViewManagement_UISettings_CaretWidth
|
|
@ -0,0 +1 @@
|
|||
https://blogs.msdn.microsoft.com/oldnewthing/20181226-00/?p=100565
|
|
@ -0,0 +1,6 @@
|
|||
https://developer.apple.com/library/mac/documentation/AppKit/Reference/NSLayoutConstraint_Class/
|
||||
https://developer.apple.com/documentation/uikit/nslayoutconstraint?language=objc
|
||||
https://developer.apple.com/library/mac/documentation/UserExperience/Conceptual/AutolayoutPG/ProgrammaticallyCreatingConstraints.html#//apple_ref/doc/uid/TP40010853-CH16-SW1
|
||||
https://developer.apple.com/library/content/documentation/UserExperience/Conceptual/AutolayoutPG/ProgrammaticallyCreatingConstraints.html#//apple_ref/doc/uid/TP40010853-CH16-SW1 (Listing 13-3)
|
||||
https://developer.apple.com/library/mac/documentation/UserExperience/Conceptual/AutolayoutPG/WorkingwithScrollViews.html#//apple_ref/doc/uid/TP40010853-CH24-SW1
|
||||
https://developer.apple.com/library/content/documentation/UserExperience/Conceptual/AutolayoutPG/WorkingwithScrollViews.html#//apple_ref/doc/uid/TP40010853-CH24-SW1 ("IMPORTANT Your layout must fully define..." large box)
|
|
@ -0,0 +1,8 @@
|
|||
https://www.google.com/search?q=nsalert+error+icon&client=firefox-b&tbm=isch&source=iu&ictx=1&fir=2iRctS5fJByN0M%253A%252Cw324MTzjHa1bAM%252C_&usg=__x3wpwdNN1L8VI2kHtkKAXFMtpj4%3D&sa=X&ved=0ahUKEwjJzpjN2qDZAhVjw1kKHfOHDoQQ9QEIMTAB#imgrc=2iRctS5fJByN0M:
|
||||
http://0xced.blogspot.com/2009/11/clalert-nsalert-done-right.html
|
||||
https://gist.github.com/0xced/228140
|
||||
http://editra.org/uploads/code/artmac.html
|
||||
http://mirror.informatimago.com/next/developer.apple.com/documentation/Carbon/Reference/IconServices/index.html
|
||||
http://www.cocoabuilder.com/archive/cocoa/15427-iconref-to-nsimage.html
|
||||
https://github.com/lukakerr/Swift-NSUserNotificationPrivate
|
||||
https://stackoverflow.com/questions/32943220/the-sidebar-icon-image-name-in-osx
|
|
@ -0,0 +1,2 @@
|
|||
https://github.com/kusti8/proton-native/issues/47#issuecomment-373068947
|
||||
https://blogs.kde.org/2009/03/26/how-crash-almost-every-qtkde-application-and-how-fix-it-0
|
|
@ -0,0 +1 @@
|
|||
High DPI Displays | Qt 5.5 http://doc.qt.io/qt-5/highdpi.html bottom of page(?)
|
|
@ -0,0 +1,16 @@
|
|||
https://msdn.microsoft.com/en-us/library/windows/desktop/dd319079(v=vs.85).aspx
|
||||
https://msdn.microsoft.com/en-us/library/windows/desktop/dd318103(v=vs.85).aspx
|
||||
https://stackoverflow.com/questions/4663855/is-there-a-repository-for-localized-common-text-in-winforms
|
||||
https://stackoverflow.com/questions/2502375/find-localized-windows-strings
|
||||
https://docs.microsoft.com/en-us/windows-hardware/customize/mobile/mcsf/create-a-resource-only-dll-for-localized-strings
|
||||
https://msdn.microsoft.com/en-us/library/windows/desktop/ee845043(v=vs.85).aspx
|
||||
https://msdn.microsoft.com/en-us/library/cc194807.aspx
|
||||
https://www.codeproject.com/Articles/10542/Easily-Load-and-Format-Strings-from-the-String-Tab
|
||||
https://www.codeproject.com/Tips/431045/The-inner-working-of-FindResource-and-LoadString-W
|
||||
https://mihai-nita.net/2007/05/03/how-to-localize-an-rc-file/
|
||||
https://www.microsoft.com/en-us/language
|
||||
https://www.microsoft.com/en-us/language/Terminology
|
||||
https://www.microsoft.com/en-us/language/LicenseAgreement
|
||||
https://www.microsoft.com/en-us/language/Translations
|
||||
http://www.ttt.org/oscarstandards/tbx/
|
||||
https://blogs.msdn.microsoft.com/oldnewthing/20181122-00/?p=100295
|
|
@ -0,0 +1,23 @@
|
|||
https://developer.apple.com/documentation/corefoundation/1542764-cfurlcopyresourcepropertyforkey?language=objc
|
||||
https://developer.apple.com/documentation/foundation/nsargumentdomain?language=objc
|
||||
https://developer.apple.com/documentation/appkit/1428499-nsapplicationmain
|
||||
https://webkit.googlesource.com/WebKit/+/9ae3deefb7df48bd85f01edcf8382ee300eafbd4%5E!/
|
||||
https://stackoverflow.com/questions/11181324/how-to-change-retina-display-system-preferences-in-osx
|
||||
https://web.archive.org/web/20150828053709/http://garethjenkins.com/2012/07/01/investigating-a-high-resolution-retina-utility-for-macbook-pro-1x-and-2x-modes/
|
||||
https://github.com/avibrazil/RDM
|
||||
https://www.google.com/search?client=firefox-b-1-d&q=IOFramebuffer+subclass
|
||||
https://developer.apple.com/documentation/kernel/ioframebuffer?language=objc
|
||||
https://stackoverflow.com/questions/51846999/how-to-write-macos-display-driver
|
||||
https://twitter.com/kenkeiter/status/3631378994298882
|
||||
http://en.ennowelbers.info/node/33
|
||||
https://www.insanelymac.com/forum/topic/114528-how-to-compile-a-driver-from-source/
|
||||
https://github.com/mkernel/EWProxyFramebuffer
|
||||
https://stackoverflow.com/questions/46904493/ioframebuffer-cant-access-vram-framebuffer-in-macos-10-13
|
||||
https://www.google.com/search?client=firefox-b-1-d&q=IOFramebuffer+10.13
|
||||
https://github.com/codykrieger/gfxCardStatus/issues/296
|
||||
https://plugable.com/2018/03/30/macos-10-13-4-disables-displaylink-duet-display-devices/
|
||||
https://www.tekrevue.com/tip/hidpi-mode-os-x/
|
||||
https://medium.com/@ivan.ha/how-to-mimic-a-2k-monitor-as-retina-display-in-macos-sierra-using-hidpi-f53d87630c48
|
||||
https://www.google.com/search?client=firefox-b-1-d&q=IOFramebuffer+10.13
|
||||
https://www.quora.com/What-is-the-underlying-reason-if-DisplayLink-is-no-longer-working-properly-on-Mac-OS
|
||||
https://github.com/Siguza/iokit-utils
|
|
@ -0,0 +1,213 @@
|
|||
windows data types, "open specifications" on msdn https://msdn.microsoft.com/en-us/library/cc230321.aspx
|
||||
(I usually use https://msdn.microsoft.com/en-us/library/windows/desktop/aa383751(v=vs.85).aspx for windows data types)
|
||||
|
||||
windows platform update for windows 7
|
||||
https://msdn.microsoft.com/en-us/library/windows/desktop/jj863687(v=vs.85).aspx
|
||||
https://msdn.microsoft.com/en-us/library/windows/desktop/hh802478(v=vs.85).aspx
|
||||
https://msdn.microsoft.com/en-us/library/windows/desktop/hh802480(v=vs.85).aspx
|
||||
|
||||
DWM, header bars, toolbars
|
||||
https://stackoverflow.com/questions/41106347/why-is-my-dwmextendframeintoclientaread-window-not-drawing-the-dwm-borders/41125616#41125616
|
||||
https://developer.gnome.org/hig/stable/header-bars.html.en
|
||||
https://developer.apple.com/library/content/documentation/UserExperience/Conceptual/OSXHIGuidelines/WindowTitleBarToolbar.html#//apple_ref/doc/uid/20000957-CH39-SW1
|
||||
https://developer.apple.com/library/content/documentation/UserExperience/Conceptual/OSXHIGuidelines/ControlsAll.html#//apple_ref/doc/uid/20000957-CH46-SW2
|
||||
https://msdn.microsoft.com/en-us/library/windows/desktop/bb787329(v=vs.85).aspx
|
||||
https://msdn.microsoft.com/en-us/library/windows/desktop/bb787334(v=vs.85).aspx
|
||||
https://msdn.microsoft.com/en-us/library/windows/desktop/bb787337(v=vs.85).aspx
|
||||
https://msdn.microsoft.com/en-us/library/windows/desktop/cc835034(v=vs.85).aspx
|
||||
https://msdn.microsoft.com/en-us/library/windows/desktop/bb787345(v=vs.85).aspx
|
||||
|
||||
rendering
|
||||
https://www.youtube.com/watch?v=UUfXWzp0-DU
|
||||
|
||||
text input on windows
|
||||
https://blogs.msdn.microsoft.com/oldnewthing/20121025-00/?p=6253
|
||||
https://www.google.com/search?q=translatemessage+site%3Ahttp%3A%2F%2Farchives.miloush.net%2Fmichkap%2Farchive%2F&ie=utf-8&oe=utf-8
|
||||
http://archives.miloush.net/michkap/archive/2008/04/22/8415843.html
|
||||
http://archives.miloush.net/michkap/archive/2007/03/25/1948887.html
|
||||
http://archives.miloush.net/michkap/archive/2006/09/10/748775.html
|
||||
http://archives.miloush.net/michkap/archive/2004/11/27/270931.html
|
||||
https://stackoverflow.com/questions/41334851/since-translatemessage-returns-nonzero-unconditionally-how-can-i-tell-either
|
||||
http://stackoverflow.com/questions/41334851/since-translatemessage-returns-nonzero-unconditionally-how-can-i-tell-either?noredirect=1#comment70107257_41334851
|
||||
|
||||
text layouts
|
||||
https://developer.apple.com/reference/coretext/2110184-ctframe?language=objc
|
||||
https://msdn.microsoft.com/en-us/library/windows/desktop/dd368203(v=vs.85).aspx
|
||||
https://msdn.microsoft.com/en-us/library/windows/desktop/dd368205(v=vs.85).aspx
|
||||
https://developer.gnome.org/pango/1.30/pango-Layout-Objects.html#pango-layout-set-font-description
|
||||
https://developer.gnome.org/pango/1.30/pango-Fonts.html#PangoWeight-enum
|
||||
https://git.gnome.org/browse/pango/tree/pango/pangocoretext-fontmap.c (code for small caps)
|
||||
https://bugzilla.gnome.org/show_bug.cgi?id=766148
|
||||
https://developer.apple.com/documentation/coretext/ctline?preferredLanguage=occ
|
||||
https://developer.apple.com/reference/coretext/1508876-ctlinegetstringindexforposition?language=objc
|
||||
https://developer.apple.com/library/content/documentation/StringsTextFonts/Conceptual/CoreText_Programming/Introduction/Introduction.html#//apple_ref/doc/uid/TP40005533-CH1-SW1
|
||||
https://developer.apple.com/reference/coretext/2110184-ctframe?language=objc
|
||||
https://developer.apple.com/reference/coretext/2168885-ctline?language=objc
|
||||
https://developer.apple.com/library/content/documentation/StringsTextFonts/Conceptual/CoreText_Programming/LayoutOperations/LayoutOperations.html
|
||||
http://asciiwwdc.com/2013/sessions/205
|
||||
https://developer.apple.com/reference/coretext/1511332-ctlinegetboundswithoptions?language=objc
|
||||
https://stackoverflow.com/questions/19709751/ctlinegetboundswithoptions-what-does-the-returned-frame-origin-y-value-mean?rq=1
|
||||
https://imgur.com/a/lqhzC
|
||||
https://imgur.com/a/hYeOL
|
||||
https://www.google.com/search?q=ctline+%22paragraph+spacing%22&ie=utf-8&oe=utf-8
|
||||
http://stackoverflow.com/questions/8010615/how-do-i-determine-the-of-a-ctline-following-a-ctline
|
||||
https://imgur.com/a/dlpm2
|
||||
https://stackoverflow.com/questions/41601845/am-i-missing-something-in-core-text-that-will-let-me-see-which-line-a-certain-po
|
||||
https://www.google.com/search?q=%22core+text%22+%22combining+character%22&oq=%22core+text%22+%22combining+character%22&gs_l=serp.3...2526898.2528158.0.2528485.21.10.0.0.0.0.216.997.5j3j1.9.0....0...1c.1.64.serp..12.8.904...33i21k1j33i160k1.J69jsB9g0N0
|
||||
https://github.com/macvim-dev/macvim/issues/400#issuecomment-274292877
|
||||
https://bugs.webkit.org/show_bug.cgi?id=68287
|
||||
https://trac.webkit.org/changeset/95391
|
||||
https://trac.webkit.org/changeset/95391/trunk/Source/WebCore/platform/graphics/mac/ComplexTextControllerCoreText.cpp
|
||||
http://stackoverflow.com/questions/41857213/is-there-any-way-i-can-get-precise-metrics-line-ascent-line-descent-etc-of
|
||||
http://pawlan.com/monica/articles/texttutorial/int.html
|
||||
|
||||
enter in entry fields, possibly other text layout issues, possibly keyboard shortcuts
|
||||
https://developer.gnome.org/gtk3/3.10/GtkEntry.html "activate" signal
|
||||
https://gitlab.gnome.org/GNOME/gtk/blob/gtk-3-10/gtk/gtkentry.c (not sure where)
|
||||
https://developer.gnome.org/gtk3/3.10/GtkTextView.html signals section of contents
|
||||
https://gitlab.gnome.org/GNOME/gtk/blob/gtk-3-10/gtk/gtktextview.c (not sure where; closed before I could find out again though gitlab might wreck it anyway)
|
||||
https://developer.gnome.org/gtk3/3.10/gtk3-Bindings.html
|
||||
https://gitlab.gnome.org/GNOME/gtk/blob/gtk-3-10/gtk/gtkwidget.c (not sure where)
|
||||
https://gitlab.gnome.org/GNOME/gtk/blob/gtk-3-10/gtk/gtkbindings.c (not sure where)
|
||||
https://gitlab.gnome.org/GNOME/gnome-builder/tree/master/libide/keybindings/ide-keybindings.c 404; TODO find the original cgit link in the chat logs to see what hergertme wanted me to see
|
||||
https://gitlab.gnome.org/GNOME/gnome-builder/tree/master/libide/sourceview/ide-source-view.c likewise
|
||||
https://gitlab.gnome.org/GNOME/gnome-builder/tree/master/libide/sourceview/ide-source-view-mode.c#n323 likewise
|
||||
|
||||
rgb int->float conversion
|
||||
https://stackoverflow.com/questions/41348339/how-to-convert-rgb-to-hexadecimal-using-gtk?noredirect=1#comment69903262_41348339
|
||||
|
||||
windows fonts, hi-dpi
|
||||
https://stackoverflow.com/questions/41448320/dlgtemplateex-and-ds-shellfont-what-about-point-size
|
||||
|
||||
windows default fonts
|
||||
https://stackoverflow.com/questions/41505151/how-to-draw-text-with-the-default-ui-font-in-directwrite#41505750
|
||||
|
||||
uwp stuff
|
||||
https://docs.microsoft.com/en-us/windows/uwp/design/controls-and-patterns/inking-controls
|
||||
https://msdn.microsoft.com/en-us/windows/uwp/controls-and-patterns/master-details
|
||||
|
||||
cmake stuff
|
||||
https://public.kitware.com/pipermail/cmake/2010-January/034669.html
|
||||
https://cmake.org/cmake/help/v3.0/command/string.html
|
||||
https://cmake.org/pipermail/cmake/2003-April/003599.html
|
||||
|
||||
opentype features and locales
|
||||
https://www.microsoft.com/typography/otspec/featurelist.htm
|
||||
https://en.wikipedia.org/wiki/List_of_typographic_features
|
||||
https://www.microsoft.com/typography/otspec160/scripttags.htm
|
||||
https://msdn.microsoft.com/en-us/library/windows/desktop/dd371503(v=vs.85).aspx
|
||||
|
||||
gspell (TODO figure out what I wanted from this; possibly spelling checking)
|
||||
https://git.gnome.org/browse/gspell/tree/gspell/gspell-inline-checker-text-buffer.c
|
||||
https://git.gnome.org/browse/gtk+/tree/gtk/gtktextview.c?h=gtk-3-10
|
||||
|
||||
other assorted notes on text
|
||||
https://mail.gnome.org/archives/gtk-devel-list/2016-March/msg00037.html
|
||||
https://mail.gnome.org/archives/gtk-devel-list/2016-June/msg00007.html
|
||||
https://blogs.msdn.microsoft.com/tsfaware/
|
||||
https://stackoverflow.com/questions/40453186/what-is-the-correct-modern-way-to-handle-arbitrary-text-input-in-a-custom-contr
|
||||
https://www.microsoft.com/typography/fonts/family.aspx
|
||||
|
||||
windows ribbon
|
||||
https://twitter.com/_Ninji/status/814918189708668929 (C08rw9TWgAMpLkC.jpg:large)
|
||||
|
||||
https://developer.apple.com/library/content/samplecode/CocoaTipsAndTricks/Introduction/Intro.html#//apple_ref/doc/uid/DTS40010039 BlurryView and StaticObjcMethods in particular
|
||||
printing https://developer.apple.com/library/content/samplecode/PMPrinterPrintWithFile/Introduction/Intro.html#//apple_ref/doc/uid/DTS10003958
|
||||
auto layout https://developer.apple.com/library/content/releasenotes/UserExperience/RNAutomaticLayout/index.html#//apple_ref/doc/uid/TP40010631
|
||||
|
||||
other stuff, mostly custom draw on windows
|
||||
https://github.com/pauldotknopf/WindowsSDK7-Samples/blob/master/multimedia/DirectWrite/ChooseFont/ChooseFont.cpp at ChooseFontDialog::OnFontSizeNameEdit() definition
|
||||
https://developer.gnome.org/gtk3/3.10/GtkColorButton.html
|
||||
https://msdn.microsoft.com/en-us/library/windows/desktop/bb775951(v=vs.85).aspx
|
||||
https://msdn.microsoft.com/en-us/library/windows/desktop/bb775951(v=vs.85).aspx between BS_LEFTTEXT and BS_RADIOBUTTON
|
||||
https://msdn.microsoft.com/en-us/library/windows/desktop/bb775923(v=vs.85).aspx
|
||||
https://msdn.microsoft.com/en-us/library/windows/desktop/bb775923(v=vs.85).aspx
|
||||
https://msdn.microsoft.com/en-us/library/windows/desktop/bb775802(v=vs.85).aspx
|
||||
https://msdn.microsoft.com/en-us/library/windows/desktop/bb775802(v=vs.85).aspx
|
||||
https://msdn.microsoft.com/en-us/library/windows/desktop/bb761837(v=vs.85).aspx
|
||||
https://msdn.microsoft.com/en-us/library/windows/desktop/bb761837(v=vs.85).aspx
|
||||
https://msdn.microsoft.com/en-us/library/windows/desktop/bb761847(v=vs.85).aspx
|
||||
https://msdn.microsoft.com/en-us/library/windows/desktop/bb761847(v=vs.85).aspx description + parameters
|
||||
https://msdn.microsoft.com/en-us/library/windows/desktop/bb775483(v=vs.85).aspx
|
||||
https://msdn.microsoft.com/en-us/library/windows/desktop/bb775483(v=vs.85).aspx dwItemSpec parameter
|
||||
https://msdn.microsoft.com/en-us/library/windows/desktop/bb775951(v=vs.85).aspx#BS_NOTIFY
|
||||
https://msdn.microsoft.com/en-us/library/windows/desktop/bb775951(v=vs.85).aspx#BS_NOTIFY BS_NOTIFY to BS_RIGHT
|
||||
http://stackoverflow.com/questions/17678261/how-to-change-color-of-a-button-while-keeping-buttons-functions-in-win32
|
||||
https://msdn.microsoft.com/en-us/library/windows/desktop/ff919569(v=vs.85).aspx
|
||||
https://msdn.microsoft.com/en-us/library/windows/desktop/ff919569(v=vs.85).aspx
|
||||
https://msdn.microsoft.com/en-us/library/windows/desktop/ff919573(v=vs.85).aspx
|
||||
https://msdn.microsoft.com/en-us/library/windows/desktop/ff919573(v=vs.85).aspx switch (pnm->hdr.code){
|
||||
http://stackoverflow.com/questions/36756094/will-the-device-context-of-a-child-window-have-the-same-text-metrics-and-text-ex
|
||||
actually I think most if not all of these were from when I was trying to implement uiColorButton on Windows, so I'm not sure if I still need these, but meh...
|
||||
|
||||
more miscellaneous
|
||||
https://blogs.msdn.microsoft.com/oldnewthing/20160328-00/?p=93204 from "One is virtual memory" to "occured to them that their"
|
||||
https://blogs.msdn.microsoft.com/oldnewthing/20160331-00/?p=93231
|
||||
https://msdn.microsoft.com/en-us/library/windows/desktop/aa373631(v=vs.85).aspx
|
||||
https://msdn.microsoft.com/en-us/library/windows/desktop/ms648395(v=vs.85).aspx
|
||||
https://msdn.microsoft.com/en-us/library/windows/desktop/ms648395(v=vs.85).aspx
|
||||
https://blogs.msdn.microsoft.com/oldnewthing/20101118-00/?p=12253#comment-875273 "MTA implies poor service and fare hikes."
|
||||
|
||||
from #gtk+
|
||||
[18:06:48] <zorcon> if i am doing an animation that needs to be updated at every duration(say every 50ms), should i use get_frame_time() from the frameclock to do that? (from add_tick_callback())
|
||||
[18:12:47] <~Company> zorcon: yes
|
||||
|
||||
windows stuff: mostly aero tricks, sdk version macro notes, ribbon stuff, scrollbar stuff too
|
||||
https://tools.stefankueng.com/SendMessage.html
|
||||
https://sourceforge.net/p/sendmessage/code/HEAD/tree/trunk/src/SendMessage.vcxproj
|
||||
https://sourceforge.net/p/sendmessage/code/HEAD/tree/trunk/src/stdafx.h
|
||||
https://sourceforge.net/p/sendmessage/code/HEAD/tree/trunk/src/MainDlg.h
|
||||
https://sourceforge.net/u/steveking/profile/
|
||||
https://github.com/stefankueng/BowPad/tree/master/src + sourceforge original
|
||||
https://github.com/stefankueng/sktoolslib/blob/master/AeroControls.cpp + sourceforge original
|
||||
https://tools.stefankueng.com/BowPad.html
|
||||
https://www.codeproject.com/Articles/1084/Custom-Scrollbar-Library-version-1-1#_articleTop
|
||||
https://www.google.com/search?client=firefox-b-1&biw=1080&bih=600&ei=gUHRWsrdMYayggf30bfoAw&q=%22aerocontrols.h%22&oq=%22aerocontrols.h%22&gs_l=psy-ab.3..35i39k1l6.1816.3367.0.3413.4.2.0.0.0.0.0.0..1.0....0...1c.1.64.psy-ab..3.1.211.6...211.0Ppw_OREAk0
|
||||
https://searchcode.com/codesearch/view/7295148/
|
||||
https://github.com/TortoiseGit/tortoisesvn/blob/master/src/Utils/MiscUI/AeroBaseDlg.h
|
||||
https://github.com/TortoiseGit/tortoisesvn/blob/master/src/Utils/MiscUI/AeroControls.cpp
|
||||
https://github.com/gavioto/stexbar/blob/master/Misc/FileTool/src/CleanVerifyDlg.h
|
||||
|
||||
windows ribbon colors and dwm customization
|
||||
https://github.com/stefankueng/BowPad/blob/master/src/MainWindow.cpp
|
||||
https://msdn.microsoft.com/en-us/library/windows/desktop/dd940487(v=vs.85).aspx
|
||||
https://github.com/yohei-yoshihara/GameOfLife3D/blob/master/GameOfLife3DLib/gameoflife3d/Ribbon.cpp
|
||||
https://msdn.microsoft.com/en-us/library/windows/desktop/dd940404(v=vs.85).aspx
|
||||
https://github.com/stefankueng/sktoolslib/blob/master/AeroColors.cpp
|
||||
https://gist.github.com/emoacht/bfa852ccc16bdb5465bd
|
||||
https://stackoverflow.com/questions/4258295/aero-how-to-draw-solid-opaque-colors-on-glass
|
||||
https://github.com/ComponentFactory/Krypton
|
||||
https://www.codeproject.com/Articles/620045/Custom-Controls-in-Win-API-Visual-Styles
|
||||
https://blogs.msdn.microsoft.com/wpf/2010/11/30/systemcolors-reference/
|
||||
https://stackoverflow.com/questions/25639621/check-when-a-user-changes-windows-glass-brush-theme-color
|
||||
https://msdn.microsoft.com/en-us/library/windows/desktop/aa969513(v=vs.85).aspx
|
||||
https://msdn.microsoft.com/en-us/library/windows/desktop/aa969527(v=vs.85).aspx
|
||||
https://msdn.microsoft.com/en-us/library/windows/desktop/dd388198(v=vs.85).aspx
|
||||
I hope the MFC ribbon can have customizable colors too, otherwise I'll have to do some serious image processing...
|
||||
|
||||
windows debugging
|
||||
https://msdn.microsoft.com/en-us/library/windows/desktop/hh780351(v=vs.85).aspx
|
||||
https://msdn.microsoft.com/en-us/library/windows/desktop/ff476881(v=vs.85).aspx#Debug
|
||||
https://msdn.microsoft.com/en-us/library/windows/desktop/hh780343(v=vs.85).aspx
|
||||
https://msdn.microsoft.com/en-us/library/windows/desktop/dn457937(v=vs.85).aspx
|
||||
|
||||
more OS2 stuff
|
||||
https://www.google.com/search?q=%22ibm+graphics+development+toolkit%22&ie=utf-8&oe=utf-8&client=firefox-b-1
|
||||
https://www.google.com/search?q=%22os%2F2+graphics+development+toolkit%22&ie=utf-8&oe=utf-8&client=firefox-b-1
|
||||
http://www.edm2.com/index.php/IBM_OS/2_Developer%27s_Packages
|
||||
|
||||
[17:48:52] <Chloe> andlabs: I got splitView and NSWindow size restoration working btw
|
||||
[17:49:09] <Chloe> andlabs: see https://github.com/eintw1ck/mail/blob/master/Sources/mail/MainViewController.swift for splitView
|
||||
|
||||
old stuff
|
||||
font1.gif (GIF Image, 424 × 475 pixels) http://www.functionx.com/win32/controls/dlgboxes/font1.gif
|
||||
Inskcape’s Hidden Little Feature: Mesh Gradients | OCS-Mag http://www.ocsmag.com/2016/02/27/inskcapes-hidden-little-feature-mesh-gradients/ (near "When you’re done colouring in all the nodes, you may want to drag")
|
||||
|
||||
https://msdn.microsoft.com/en-us/library/windows/desktop/ms632615(v=vs.85).aspx we need to handle this alongisde WM_CAPTURECHANGED
|
||||
|
||||
https://msdn.microsoft.com/en-us/library/windows/desktop/dn424996%28v=vs.85%29.aspx?f=255&MSPPError=-2147217396
|
||||
whatever this is seems to have no documentation... it appears to be related to UWP, if not only Store apps...
|
||||
|
||||
unreachable code optimizations
|
||||
https://docs.microsoft.com/en-us/cpp/intrinsics/assume?view=vs-2017
|
||||
https://gcc.gnu.org/onlinedocs/gcc/Other-Builtins.html
|
|
@ -0,0 +1,9 @@
|
|||
https://docs.microsoft.com/en-us/windows/desktop/uxguide/cmd-toolbars
|
||||
https://docs.microsoft.com/en-us/windows/desktop/controls/cc-faq-iemenubar
|
||||
https://docs.microsoft.com/en-us/windows/desktop/controls/cc-faq-ietoolbar
|
||||
https://docs.microsoft.com/en-us/windows/desktop/controls/create-rebar-controls
|
||||
https://docs.microsoft.com/en-us/windows/desktop/controls/create-toolbars
|
||||
https://docs.microsoft.com/en-us/windows/desktop/controls/handle-drop-down-buttons
|
||||
https://docs.microsoft.com/en-us/windows/desktop/controls/tb-buttonstructsize
|
||||
https://www.google.com/search?q=winapi+toolbar+dropdown+position&ie=utf-8&oe=utf-8&client=firefox-b-1
|
||||
https://www.google.com/search?q=winapi+toolbar+dropdown+arrow+position&ie=utf-8&oe=utf-8&client=firefox-b-1
|
|
@ -0,0 +1,4 @@
|
|||
https://developer.apple.com/library/mac/documentation/Cocoa/Reference/ApplicationKit/Protocols/NSOutlineViewDataSource_Protocol/index.html#//apple_ref/occ/intfm/NSOutlineViewDataSource/outlineView:child:ofItem:
|
||||
https://developer.apple.com/library/mac/documentation/Cocoa/Reference/NSOutlineViewDelegate_Protocol/index.html#//apple_ref/occ/intfm/NSOutlineViewDelegate/outlineView:didAddRowView:forRow:
|
||||
https://developer.apple.com/documentation/appkit/nsoutlineviewdelegate/1528320-outlineview?language=objc
|
||||
https://github.com/mity/mctrl/blob/master/mctrl/treelist.c around treelist_set_subitem()
|
|
@ -0,0 +1,3 @@
|
|||
http://www.catch22.net/tuts/transparent-text
|
||||
https://msdn.microsoft.com/en-us/library/windows/desktop/ms724371(v=vs.85).aspx
|
||||
TODO which color did I want from this list
|
|
@ -0,0 +1,3 @@
|
|||
http://www.os2museum.com/wp/windows-10-arm64-on-qemu/
|
||||
https://docs.microsoft.com/en-us/windows/uwp/porting/apps-on-arm
|
||||
http://pete.akeo.ie/2017/05/compiling-desktop-arm-applications-with.html
|
|
@ -0,0 +1,21 @@
|
|||
https://msdn.microsoft.com/en-us/library/windows/desktop/dn469266(v=vs.85).aspx
|
||||
https://msdn.microsoft.com/en-us/library/windows/desktop/mt744321(v=vs.85).aspx
|
||||
!!!! http://stackoverflow.com/questions/41917279/do-child-windows-have-the-same-dpi-as-their-parents-in-a-per-monitor-aware-appli
|
||||
https://msdn.microsoft.com/en-us/library/windows/desktop/mt843498(v=vs.85).aspx
|
||||
https://msdn.microsoft.com/library/windows/desktop/mt791579(v=vs.85).aspx
|
||||
https://blogs.windows.com/buildingapps/2017/04/04/high-dpi-scaling-improvements-desktop-applications-windows-10-creators-update/
|
||||
https://channel9.msdn.com/Events/Windows/Windows-Developer-Day-Creators-Update/High-DPI-Improvements-for-Desktop-Developers
|
||||
https://social.msdn.microsoft.com/Forums/vstudio/en-US/31d2a89f-3518-403e-b1e4-bbe37ac1b0f0/per-monitor-high-dpi-awareness-wmdpichanged?forum=vcgeneral
|
||||
https://stackoverflow.com/questions/36864894/scaling-the-non-client-area-title-bar-menu-bar-for-per-monitor-high-dpi-suppo/37624363
|
||||
https://stackoverflow.com/questions/41448320/dlgtemplateex-and-ds-shellfont-what-about-point-size
|
||||
http://stackoverflow.com/questions/41917279/do-child-windows-have-the-same-dpi-as-their-parents-in-a-per-monitor-aware-appli
|
||||
|
||||
https://msdn.microsoft.com/en-us/library/windows/desktop/dn469266(v=vs.85).aspx
|
||||
https://msdn.microsoft.com/library/windows/desktop/mt843498(v=vs.85).aspx(d=robot)
|
||||
https://msdn.microsoft.com/en-us/library/windows/desktop/dn469266(v=vs.85).aspx#appendix_c_common_high_dpi_issues
|
||||
https://msdn.microsoft.com/library/windows/desktop/mt843498(v=vs.85).aspx(d=robot)#appendix_c_common_high_dpi_issues
|
||||
https://msdn.microsoft.com/en-us/library/windows/desktop/dn469266(v=vs.85).aspx#addressing_high_dpi_issues
|
||||
https://msdn.microsoft.com/library/windows/desktop/mt843498(v=vs.85).aspx(d=robot)#addressing_high_dpi_issues
|
||||
|
||||
https://msdn.microsoft.com/en-us/library/windows/desktop/dn302215(v=vs.85).aspx
|
||||
https://msdn.microsoft.com/en-us/library/windows/desktop/hh802769(v=vs.85).aspx
|
|
@ -0,0 +1,7 @@
|
|||
https://msdn.microsoft.com/en-us/library/windows/desktop/ff686805(v=vs.85).aspx
|
||||
https://msdn.microsoft.com/en-us/library/windows/desktop/dn495653(v=vs.85).aspx
|
||||
https://msdn.microsoft.com/en-us/library/windows/desktop/hh448422(v=vs.85).aspx
|
||||
https://msdn.microsoft.com/en-us/library/windows/desktop/dd316975(v=vs.85).aspx
|
||||
https://msdn.microsoft.com/en-us/library/windows/desktop/dd372919(v=vs.85).aspx
|
||||
https://msdn.microsoft.com/en-us/library/windows/desktop/ee264335(v=vs.85).aspx
|
||||
https://msdn.microsoft.com/en-us/library/windows/desktop/dd145198(v=vs.85).aspx
|
|
@ -0,0 +1,46 @@
|
|||
https://twitter.com/omgubuntu/status/962765197109841922
|
||||
https://github.com/DominicMaas/SoundByte/blob/master/SoundByte.UWP/Resources/Brushes.xaml
|
||||
https://docs.microsoft.com/en-us/windows/uwp/design/style/acrylic
|
||||
https://www.google.com/search?q=uwp+acrylic+winapi&ie=utf-8&oe=utf-8&client=firefox-b-1
|
||||
https://stackoverflow.com/questions/43931709/acrylic-material-in-win32-app
|
||||
https://stackoverflow.com/questions/44000217/mimicking-acrylic-in-a-win32-app
|
||||
https://stackoverflow.com/questions/43699256/how-to-use-acrylic-accent-in-windows-10-creators-update
|
||||
https://stackoverflow.com/questions/39135834/how-to-call-uwp-api-from-converted-win32-app-desktop-app-converter
|
||||
https://stackoverflow.com/questions/32724187/how-do-you-set-the-glass-blend-colour-on-windows-10
|
||||
https://channel9.msdn.com/Events/Build/2017/B8034
|
||||
https://www.reddit.com/r/Windows10/comments/7lzyzc/since_the_taskbar_is_still_win32_and_it_can_have/
|
||||
https://thenextweb.com/microsoft/2017/05/15/microsoft-fluent-design-system-breaking-windows-10s-new-look/
|
||||
https://github.com/bbougot/AcrylicWPF
|
||||
http://vhanla.codigobit.info/2015/07/enable-windows-10-aero-glass-aka-blur.html
|
||||
http://undoc.airesoft.co.uk/user32.dll/SetWindowCompositionAttribute.php
|
||||
http://undoc.airesoft.co.uk/user32.dll/GetWindowCompositionAttribute.php
|
||||
https://msdn.microsoft.com/en-us/library/windows/desktop/aa969530(v=vs.85).aspx
|
||||
https://github.com/bbougot/AcrylicWPF/blob/master/MainWindow.xaml.cs
|
||||
https://www.google.com/search?q=%22WCA_ACCENT_POLICY%22&client=firefox-b-1&source=lnt&tbs=cdr%3A1%2Ccd_min%3A1%2F1%2F2001%2Ccd_max%3A12%2F31%2F2013&tbm=
|
||||
https://github.com/sgothel/jogl/blob/master/src/nativewindow/native/win32/WindowsDWM.c
|
||||
http://www.brandonfa.lk/win8/win8_devrel_head_x86/uxtheme.h
|
||||
http://www.brandonfa.lk/win8/
|
||||
https://github.com/gamozolabs/pdblister
|
||||
https://github.com/wbenny/pdbex
|
||||
https://github.com/wbenny/pdbex/releases
|
||||
|
||||
https://stackoverflow.com/questions/44000217/mimicking-acrylic-in-a-win32-app
|
||||
https://github.com/riverar/sample-win32-acrylicblur
|
||||
https://github.com/riverar/sample-win10-aeroglass
|
||||
https://guerra24blog.wordpress.com/2017/12/20/windows-10-use-acrylic-in-win32-apps/
|
||||
https://news.ycombinator.com/item?id=14432951
|
||||
https://gist.github.com/ethanhs/0e157e4003812e99bf5bc7cb6f73459f ACCENTPOLICY
|
||||
https://github.com/TranslucentTB/Tools
|
||||
https://withinrafael.com/2015/07/08/adding-the-aero-glass-blur-to-your-windows-10-apps/
|
||||
https://withinrafael.com/2018/02/01/adding-acrylic-blur-to-your-windows-10-apps-redstone-4-desktop-apps/
|
||||
|
||||
diversion: DwmEnableBlurBehindWindow {
|
||||
https://docs.microsoft.com/en-us/windows/desktop/api/dwmapi/nf-dwmapi-dwmenableblurbehindwindow
|
||||
https://docs.microsoft.com/en-us/windows/desktop/api/dwmapi/ns-dwmapi-_dwm_blurbehind
|
||||
https://docs.microsoft.com/en-us/windows/desktop/api/dwmapi/nf-dwmapi-dwmenableblurbehindwindow
|
||||
http://www.danielmoth.com/Blog/Vista-Glass-Answers-And-DwmEnableBlurBehindWindow.aspx
|
||||
http://www.danielmoth.com/Blog/Glass-In-C-An-Alternative-Approach.aspx
|
||||
http://www.danielmoth.com/Blog/Vista-Glass-In-C.aspx
|
||||
http://www.danielmoth.com/Blog/Glass-In-C-An-Alternative-Approach.aspx
|
||||
https://weblogs.asp.net/kennykerr/Windows-Vista-for-Developers-_1320_-Part-3-_1320_-The-Desktop-Window-Manager
|
||||
}
|
|
@ -0,0 +1,6 @@
|
|||
# TODO is there a -Wno-switch equivalent?
|
||||
# TODO /sdl turns C4996 into an ERROR
|
||||
# don't use /analyze; that requires us to write annotations everywhere
|
||||
# TODO undecided flags from qo?
|
||||
# the RTC flags are only supplied in debug builds because they are only supposed to be used by debug builds (see "This is because run-time error checks are not valid in a release (optimized) build." in https://docs.microsoft.com/cpp/build/reference/rtc-run-time-error-checks)
|
||||
# /RTCc is not supplied because it's discouraged as of VS2015; see https://www.reddit.com/r/cpp/comments/46mhne/rtcc_rejects_conformant_code_with_visual_c_2015/d06auq5
|
|
@ -0,0 +1,92 @@
|
|||
// 10 february 2017
|
||||
#include "../ui.h"
|
||||
#include "uipriv.h"
|
||||
|
||||
// TODO this doesn't handle the case where nLines == 0
|
||||
// TODO this should never happen even if there are no characters??
|
||||
// TODO figure out how to make this work on GTK+
|
||||
void uiDrawCaret(uiDrawContext *c, double x, double y, uiDrawTextLayout *layout, size_t pos, int *line)
|
||||
{
|
||||
double xoff;
|
||||
uiDrawTextLayoutLineMetrics m;
|
||||
struct caretDrawParams cdp;
|
||||
uiDrawPath *path;
|
||||
uiDrawBrush brush;
|
||||
|
||||
if (*line < 0)
|
||||
*line = 0;
|
||||
if (*line > (uiDrawTextLayoutNumLines(layout) - 1))
|
||||
*line = (uiDrawTextLayoutNumLines(layout) - 1);
|
||||
// TODO cap pos
|
||||
xoff = uiDrawTextLayoutByteLocationInLine(layout, pos, *line);
|
||||
while (xoff < 0) {
|
||||
size_t start, end;
|
||||
|
||||
uiDrawTextLayoutLineByteRange(layout, *line, &start, &end);
|
||||
if (end < pos) // too far up
|
||||
(*line)++;
|
||||
else
|
||||
(*line)--;
|
||||
xoff = uiDrawTextLayoutByteLocationInLine(layout, pos, *line);
|
||||
}
|
||||
uiDrawTextLayoutLineGetMetrics(layout, *line, &m);
|
||||
|
||||
caretDrawParams(c, m.Height, &cdp);
|
||||
|
||||
uiDrawSave(c);
|
||||
|
||||
path = uiDrawNewPath(uiDrawFillModeWinding);
|
||||
uiDrawPathAddRectangle(path,
|
||||
// TODO add m.X?
|
||||
x + xoff - cdp.xoff, y + m.Y,
|
||||
cdp.width, m.Height);
|
||||
uiDrawPathEnd(path);
|
||||
brush.Type = uiDrawBrushTypeSolid;
|
||||
brush.R = cdp.r;
|
||||
brush.G = cdp.g;
|
||||
brush.B = cdp.b;
|
||||
brush.A = cdp.a;
|
||||
uiDrawFill(c, path, &brush);
|
||||
uiDrawFreePath(path);
|
||||
|
||||
uiDrawRestore(c);
|
||||
}
|
||||
|
||||
void drawTextBackground(uiDrawContext *c, double x, double y, uiDrawTextLayout *layout, size_t start, size_t end, uiDrawBrush *brush, int isSelection)
|
||||
{
|
||||
int line, nLines;
|
||||
size_t lstart, lend;
|
||||
double layoutwid, layoutht;
|
||||
|
||||
uiDrawTextLayoutExtents(layout, &layoutwid, &layoutht);
|
||||
nLines = uiDrawTextLayoutNumLines(layout);
|
||||
for (line = 0; line < nLines; line++) {
|
||||
uiDrawTextLayoutLineByteRange(layout, line, &lstart, &lend);
|
||||
if (start >= lstart && start < lend)
|
||||
break;
|
||||
}
|
||||
while (end - start > 0) {
|
||||
uiDrawTextLayoutLineMetrics m;
|
||||
double startx, endx;
|
||||
uiDrawPath *path;
|
||||
|
||||
uiDrawTextLayoutLineByteRange(layout, line, &lstart, &lend);
|
||||
if (lend > end) // don't cross lines
|
||||
lend = end;
|
||||
startx = uiDrawTextLayoutByteLocationInLine(layout, start, line);
|
||||
// TODO explain this; note the use of start with lend
|
||||
endx = layoutwid;
|
||||
if (!isSelection || end <= lend)
|
||||
endx = uiDrawTextLayoutByteLocationInLine(layout, lend, line);
|
||||
uiDrawTextLayoutLineGetMetrics(layout, line, &m);
|
||||
path = uiDrawNewPath(uiDrawFillModeWinding);
|
||||
uiDrawPathAddRectangle(path,
|
||||
x + startx, y + m.Y,
|
||||
endx - startx, m.Height);
|
||||
uiDrawPathEnd(path);
|
||||
uiDrawFill(c, path, brush);
|
||||
uiDrawFreePath(path);
|
||||
line++;
|
||||
start = lend;
|
||||
}
|
||||
}
|
|
@ -0,0 +1,15 @@
|
|||
|
||||
|
||||
// TODO split these into a separate header file?
|
||||
|
||||
// drawtext.c
|
||||
struct caretDrawParams {
|
||||
double r;
|
||||
double g;
|
||||
double b;
|
||||
double a;
|
||||
double xoff;
|
||||
double width;
|
||||
};
|
||||
extern void caretDrawParams(uiDrawContext *c, double height, struct caretDrawParams *p);
|
||||
extern void drawTextBackground(uiDrawContext *c, double x, double y, uiDrawTextLayout *layout, size_t start, size_t end, uiDrawBrush *brush, int isSelection);
|
|
@ -0,0 +1,347 @@
|
|||
// 2 january 2017
|
||||
#import "uipriv_darwin.h"
|
||||
#import "draw.h"
|
||||
|
||||
@interface lineInfo : NSObject
|
||||
@property NSRange glyphRange;
|
||||
@property NSRange characterRange;
|
||||
@property NSRect lineRect;
|
||||
@property NSRect lineUsedRect;
|
||||
@property NSRect glyphBoundingRect;
|
||||
@property CGFloat baselineOffset;
|
||||
@property double ascent;
|
||||
@property double descent;
|
||||
@property double leading;
|
||||
@end
|
||||
|
||||
@implementation lineInfo
|
||||
@end
|
||||
|
||||
struct uiDrawTextLayout {
|
||||
// NSTextStorage is subclassed from NSMutableAttributedString
|
||||
NSTextStorage *attrstr;
|
||||
NSTextContainer *container;
|
||||
NSLayoutManager *layoutManager;
|
||||
|
||||
// the width as passed into uiDrawTextLayout constructors
|
||||
double width;
|
||||
|
||||
#if 0 /* TODO */
|
||||
// the *actual* size of the frame
|
||||
// note: technically, metrics returned from frame are relative to CGPathGetPathBoundingBox(tl->path)
|
||||
// however, from what I can gather, for a path created by CGPathCreateWithRect(), like we do (with a NULL transform), CGPathGetPathBoundingBox() seems to just return the standardized form of the rect used to create the path
|
||||
// (this I confirmed through experimentation)
|
||||
// so we can just use tl->size for adjustments
|
||||
// we don't need to adjust coordinates by any origin since our rect origin is (0, 0)
|
||||
CGSize size;
|
||||
#endif
|
||||
|
||||
NSMutableArray<lineInfo *> *lineInfo;
|
||||
|
||||
// for converting CFAttributedString indices to byte offsets
|
||||
size_t *u16tou8;
|
||||
size_t nu16tou8; // TODO I don't like the casing of this name
|
||||
};
|
||||
|
||||
static NSFont *fontdescToNSFont(uiDrawFontDescriptor *fd)
|
||||
{
|
||||
NSFontDescriptor *desc;
|
||||
NSFont *font;
|
||||
|
||||
desc = fontdescToNSFontDescriptor(fd);
|
||||
font = [NSFont fontWithDescriptor:desc size:fd->Size];
|
||||
[desc release];
|
||||
return font;
|
||||
}
|
||||
|
||||
static NSTextStorage *attrstrToTextStorage(uiAttributedString *s, uiDrawFontDescriptor *defaultFont)
|
||||
{
|
||||
NSString *nsstr;
|
||||
NSMutableDictionary *defaultAttrs;
|
||||
NSTextStorage *attrstr;
|
||||
|
||||
nsstr = [[NSString alloc] initWithCharacters:attrstrUTF16(s)
|
||||
length:attrstrUTF16Len(s)];
|
||||
|
||||
defaultAttrs = [NSMutableDictionary new];
|
||||
[defaultAttrs setObject:fontdescToNSFont(defaultFont)
|
||||
forKey:NSFontAttributeName];
|
||||
|
||||
attrstr = [[NSTextStorage alloc] initWithString:nsstr
|
||||
attributes:defaultAttrs];
|
||||
[defaultAttrs release];
|
||||
[nsstr release];
|
||||
|
||||
[attrstr beginEditing];
|
||||
// TODO copy in the attributes
|
||||
[attrstr endEditing];
|
||||
|
||||
return attrstr;
|
||||
}
|
||||
|
||||
// TODO fine-tune all the properties
|
||||
uiDrawTextLayout *uiDrawNewTextLayout(uiAttributedString *s, uiDrawFontDescriptor *defaultFont, double width)
|
||||
{
|
||||
uiDrawTextLayout *tl;
|
||||
CGFloat cgwidth;
|
||||
// TODO correct type?
|
||||
NSUInteger index;
|
||||
|
||||
tl = uiNew(uiDrawTextLayout);
|
||||
tl->attrstr = attrstrToTextStorage(s, defaultFont);
|
||||
tl->width = width;
|
||||
|
||||
// TODO the documentation on the size property implies this might not be necessary?
|
||||
cgwidth = (CGFloat) width;
|
||||
if (cgwidth < 0)
|
||||
cgwidth = CGFLOAT_MAX;
|
||||
// TODO rename to tl->textContainer
|
||||
tl->container = [[NSTextContainer alloc] initWithSize:NSMakeSize(cgwidth, CGFLOAT_MAX)];
|
||||
// TODO pull the reference for this
|
||||
[tl->container setLineFragmentPadding:0];
|
||||
|
||||
tl->layoutManager = [[NSLayoutManager alloc] init];
|
||||
[tl->layoutManager setTypesetterBehavior:NSTypesetterLatestBehavior];
|
||||
|
||||
[tl->layoutManager addTextContainer:tl->container];
|
||||
[tl->attrstr addLayoutManager:tl->layoutManager];
|
||||
// and force a re-layout (TODO get source
|
||||
[tl->layoutManager glyphRangeForTextContainer:tl->container];
|
||||
|
||||
// TODO equivalent of CTFrameProgression for RTL/LTR?
|
||||
|
||||
// now collect line information; see https://developer.apple.com/library/content/documentation/Cocoa/Conceptual/TextLayout/Tasks/CountLines.html
|
||||
tl->lineInfo = [NSMutableArray<lineInfo *> new];
|
||||
index = 0;
|
||||
while (index < [tl->layoutManager numberOfGlyphs]) {
|
||||
NSRange glyphRange;
|
||||
__block lineInfo *li;
|
||||
|
||||
li = [lineInfo new];
|
||||
li.lineRect = [tl->layoutManager lineFragmentRectForGlyphAtIndex:index effectiveRange:&glyphRange];
|
||||
li.glyphRange = glyphRange;
|
||||
li.characterRange = [tl->layoutManager characterRangeForGlyphRange:li.glyphRange actualGlyphRange:NULL];
|
||||
li.lineUsedRect = [tl->layoutManager lineFragmentUsedRectForGlyphAtIndex:index effectiveRange:NULL];
|
||||
li.glyphBoundingRect = [tl->layoutManager boundingRectForGlyphRange:li.glyphRange inTextContainer:tl->container];
|
||||
// and this is from http://www.cocoabuilder.com/archive/cocoa/308568-how-to-get-baseline-info.html and http://www.cocoabuilder.com/archive/cocoa/199283-height-and-location-of-text-within-line-in-nslayoutmanager-ignoring-spacing.html
|
||||
li.baselineOffset = [[tl->layoutManager typesetter] baselineOffsetInLayoutManager:tl->layoutManager glyphIndex:index];
|
||||
li.ascent = 0;
|
||||
li.descent = 0;
|
||||
li.leading = 0;
|
||||
// imitate what AppKit actually does (or seems to)
|
||||
[tl->attrstr enumerateAttributesInRange:li.characterRange options:0 usingBlock:^(NSDictionary<NSString *,id> *attrs, NSRange range, BOOL *stop) {
|
||||
NSFont *f;
|
||||
NSRect boundingRect;
|
||||
double v, realAscent, realDescent, realLeading;
|
||||
BOOL skipAdjust, doAdjust;
|
||||
|
||||
f = (NSFont *) [attrs objectForKey:NSFontAttributeName];
|
||||
if (f == nil) {
|
||||
f = [NSFont fontWithName:@"Helvetica" size:12.0];
|
||||
if (f == nil)
|
||||
f = [NSFont systemFontOfSize:12.0];
|
||||
}
|
||||
|
||||
boundingRect = [f boundingRectForFont];
|
||||
realAscent = [f ascender];
|
||||
realDescent = -[f descender];
|
||||
realLeading = [f leading];
|
||||
skipAdjust = NO;
|
||||
doAdjust = NO;
|
||||
if (NSMaxY(boundingRect) <= realAscent) {
|
||||
// ascent entirely within bounding box
|
||||
// don't do anything if there's leading; I'm guessing it's a combination of both of the reasons to skip below... (least sure of this one)
|
||||
if (realLeading != 0)
|
||||
skipAdjust = YES;
|
||||
// does the descent slip outside the bounding box?
|
||||
if (-realDescent <= NSMinY(boundingRect))
|
||||
// yes — I guess we should assume accents don't collide with the previous line's descent, though I'm not as sure of that as I am about the else clause below...
|
||||
skipAdjust = YES;
|
||||
} else {
|
||||
// ascent outside bounding box — ascent does not include accents
|
||||
// only skip adjustment if there isn't leading (apparently some fonts use the previous line's leading for accents? :/ )
|
||||
if (realLeading != 0)
|
||||
skipAdjust = YES;
|
||||
}
|
||||
if (!skipAdjust) {
|
||||
UniChar ch = 0xC0;
|
||||
CGGlyph glyph;
|
||||
|
||||
// there does not seem to be an AppKit API for this...
|
||||
if (CTFontGetGlyphsForCharacters((CTFontRef) f, &ch, &glyph, 1) != false) {
|
||||
NSRect bbox;
|
||||
|
||||
bbox = [f boundingRectForGlyph:glyph];
|
||||
if (NSMaxY(bbox) > realAscent)
|
||||
doAdjust = YES;
|
||||
if (-realDescent > NSMinY(bbox))
|
||||
doAdjust = YES;
|
||||
}
|
||||
}
|
||||
// TODO vertical
|
||||
|
||||
v = [f ascender];
|
||||
// TODO get this one back out
|
||||
if (doAdjust)
|
||||
v += 0.2 * ([f ascender] + [f descender]);
|
||||
//v = floor(v + 0.5);
|
||||
if (li.ascent < v)
|
||||
li.ascent = v;
|
||||
|
||||
v = -[f descender];// floor(-[f descender] + 0.5);
|
||||
if (li.descent < v)
|
||||
li.descent = v;
|
||||
|
||||
v = [f leading];//floor([f leading] + 0.5);
|
||||
if (li.leading < v)
|
||||
li.leading = v;
|
||||
}];
|
||||
li.ascent = floor(li.ascent + 0.5);
|
||||
li.descent = floor(li.descent + 0.5);
|
||||
li.leading = floor(li.leading + 0.5);
|
||||
[tl->lineInfo addObject:li];
|
||||
[li release];
|
||||
index = glyphRange.location + glyphRange.length;
|
||||
}
|
||||
|
||||
// and finally copy the UTF-16 to UTF-8 index conversion table
|
||||
tl->u16tou8 = attrstrCopyUTF16ToUTF8(s, &(tl->nu16tou8));
|
||||
|
||||
return tl;
|
||||
}
|
||||
|
||||
void uiDrawFreeTextLayout(uiDrawTextLayout *tl)
|
||||
{
|
||||
uiFree(tl->u16tou8);
|
||||
[tl->lineInfo release];
|
||||
[tl->layoutManager release];
|
||||
[tl->container release];
|
||||
[tl->attrstr release];
|
||||
uiFree(tl);
|
||||
}
|
||||
|
||||
// TODO document that (x,y) is the top-left corner of the *entire frame*
|
||||
void uiDrawText(uiDrawContext *c, uiDrawTextLayout *tl, double x, double y)
|
||||
{
|
||||
NSGraphicsContext *gc;
|
||||
|
||||
CGContextFlush(c->c); // just to be safe
|
||||
[NSGraphicsContext saveGraphicsState];
|
||||
gc = [NSGraphicsContext graphicsContextWithGraphicsPort:c->c flipped:YES];
|
||||
[NSGraphicsContext setCurrentContext:gc];
|
||||
|
||||
// TODO is this the right point?
|
||||
// TODO does this draw with the proper default styles?
|
||||
[tl->layoutManager drawGlyphsForGlyphRange:[tl->layoutManager glyphRangeForTextContainer:tl->container]
|
||||
atPoint:NSMakePoint(x, y)];
|
||||
|
||||
[gc flushGraphics]; // just to be safe
|
||||
[NSGraphicsContext restoreGraphicsState];
|
||||
// TODO release gc?
|
||||
}
|
||||
|
||||
// TODO update all of these {
|
||||
// TODO document that the width and height of a layout is not necessarily the sum of the widths and heights of its constituent lines; this is definitely untrue on OS X, where lines are placed in such a way that the distance between baselines is always integral
|
||||
// TODO width doesn't include trailing whitespace...
|
||||
// TODO figure out how paragraph spacing should play into this
|
||||
// }
|
||||
void uiDrawTextLayoutExtents(uiDrawTextLayout *tl, double *width, double *height)
|
||||
{
|
||||
NSRect r;
|
||||
|
||||
// see https://developer.apple.com/library/content/documentation/Cocoa/Conceptual/TextLayout/Tasks/StringHeight.html
|
||||
r = [tl->layoutManager usedRectForTextContainer:tl->container];
|
||||
*width = r.size.width;
|
||||
*height = r.size.height;
|
||||
}
|
||||
|
||||
int uiDrawTextLayoutNumLines(uiDrawTextLayout *tl)
|
||||
{
|
||||
return [tl->lineInfo count];
|
||||
}
|
||||
|
||||
void uiDrawTextLayoutLineByteRange(uiDrawTextLayout *tl, int line, size_t *start, size_t *end)
|
||||
{
|
||||
lineInfo *li;
|
||||
|
||||
li = (lineInfo *) [tl->lineInfo objectAtIndex:line];
|
||||
*start = tl->u16tou8[li.characterRange.location];
|
||||
*end = tl->u16tou8[li.characterRange.location + li.characterRange.length];
|
||||
}
|
||||
|
||||
void uiDrawTextLayoutLineGetMetrics(uiDrawTextLayout *tl, int line, uiDrawTextLayoutLineMetrics *m)
|
||||
{
|
||||
lineInfo *li;
|
||||
|
||||
li = (lineInfo *) [tl->lineInfo objectAtIndex:line];
|
||||
|
||||
m->X = li.lineRect.origin.x;
|
||||
m->Y = li.lineRect.origin.y;
|
||||
// if we use li.lineRect here we get the whole line, not just the part with stuff in it
|
||||
m->Width = li.lineUsedRect.size.width;
|
||||
m->Height = li.lineRect.size.height;
|
||||
|
||||
// TODO is this correct?
|
||||
m->BaselineY = (m->Y + m->Height) - li.baselineOffset;
|
||||
m->Ascent = li.ascent;
|
||||
m->Descent = li.descent;
|
||||
m->Leading = li.leading;
|
||||
|
||||
// TODO
|
||||
m->ParagraphSpacingBefore = 0;
|
||||
m->LineHeightSpace = 0;
|
||||
m->LineSpacing = 0;
|
||||
m->ParagraphSpacing = 0;
|
||||
}
|
||||
|
||||
void uiDrawTextLayoutHitTest(uiDrawTextLayout *tl, double x, double y, uiDrawTextLayoutHitTestResult *result)
|
||||
{
|
||||
#if 0 /* TODO */
|
||||
CFIndex i;
|
||||
CTLineRef line;
|
||||
CFIndex pos;
|
||||
|
||||
if (y >= 0) {
|
||||
for (i = 0; i < tl->nLines; i++) {
|
||||
double ltop, lbottom;
|
||||
|
||||
ltop = tl->lineMetrics[i].Y;
|
||||
lbottom = ltop + tl->lineMetrics[i].Height;
|
||||
if (y >= ltop && y < lbottom)
|
||||
break;
|
||||
}
|
||||
result->YPosition = uiDrawTextLayoutHitTestPositionInside;
|
||||
if (i == tl->nLines) {
|
||||
i--;
|
||||
result->YPosition = uiDrawTextLayoutHitTestPositionAfter;
|
||||
}
|
||||
} else {
|
||||
i = 0;
|
||||
result->YPosition = uiDrawTextLayoutHitTestPositionBefore;
|
||||
}
|
||||
result->Line = i;
|
||||
|
||||
result->XPosition = uiDrawTextLayoutHitTestPositionInside;
|
||||
if (x < tl->lineMetrics[i].X) {
|
||||
result->XPosition = uiDrawTextLayoutHitTestPositionBefore;
|
||||
// and forcibly return the first character
|
||||
x = tl->lineMetrics[i].X;
|
||||
} else if (x > (tl->lineMetrics[i].X + tl->lineMetrics[i].Width)) {
|
||||
result->XPosition = uiDrawTextLayoutHitTestPositionAfter;
|
||||
// and forcibly return the last character
|
||||
x = tl->lineMetrics[i].X + tl->lineMetrics[i].Width;
|
||||
}
|
||||
|
||||
line = (CTLineRef) CFArrayGetValueAtIndex(tl->lines, i);
|
||||
// TODO copy the part from the docs about this point
|
||||
pos = CTLineGetStringIndexForPosition(line, CGPointMake(x, 0));
|
||||
if (pos == kCFNotFound) {
|
||||
// TODO
|
||||
}
|
||||
result->Pos = tl->u16tou8[pos];
|
||||
#endif
|
||||
}
|
||||
|
||||
void uiDrawTextLayoutByteRangeToRectangle(uiDrawTextLayout *tl, size_t start, size_t end, uiDrawTextLayoutByteRangeRectangle *r)
|
||||
{
|
||||
}
|
|
@ -0,0 +1,223 @@
|
|||
// 3 january 2017
|
||||
#import "uipriv_darwin.h"
|
||||
|
||||
// Stupidity: Core Text requires an **exact match for the entire traits dictionary**, otherwise it will **drop ALL the traits**.
|
||||
// This seems to be true for every function in Core Text that advertises that it performs font matching; I can confirm at the time of writing this is the case for
|
||||
// - CTFontDescriptorCreateMatchingFontDescriptors() (though the conditions here seem odd)
|
||||
// - CTFontCreateWithFontDescriptor()
|
||||
// - CTFontCreateCopyWithAttributes()
|
||||
// And as a bonus prize, this also applies to Cocoa's NSFontDescriptor methods as well!
|
||||
// We have to implement the closest match ourselves.
|
||||
// This needs to be done early in the matching process; in particular, we have to do this before adding any features (such as small caps), because the matching descriptors won't have those.
|
||||
|
||||
struct closeness {
|
||||
NSUInteger index;
|
||||
double weight;
|
||||
double italic;
|
||||
double stretch;
|
||||
double distance;
|
||||
};
|
||||
|
||||
static double doubleAttr(NSDictionary *traits, NSString *attr)
|
||||
{
|
||||
NSNumber *n;
|
||||
|
||||
n = (NSNumber *) [traits objectForKey:attr];
|
||||
return [n doubleValue];
|
||||
}
|
||||
|
||||
struct italicCloseness {
|
||||
double normal;
|
||||
double oblique;
|
||||
double italic;
|
||||
};
|
||||
|
||||
// remember that in closeness, 0 means exact
|
||||
// in this case, since we define the range, we use 0.5 to mean "close enough" (oblique for italic and italic for oblique) and 1 to mean "not a match"
|
||||
static const struct italicCloseness italicClosenesses[] = {
|
||||
[uiDrawTextItalicNormal] = { 0, 1, 1 },
|
||||
[uiDrawTextItalicOblique] = { 1, 0, 0.5 },
|
||||
[uiDrawTextItalicItalic] = { 1, 0.5, 0 },
|
||||
};
|
||||
|
||||
// Italics are hard because Core Text does NOT distinguish between italic and oblique.
|
||||
// All Core Text provides is a slant value and the italic bit of the symbolic traits mask.
|
||||
// However, Core Text does seem to guarantee (from experimentation; see below) that the slant will be nonzero if and only if the italic bit is set, so we don't need to use the slant value.
|
||||
// Core Text also seems to guarantee that if a font lists itself as Italic or Oblique by name (font subfamily name, font style name, whatever), it will also have that bit set, so testing this bit does cover all fonts that name themselves as Italic and Oblique. (Again, this is from the below experimentation.)
|
||||
// TODO there is still one catch that might matter from a user's POV: the reverse is not true — the italic bit can be set even if the style of the font face/subfamily/style isn't named as Italic (for example, script typefaces like Adobe's Palace Script MT Std); I don't know what to do about this... I know how to start: find a script font that has an italic form (Adobe's Palace Script MT Std does not; only Regular and Semibold)
|
||||
// TODO see if the above applies to Cocoa as well
|
||||
static double italicCloseness(NSFontDescriptor *desc, NSDictionary *traits, uiDrawTextItalic italic)
|
||||
{
|
||||
const struct italicCloseness *ic = &(italicClosenesses[italic]);
|
||||
NSNumber *num;
|
||||
NSFontSymbolicTraits symbolic;
|
||||
NSString *styleName;
|
||||
NSRange range;
|
||||
BOOL isOblique;
|
||||
|
||||
num = (NSNumber *) [traits objectForKey:NSFontSymbolicTrait];
|
||||
// TODO this should really be a uint32_t-specific one
|
||||
symbolic = (NSFontSymbolicTraits) [num unsignedIntegerValue];
|
||||
if ((symbolic & NSFontItalicTrait) == 0)
|
||||
return ic->normal;
|
||||
|
||||
// Okay, now we know it's either Italic or Oblique
|
||||
// Pango's Core Text code just does a g_strrstr() (backwards case-sensitive search) for "Oblique" in the font's style name (see https://git.gnome.org/browse/pango/tree/pango/pangocoretext-fontmap.c); let's do that too I guess
|
||||
// note that NSFontFaceAttribute is the Cocoa equivalent of the style name
|
||||
isOblique = NO; // default value
|
||||
styleName = (NSString *) [desc objectForKey:NSFontFaceAttribute];
|
||||
// TODO is styleName guaranteed?
|
||||
// TODO NSLiteralSearch?
|
||||
range = [styleName rangeOfString:@"Oblique" options:NSCaseInsensitiveSearch];
|
||||
if (range.location != NSNotFound)
|
||||
return ic->oblique;
|
||||
return ic->italic;
|
||||
}
|
||||
|
||||
static NSFontDescriptor *matchTraits(NSFontDescriptor *against, double targetWeight, uiDrawTextItalic targetItalic, double targetStretch)
|
||||
{
|
||||
NSArray<NSFontDescriptor *> *matching;
|
||||
NSUInteger i, n;
|
||||
struct closeness *closeness;
|
||||
NSFontDescriptor *current;
|
||||
NSFontDescriptor *out;
|
||||
|
||||
matching = [against matchingFontDescriptorsWithMandatoryKeys:nil];
|
||||
if (matching == nil)
|
||||
// no matches; give the original back and hope for the best
|
||||
return against;
|
||||
n = [matching count];
|
||||
if (n == 0) {
|
||||
// likewise
|
||||
//TODO [matching release];
|
||||
return against;
|
||||
}
|
||||
|
||||
closeness = (struct closeness *) uiAlloc(n * sizeof (struct closeness), "struct closeness[]");
|
||||
for (i = 0; i < n; i++) {
|
||||
NSDictionary *traits;
|
||||
|
||||
closeness[i].index = i;
|
||||
current = (NSFontDescriptor *) [matching objectAtIndex:i];
|
||||
traits = (NSDictionary *) [current objectForKey:NSFontTraitsAttribute];
|
||||
if (traits == nil) {
|
||||
// couldn't get traits; be safe by ranking it lowest
|
||||
// LONGTERM figure out what the longest possible distances are
|
||||
closeness[i].weight = 3;
|
||||
closeness[i].italic = 2;
|
||||
closeness[i].stretch = 3;
|
||||
continue;
|
||||
}
|
||||
closeness[i].weight = doubleAttr(traits, NSFontWeightTrait) - targetWeight;
|
||||
closeness[i].italic = italicCloseness(current, traits, targetItalic);
|
||||
closeness[i].stretch = doubleAttr(traits, NSFontWidthTrait) - targetStretch;
|
||||
// TODO release traits?
|
||||
}
|
||||
|
||||
// now figure out the 3-space difference between the three and sort by that
|
||||
// TODO merge this loop with the previous loop?
|
||||
for (i = 0; i < n; i++) {
|
||||
double weight, italic, stretch;
|
||||
|
||||
weight = closeness[i].weight;
|
||||
weight *= weight;
|
||||
italic = closeness[i].italic;
|
||||
italic *= italic;
|
||||
stretch = closeness[i].stretch;
|
||||
stretch *= stretch;
|
||||
closeness[i].distance = sqrt(weight + italic + stretch);
|
||||
}
|
||||
qsort_b(closeness, n, sizeof (struct closeness), ^(const void *aa, const void *bb) {
|
||||
const struct closeness *a = (const struct closeness *) aa;
|
||||
const struct closeness *b = (const struct closeness *) bb;
|
||||
|
||||
// via http://www.gnu.org/software/libc/manual/html_node/Comparison-Functions.html#Comparison-Functions
|
||||
// LONGTERM is this really the best way? isn't it the same as if (*a < *b) return -1; if (*a > *b) return 1; return 0; ?
|
||||
return (a->distance > b->distance) - (a->distance < b->distance);
|
||||
});
|
||||
// and the first element of the sorted array is what we want
|
||||
out = (NSFontDescriptor *) [matching objectAtIndex:closeness[0].index];
|
||||
// TODO is this correct?
|
||||
[out retain]; // get rule
|
||||
|
||||
// release everything
|
||||
uiFree(closeness);
|
||||
//TODO [matching release];
|
||||
// and release the original descriptor since we no longer need it
|
||||
[against release];
|
||||
|
||||
return out;
|
||||
}
|
||||
|
||||
// since uiDrawTextWeight effectively corresponds to OS/2 weights (which roughly correspond to GDI, Pango, and DirectWrite weights, and to a lesser(? TODO) degree, CSS weights), let's just do what Core Text does with OS/2 weights
|
||||
// TODO this will not be correct for system fonts, which use cached values that have no relation to the OS/2 weights; we need to figure out how to reconcile these
|
||||
// for more information, see https://bugzilla.gnome.org/show_bug.cgi?id=766148 and TODO_put_blog_post_here_once_I_write_it (TODO keep this line when resolving the above TODO)
|
||||
static const double weightsToCTWeights[] = {
|
||||
-1.0, // 0..99
|
||||
-0.7, // 100..199
|
||||
-0.5, // 200..299
|
||||
-0.23, // 300..399
|
||||
0.0, // 400..499
|
||||
0.2, // 500..599
|
||||
0.3, // 600..699
|
||||
0.4, // 700..799
|
||||
0.6, // 800..899
|
||||
0.8, // 900..999
|
||||
1.0, // 1000
|
||||
};
|
||||
|
||||
static double weightToCTWeight(uiDrawTextWeight weight)
|
||||
{
|
||||
int weightClass;
|
||||
double ctclass;
|
||||
double rest, weightFloor, nextFloor;
|
||||
|
||||
if (weight <= 0)
|
||||
return -1.0;
|
||||
if (weight >= 1000)
|
||||
return 1.0;
|
||||
|
||||
weightClass = weight / 100;
|
||||
rest = (double) weight;
|
||||
weightFloor = (double) (weightClass * 100);
|
||||
nextFloor = (double) ((weightClass + 1) * 100);
|
||||
rest = (rest - weightFloor) / (nextFloor - weightFloor);
|
||||
|
||||
ctclass = weightsToCTWeights[weightClass];
|
||||
return fma(rest,
|
||||
weightsToCTWeights[weightClass + 1] - ctclass,
|
||||
ctclass);
|
||||
}
|
||||
|
||||
// based on what Core Text says about actual fonts (system fonts, system fonts in another folder to avoid using cached values, Adobe Font Folio 11, Google Fonts archive, fonts in Windows 7/8.1/10)
|
||||
static const double stretchesToCTWidths[] = {
|
||||
[uiDrawTextStretchUltraCondensed] = -0.400000,
|
||||
[uiDrawTextStretchExtraCondensed] = -0.300000,
|
||||
[uiDrawTextStretchCondensed] = -0.200000,
|
||||
[uiDrawTextStretchSemiCondensed] = -0.100000,
|
||||
[uiDrawTextStretchNormal] = 0.000000,
|
||||
[uiDrawTextStretchSemiExpanded] = 0.100000,
|
||||
[uiDrawTextStretchExpanded] = 0.200000,
|
||||
[uiDrawTextStretchExtraExpanded] = 0.300000,
|
||||
// this one isn't present in any of the fonts I tested, but it follows naturally from the pattern of the rest, so... (TODO verify by checking the font files directly)
|
||||
[uiDrawTextStretchUltraExpanded] = 0.400000,
|
||||
};
|
||||
|
||||
NSFontDescriptor *fontdescToNSFontDescriptor(uiDrawFontDescriptor *fd)
|
||||
{
|
||||
NSMutableDictionary *attrs;
|
||||
NSFontDescriptor *basedesc;
|
||||
|
||||
attrs = [NSMutableDictionary new];
|
||||
[attrs setObject:[NSString stringWithUTF8String:fd->Family]
|
||||
forKey:NSFontFamilyAttribute];
|
||||
[attrs setObject:[NSNumber numberWithDouble:fd->Size]
|
||||
forKey:NSFontSizeAttribute];
|
||||
|
||||
basedesc = [[NSFontDescriptor alloc] initWithFontAttributes:attrs];
|
||||
[attrs release];
|
||||
return matchTraits(basedesc,
|
||||
weightToCTWeight(fd->Weight),
|
||||
fd->Italic,
|
||||
stretchesToCTWidths[fd->Stretch]);
|
||||
}
|
|
@ -0,0 +1,268 @@
|
|||
// 6 september 2015
|
||||
#import "uipriv_darwin.h"
|
||||
|
||||
// TODO double-check that we are properly handling allocation failures (or just toll free bridge from cocoa)
|
||||
struct uiDrawFontFamilies {
|
||||
CFArrayRef fonts;
|
||||
};
|
||||
|
||||
uiDrawFontFamilies *uiDrawListFontFamilies(void)
|
||||
{
|
||||
uiDrawFontFamilies *ff;
|
||||
|
||||
ff = uiNew(uiDrawFontFamilies);
|
||||
ff->fonts = CTFontManagerCopyAvailableFontFamilyNames();
|
||||
if (ff->fonts == NULL)
|
||||
implbug("error getting available font names (no reason specified) (TODO)");
|
||||
return ff;
|
||||
}
|
||||
|
||||
int uiDrawFontFamiliesNumFamilies(uiDrawFontFamilies *ff)
|
||||
{
|
||||
return CFArrayGetCount(ff->fonts);
|
||||
}
|
||||
|
||||
char *uiDrawFontFamiliesFamily(uiDrawFontFamilies *ff, int n)
|
||||
{
|
||||
CFStringRef familystr;
|
||||
char *family;
|
||||
|
||||
familystr = (CFStringRef) CFArrayGetValueAtIndex(ff->fonts, n);
|
||||
// toll-free bridge
|
||||
family = uiDarwinNSStringToText((NSString *) familystr);
|
||||
// Get Rule means we do not free familystr
|
||||
return family;
|
||||
}
|
||||
|
||||
void uiDrawFreeFontFamilies(uiDrawFontFamilies *ff)
|
||||
{
|
||||
CFRelease(ff->fonts);
|
||||
uiFree(ff);
|
||||
}
|
||||
|
||||
struct uiDrawTextFont {
|
||||
CTFontRef f;
|
||||
};
|
||||
|
||||
uiDrawTextFont *mkTextFont(CTFontRef f, BOOL retain)
|
||||
{
|
||||
uiDrawTextFont *font;
|
||||
|
||||
font = uiNew(uiDrawTextFont);
|
||||
font->f = f;
|
||||
if (retain)
|
||||
CFRetain(font->f);
|
||||
return font;
|
||||
}
|
||||
|
||||
uiDrawTextFont *mkTextFontFromNSFont(NSFont *f)
|
||||
{
|
||||
// toll-free bridging; we do retain, though
|
||||
return mkTextFont((CTFontRef) f, YES);
|
||||
}
|
||||
|
||||
static CFMutableDictionaryRef newAttrList(void)
|
||||
{
|
||||
CFMutableDictionaryRef attr;
|
||||
|
||||
attr = CFDictionaryCreateMutable(NULL, 0, &kCFTypeDictionaryKeyCallBacks, &kCFTypeDictionaryValueCallBacks);
|
||||
if (attr == NULL)
|
||||
complain("error creating attribute dictionary in newAttrList()()");
|
||||
return attr;
|
||||
}
|
||||
|
||||
static void addFontFamilyAttr(CFMutableDictionaryRef attr, const char *family)
|
||||
{
|
||||
CFStringRef cfstr;
|
||||
|
||||
cfstr = CFStringCreateWithCString(NULL, family, kCFStringEncodingUTF8);
|
||||
if (cfstr == NULL)
|
||||
complain("error creating font family name CFStringRef in addFontFamilyAttr()");
|
||||
CFDictionaryAddValue(attr, kCTFontFamilyNameAttribute, cfstr);
|
||||
CFRelease(cfstr); // dictionary holds its own reference
|
||||
}
|
||||
|
||||
static void addFontSizeAttr(CFMutableDictionaryRef attr, double size)
|
||||
{
|
||||
CFNumberRef n;
|
||||
|
||||
n = CFNumberCreate(NULL, kCFNumberDoubleType, &size);
|
||||
CFDictionaryAddValue(attr, kCTFontSizeAttribute, n);
|
||||
CFRelease(n);
|
||||
}
|
||||
|
||||
#if 0
|
||||
TODO
|
||||
// See http://stackoverflow.com/questions/4810409/does-coretext-support-small-caps/4811371#4811371 and https://git.gnome.org/browse/pango/tree/pango/pangocoretext-fontmap.c for what these do
|
||||
// And fortunately, unlike the traits (see below), unmatched features are simply ignored without affecting the other features :D
|
||||
static void addFontSmallCapsAttr(CFMutableDictionaryRef attr)
|
||||
{
|
||||
CFMutableArrayRef outerArray;
|
||||
CFMutableDictionaryRef innerDict;
|
||||
CFNumberRef numType, numSelector;
|
||||
int num;
|
||||
|
||||
outerArray = CFArrayCreateMutable(NULL, 0, &kCFTypeArrayCallBacks);
|
||||
if (outerArray == NULL)
|
||||
complain("error creating outer CFArray for adding small caps attributes in addFontSmallCapsAttr()");
|
||||
|
||||
// Apple's headers say these are deprecated, but a few fonts still rely on them
|
||||
num = kLetterCaseType;
|
||||
numType = CFNumberCreate(NULL, kCFNumberIntType, &num);
|
||||
num = kSmallCapsSelector;
|
||||
numSelector = CFNumberCreate(NULL, kCFNumberIntType, &num);
|
||||
innerDict = newAttrList();
|
||||
CFDictionaryAddValue(innerDict, kCTFontFeatureTypeIdentifierKey, numType);
|
||||
CFRelease(numType);
|
||||
CFDictionaryAddValue(innerDict, kCTFontFeatureSelectorIdentifierKey, numSelector);
|
||||
CFRelease(numSelector);
|
||||
CFArrayAppendValue(outerArray, innerDict);
|
||||
CFRelease(innerDict); // and likewise for CFArray
|
||||
|
||||
// these are the non-deprecated versions of the above; some fonts have these instead
|
||||
num = kLowerCaseType;
|
||||
numType = CFNumberCreate(NULL, kCFNumberIntType, &num);
|
||||
num = kLowerCaseSmallCapsSelector;
|
||||
numSelector = CFNumberCreate(NULL, kCFNumberIntType, &num);
|
||||
innerDict = newAttrList();
|
||||
CFDictionaryAddValue(innerDict, kCTFontFeatureTypeIdentifierKey, numType);
|
||||
CFRelease(numType);
|
||||
CFDictionaryAddValue(innerDict, kCTFontFeatureSelectorIdentifierKey, numSelector);
|
||||
CFRelease(numSelector);
|
||||
CFArrayAppendValue(outerArray, innerDict);
|
||||
CFRelease(innerDict); // and likewise for CFArray
|
||||
|
||||
CFDictionaryAddValue(attr, kCTFontFeatureSettingsAttribute, outerArray);
|
||||
CFRelease(outerArray);
|
||||
}
|
||||
#endif
|
||||
|
||||
#if 0
|
||||
// Named constants for these were NOT added until 10.11, and even then they were added as external symbols instead of macros, so we can't use them directly :(
|
||||
// kode54 got these for me before I had access to El Capitan; thanks to him.
|
||||
#define ourNSFontWeightUltraLight -0.800000
|
||||
#define ourNSFontWeightThin -0.600000
|
||||
#define ourNSFontWeightLight -0.400000
|
||||
#define ourNSFontWeightRegular 0.000000
|
||||
#define ourNSFontWeightMedium 0.230000
|
||||
#define ourNSFontWeightSemibold 0.300000
|
||||
#define ourNSFontWeightBold 0.400000
|
||||
#define ourNSFontWeightHeavy 0.560000
|
||||
#define ourNSFontWeightBlack 0.620000
|
||||
#endif
|
||||
|
||||
// Now remember what I said earlier about having to add the small caps traits after calling the above? This gets a dictionary back so we can do so.
|
||||
CFMutableDictionaryRef extractAttributes(CTFontDescriptorRef desc)
|
||||
{
|
||||
CFDictionaryRef dict;
|
||||
CFMutableDictionaryRef mdict;
|
||||
|
||||
dict = CTFontDescriptorCopyAttributes(desc);
|
||||
// this might not be mutable, so make a mutable copy
|
||||
mdict = CFDictionaryCreateMutableCopy(NULL, 0, dict);
|
||||
CFRelease(dict);
|
||||
return mdict;
|
||||
}
|
||||
|
||||
uiDrawTextFont *uiDrawLoadClosestFont(const uiDrawTextFontDescriptor *desc)
|
||||
{
|
||||
CTFontRef f;
|
||||
CFMutableDictionaryRef attr;
|
||||
CTFontDescriptorRef cfdesc;
|
||||
|
||||
attr = newAttrList();
|
||||
addFontFamilyAttr(attr, desc->Family);
|
||||
addFontSizeAttr(attr, desc->Size);
|
||||
|
||||
// now we have to do the traits matching, so create a descriptor, match the traits, and then get the attributes back
|
||||
cfdesc = CTFontDescriptorCreateWithAttributes(attr);
|
||||
// TODO release attr?
|
||||
cfdesc = matchTraits(cfdesc, desc->Weight, desc->Italic, desc->Stretch);
|
||||
|
||||
// specify the initial size again just to be safe
|
||||
f = CTFontCreateWithFontDescriptor(cfdesc, desc->Size, NULL);
|
||||
// TODO release cfdesc?
|
||||
|
||||
return mkTextFont(f, NO); // we hold the initial reference; no need to retain again
|
||||
}
|
||||
|
||||
void uiDrawFreeTextFont(uiDrawTextFont *font)
|
||||
{
|
||||
CFRelease(font->f);
|
||||
uiFree(font);
|
||||
}
|
||||
|
||||
uintptr_t uiDrawTextFontHandle(uiDrawTextFont *font)
|
||||
{
|
||||
return (uintptr_t) (font->f);
|
||||
}
|
||||
|
||||
void uiDrawTextFontDescribe(uiDrawTextFont *font, uiDrawTextFontDescriptor *desc)
|
||||
{
|
||||
// TODO
|
||||
}
|
||||
|
||||
// text sizes and user space points are identical:
|
||||
// - https://developer.apple.com/library/mac/documentation/TextFonts/Conceptual/CocoaTextArchitecture/TypoFeatures/TextSystemFeatures.html#//apple_ref/doc/uid/TP40009459-CH6-51627-BBCCHIFF text points are 72 per inch
|
||||
// - https://developer.apple.com/library/mac/documentation/Cocoa/Conceptual/CocoaDrawingGuide/Transforms/Transforms.html#//apple_ref/doc/uid/TP40003290-CH204-SW5 user space points are 72 per inch
|
||||
void uiDrawTextFontGetMetrics(uiDrawTextFont *font, uiDrawTextFontMetrics *metrics)
|
||||
{
|
||||
metrics->Ascent = CTFontGetAscent(font->f);
|
||||
metrics->Descent = CTFontGetDescent(font->f);
|
||||
metrics->Leading = CTFontGetLeading(font->f);
|
||||
metrics->UnderlinePos = CTFontGetUnderlinePosition(font->f);
|
||||
metrics->UnderlineThickness = CTFontGetUnderlineThickness(font->f);
|
||||
}
|
||||
|
||||
// LONGTERM allow line separation and leading to be factored into a wrapping text layout
|
||||
|
||||
// TODO reconcile differences in character wrapping on platforms
|
||||
void uiDrawTextLayoutExtents(uiDrawTextLayout *layout, double *width, double *height)
|
||||
{
|
||||
struct framesetter fs;
|
||||
|
||||
mkFramesetter(layout, &fs);
|
||||
*width = fs.extents.width;
|
||||
*height = fs.extents.height;
|
||||
freeFramesetter(&fs);
|
||||
}
|
||||
|
||||
// LONGTERM provide an equivalent to CTLineGetTypographicBounds() on uiDrawTextLayout?
|
||||
|
||||
// LONGTERM keep this for later features and documentation purposes
|
||||
#if 0
|
||||
|
||||
// LONGTERM provide a way to get the image bounds as a separate function later
|
||||
bounds = CTLineGetImageBounds(line, c);
|
||||
// though CTLineGetImageBounds() returns CGRectNull on error, it also returns CGRectNull on an empty string, so we can't reasonably check for error
|
||||
|
||||
// CGContextSetTextPosition() positions at the baseline in the case of CTLineDraw(); we need the top-left corner instead
|
||||
CTLineGetTypographicBounds(line, &yoff, NULL, NULL);
|
||||
// remember that we're flipped, so we subtract
|
||||
y -= yoff;
|
||||
CGContextSetTextPosition(c, x, y);
|
||||
#endif
|
||||
|
||||
#if 0
|
||||
void uiDrawTextLayoutSetColor(uiDrawTextLayout *layout, int startChar, int endChar, double r, double g, double b, double a)
|
||||
{
|
||||
CGColorSpaceRef colorspace;
|
||||
CGFloat components[4];
|
||||
CGColorRef color;
|
||||
|
||||
// for consistency with windows, use sRGB
|
||||
colorspace = CGColorSpaceCreateWithName(kCGColorSpaceSRGB);
|
||||
components[0] = r;
|
||||
components[1] = g;
|
||||
components[2] = b;
|
||||
components[3] = a;
|
||||
color = CGColorCreate(colorspace, components);
|
||||
CGColorSpaceRelease(colorspace);
|
||||
|
||||
CFAttributedStringSetAttribute(layout->mas,
|
||||
rangeToCFRange(),
|
||||
kCTForegroundColorAttributeName,
|
||||
color);
|
||||
CGColorRelease(color); // TODO safe?
|
||||
}
|
||||
#endif
|
|
@ -0,0 +1,314 @@
|
|||
// 2 january 2017
|
||||
#import "uipriv_darwin.h"
|
||||
#import "draw.h"
|
||||
|
||||
// TODO on an empty string nLines == 0
|
||||
// we must prevent this somehow
|
||||
// TODO in general, every function could be more robust, but we cannot have a situation where there are zero lines
|
||||
// TODO what happens to extents if only whitespace?
|
||||
|
||||
struct uiDrawTextLayout {
|
||||
CFAttributedStringRef attrstr;
|
||||
|
||||
// the width as passed into uiDrawTextLayout constructors
|
||||
double width;
|
||||
|
||||
CTFramesetterRef framesetter;
|
||||
|
||||
// the *actual* size of the frame
|
||||
// note: technically, metrics returned from frame are relative to CGPathGetPathBoundingBox(tl->path)
|
||||
// however, from what I can gather, for a path created by CGPathCreateWithRect(), like we do (with a NULL transform), CGPathGetPathBoundingBox() seems to just return the standardized form of the rect used to create the path
|
||||
// (this I confirmed through experimentation)
|
||||
// so we can just use tl->size for adjustments
|
||||
// we don't need to adjust coordinates by any origin since our rect origin is (0, 0)
|
||||
CGSize size;
|
||||
|
||||
CGPathRef path;
|
||||
CTFrameRef frame;
|
||||
|
||||
CFArrayRef lines;
|
||||
CFIndex nLines;
|
||||
// we compute this once when first creating the layout
|
||||
uiDrawTextLayoutLineMetrics *lineMetrics;
|
||||
|
||||
NSArray *backgroundBlocks;
|
||||
|
||||
// for converting CFAttributedString indices from/to byte offsets
|
||||
size_t *u8tou16;
|
||||
size_t nUTF8;
|
||||
size_t *u16tou8;
|
||||
size_t nUTF16;
|
||||
};
|
||||
|
||||
// TODO document that lines may or may not overlap because ours do in the case of multiple combining characters
|
||||
static uiDrawTextLayoutLineMetrics *computeLineMetrics(CTFrameRef frame, CGSize size)
|
||||
{
|
||||
uiDrawTextLayoutLineMetrics *metrics;
|
||||
CFArrayRef lines;
|
||||
CTLineRef line;
|
||||
CFIndex i, n;
|
||||
CGFloat ypos;
|
||||
CGRect bounds, boundsNoLeading;
|
||||
CGFloat ascent, descent, leading;
|
||||
CGPoint *origins;
|
||||
|
||||
lines = CTFrameGetLines(frame);
|
||||
n = CFArrayGetCount(lines);
|
||||
metrics = (uiDrawTextLayoutLineMetrics *) uiAlloc(n * sizeof (uiDrawTextLayoutLineMetrics), "uiDrawTextLayoutLineMetrics[] (text layout)");
|
||||
|
||||
origins = (CGPoint *) uiAlloc(n * sizeof (CGPoint), "CGPoint[] (text layout)");
|
||||
CTFrameGetLineOrigins(frame, CFRangeMake(0, n), origins);
|
||||
|
||||
ypos = size.height;
|
||||
for (i = 0; i < n; i++) {
|
||||
line = (CTLineRef) CFArrayGetValueAtIndex(lines, i);
|
||||
bounds = CTLineGetBoundsWithOptions(line, 0);
|
||||
boundsNoLeading = CTLineGetBoundsWithOptions(line, kCTLineBoundsExcludeTypographicLeading);
|
||||
|
||||
// this is equivalent to boundsNoLeading.size.height + boundsNoLeading.origin.y (manually verified)
|
||||
ascent = bounds.size.height + bounds.origin.y;
|
||||
descent = -boundsNoLeading.origin.y;
|
||||
leading = -bounds.origin.y - descent;
|
||||
|
||||
// Core Text always rounds these up for paragraph style calculations; there is a flag to control it but it's inaccessible (and this behavior is turned off for old versions of iPhoto)
|
||||
ascent = floor(ascent + 0.5);
|
||||
descent = floor(descent + 0.5);
|
||||
if (leading > 0)
|
||||
leading = floor(leading + 0.5);
|
||||
|
||||
metrics[i].X = origins[i].x;
|
||||
metrics[i].Y = origins[i].y - descent - leading;
|
||||
metrics[i].Width = bounds.size.width;
|
||||
metrics[i].Height = ascent + descent + leading;
|
||||
|
||||
metrics[i].BaselineY = origins[i].y;
|
||||
metrics[i].Ascent = ascent;
|
||||
metrics[i].Descent = descent;
|
||||
metrics[i].Leading = leading;
|
||||
|
||||
// TODO
|
||||
metrics[i].ParagraphSpacingBefore = 0;
|
||||
metrics[i].LineHeightSpace = 0;
|
||||
metrics[i].LineSpacing = 0;
|
||||
metrics[i].ParagraphSpacing = 0;
|
||||
|
||||
// and finally advance to the next line
|
||||
ypos += metrics[i].Height;
|
||||
}
|
||||
|
||||
// okay, but now all these metrics are unflipped
|
||||
// we need to flip them
|
||||
for (i = 0; i < n; i++) {
|
||||
metrics[i].Y = size.height - metrics[i].Y;
|
||||
// go from bottom-left corner to top-left
|
||||
metrics[i].Y -= metrics[i].Height;
|
||||
metrics[i].BaselineY = size.height - metrics[i].BaselineY;
|
||||
}
|
||||
|
||||
uiFree(origins);
|
||||
return metrics;
|
||||
}
|
||||
|
||||
uiDrawTextLayout *uiDrawNewTextLayout(uiDrawTextLayoutParams *p)
|
||||
{
|
||||
uiDrawTextLayout *tl;
|
||||
CGFloat cgwidth;
|
||||
CFRange range, unused;
|
||||
CGRect rect;
|
||||
|
||||
tl = uiNew(uiDrawTextLayout);
|
||||
tl->attrstr = attrstrToCoreFoundation(p, &(tl->backgroundBlocks));
|
||||
range.location = 0;
|
||||
range.length = CFAttributedStringGetLength(tl->attrstr);
|
||||
tl->width = p->Width;
|
||||
|
||||
// TODO kCTParagraphStyleSpecifierMaximumLineSpacing, kCTParagraphStyleSpecifierMinimumLineSpacing, kCTParagraphStyleSpecifierLineSpacingAdjustment for line spacing
|
||||
tl->framesetter = CTFramesetterCreateWithAttributedString(tl->attrstr);
|
||||
if (tl->framesetter == NULL) {
|
||||
// TODO
|
||||
}
|
||||
|
||||
cgwidth = (CGFloat) (tl->width);
|
||||
if (cgwidth < 0)
|
||||
cgwidth = CGFLOAT_MAX;
|
||||
tl->size = CTFramesetterSuggestFrameSizeWithConstraints(tl->framesetter,
|
||||
range,
|
||||
// TODO kCTFramePathWidthAttributeName?
|
||||
NULL,
|
||||
CGSizeMake(cgwidth, CGFLOAT_MAX),
|
||||
&unused); // not documented as accepting NULL (TODO really?)
|
||||
|
||||
rect.origin = CGPointZero;
|
||||
rect.size = tl->size;
|
||||
tl->path = CGPathCreateWithRect(rect, NULL);
|
||||
tl->frame = CTFramesetterCreateFrame(tl->framesetter,
|
||||
range,
|
||||
tl->path,
|
||||
// TODO kCTFramePathWidthAttributeName?
|
||||
NULL);
|
||||
if (tl->frame == NULL) {
|
||||
// TODO
|
||||
}
|
||||
|
||||
tl->lines = CTFrameGetLines(tl->frame);
|
||||
tl->nLines = CFArrayGetCount(tl->lines);
|
||||
tl->lineMetrics = computeLineMetrics(tl->frame, tl->size);
|
||||
|
||||
// and finally copy the UTF-8/UTF-16 conversion tables
|
||||
tl->u8tou16 = attrstrCopyUTF8ToUTF16(p->String, &(tl->nUTF8));
|
||||
tl->u16tou8 = attrstrCopyUTF16ToUTF8(p->String, &(tl->nUTF16));
|
||||
|
||||
return tl;
|
||||
}
|
||||
|
||||
void uiDrawFreeTextLayout(uiDrawTextLayout *tl)
|
||||
{
|
||||
uiFree(tl->u16tou8);
|
||||
uiFree(tl->u8tou16);
|
||||
[tl->backgroundBlocks release];
|
||||
uiFree(tl->lineMetrics);
|
||||
CFRelease(tl->frame);
|
||||
CFRelease(tl->path);
|
||||
CFRelease(tl->framesetter);
|
||||
CFRelease(tl->attrstr);
|
||||
uiFree(tl);
|
||||
}
|
||||
|
||||
// TODO document that (x,y) is the top-left corner of the *entire frame*
|
||||
void uiDrawText(uiDrawContext *c, uiDrawTextLayout *tl, double x, double y)
|
||||
{
|
||||
backgroundBlock b;
|
||||
CGAffineTransform textMatrix;
|
||||
|
||||
CGContextSaveGState(c->c);
|
||||
// save the text matrix because it's not part of the graphics state
|
||||
textMatrix = CGContextGetTextMatrix(c->c);
|
||||
|
||||
for (b in tl->backgroundBlocks)
|
||||
b(c, tl, x, y);
|
||||
|
||||
// Core Text doesn't draw onto a flipped view correctly; we have to pretend it was unflipped
|
||||
// see the iOS bits of the first example at https://developer.apple.com/library/mac/documentation/StringsTextFonts/Conceptual/CoreText_Programming/LayoutOperations/LayoutOperations.html#//apple_ref/doc/uid/TP40005533-CH12-SW1 (iOS is naturally flipped)
|
||||
// TODO how is this affected by a non-identity CTM?
|
||||
CGContextTranslateCTM(c->c, 0, c->height);
|
||||
CGContextScaleCTM(c->c, 1.0, -1.0);
|
||||
CGContextSetTextMatrix(c->c, CGAffineTransformIdentity);
|
||||
|
||||
// wait, that's not enough; we need to offset y values to account for our new flipping
|
||||
// TODO explain this calculation
|
||||
y = c->height - tl->size.height - y;
|
||||
|
||||
// CTFrameDraw() draws in the path we specified when creating the frame
|
||||
// this means that in our usage, CTFrameDraw() will draw at (0,0)
|
||||
// so move the origin to be at (x,y) instead
|
||||
// TODO are the signs correct?
|
||||
CGContextTranslateCTM(c->c, x, y);
|
||||
|
||||
CTFrameDraw(tl->frame, c->c);
|
||||
|
||||
CGContextSetTextMatrix(c->c, textMatrix);
|
||||
CGContextRestoreGState(c->c);
|
||||
}
|
||||
|
||||
// TODO document that the width and height of a layout is not necessarily the sum of the widths and heights of its constituent lines
|
||||
// TODO width doesn't include trailing whitespace...
|
||||
// TODO figure out how paragraph spacing should play into this
|
||||
void uiDrawTextLayoutExtents(uiDrawTextLayout *tl, double *width, double *height)
|
||||
{
|
||||
*width = tl->size.width;
|
||||
*height = tl->size.height;
|
||||
}
|
||||
|
||||
int uiDrawTextLayoutNumLines(uiDrawTextLayout *tl)
|
||||
{
|
||||
return tl->nLines;
|
||||
}
|
||||
|
||||
void uiDrawTextLayoutLineByteRange(uiDrawTextLayout *tl, int line, size_t *start, size_t *end)
|
||||
{
|
||||
CTLineRef lr;
|
||||
CFRange range;
|
||||
|
||||
lr = (CTLineRef) CFArrayGetValueAtIndex(tl->lines, line);
|
||||
range = CTLineGetStringRange(lr);
|
||||
*start = tl->u16tou8[range.location];
|
||||
*end = tl->u16tou8[range.location + range.length];
|
||||
}
|
||||
|
||||
void uiDrawTextLayoutLineGetMetrics(uiDrawTextLayout *tl, int line, uiDrawTextLayoutLineMetrics *m)
|
||||
{
|
||||
*m = tl->lineMetrics[line];
|
||||
}
|
||||
|
||||
// in the case of overlapping lines, we read lines first to last and use their bottommost point (Y + Height) to determine where the next line should start for hit-testing
|
||||
// TODO should we document this?
|
||||
void uiDrawTextLayoutHitTest(uiDrawTextLayout *tl, double x, double y, size_t *pos, int *line)
|
||||
{
|
||||
int i;
|
||||
CTLineRef ln;
|
||||
CFIndex p;
|
||||
|
||||
for (i = 0; i < tl->nLines; i++) {
|
||||
double ltop, lbottom;
|
||||
|
||||
ltop = tl->lineMetrics[i].Y;
|
||||
lbottom = ltop + tl->lineMetrics[i].Height;
|
||||
// y will already >= ltop at this point since the past lbottom should == (or at least >=, see above) ltop
|
||||
if (y < lbottom)
|
||||
break;
|
||||
}
|
||||
if (i == tl->nLines)
|
||||
i--;
|
||||
*line = i;
|
||||
|
||||
ln = (CTLineRef) CFArrayGetValueAtIndex(tl->lines, i);
|
||||
// note: according to the docs, we pass a y of 0 for this since the is the baseline of that line (the point is relative to the line)
|
||||
// note: x is relative to the line origin
|
||||
x -= tl->lineMetrics[*line].X;
|
||||
p = CTLineGetStringIndexForPosition(ln, CGPointMake(x, 0));
|
||||
if (p == kCFNotFound) {
|
||||
// TODO
|
||||
}
|
||||
*pos = tl->u16tou8[p];
|
||||
}
|
||||
|
||||
double uiDrawTextLayoutByteLocationInLine(uiDrawTextLayout *tl, size_t pos, int line)
|
||||
{
|
||||
CTLineRef lr;
|
||||
CFRange range;
|
||||
|
||||
pos = tl->u8tou16[pos];
|
||||
if (line < 0 || line >= tl->nLines)
|
||||
return -1;
|
||||
lr = (CTLineRef) CFArrayGetValueAtIndex(tl->lines, line);
|
||||
range = CTLineGetStringRange(lr);
|
||||
// note: >, not >=, because the position at end is valid!
|
||||
if (pos < range.location || pos > (range.location + range.length))
|
||||
return -1;
|
||||
// no point in checking the return; we already validated everything and 0 is a valid return for the first index :/
|
||||
// note: the result is relative to the line origin (TODO find documentation to support this)
|
||||
// TODO document that these functions do this
|
||||
return CTLineGetOffsetForStringIndex(lr, pos, NULL) + tl->lineMetrics[line].X;
|
||||
}
|
||||
|
||||
void caretDrawParams(uiDrawContext *c, double height, struct caretDrawParams *p)
|
||||
{
|
||||
NSColor *cc;
|
||||
CGFloat cr, cg, cb, ca;
|
||||
|
||||
// Interface Builder sets this as the insertion point color for a NSTextView by default
|
||||
cc = [NSColor controlTextColor];
|
||||
// the given color may not be an RGBA color, which will cause the -getRed:green:blue:alpha: call to throw an exception
|
||||
cc = [cc colorUsingColorSpace:[NSColorSpace sRGBColorSpace]];
|
||||
[cc getRed:&cr green:&cg blue:&cb alpha:&ca];
|
||||
p->r = cr;
|
||||
p->g = cg;
|
||||
p->b = cb;
|
||||
p->a = ca;
|
||||
// both cc and the controlTextColor it was made from will be autoreleased since they aren't new or init calls
|
||||
// TODO disabled carets have some blending applied...
|
||||
|
||||
// okay there doesn't seem to be any defined metrics for these, argh...
|
||||
p->width = 1;
|
||||
p->xoff = 0;
|
||||
}
|
|
@ -0,0 +1,60 @@
|
|||
darwin
|
||||
int uiDrawTextLayoutNumLines(uiDrawTextLayout *tl)
|
||||
{
|
||||
return CFArrayGetCount([tl->forLines lines]);
|
||||
}
|
||||
|
||||
void uiDrawTextLayoutLineByteRange(uiDrawTextLayout *tl, int line, size_t *start, size_t *end)
|
||||
{
|
||||
CTLineRef lr;
|
||||
CFRange range;
|
||||
|
||||
lr = (CTLineRef) CFArrayGetValueAtIndex([tl->forLines lines], line);
|
||||
range = CTLineGetStringRange(lr);
|
||||
*start = tl->u16tou8[range.location];
|
||||
if (tl->empty)
|
||||
*end = *start;
|
||||
else
|
||||
*end = tl->u16tou8[range.location + range.length];
|
||||
}
|
||||
|
||||
|
||||
unix
|
||||
int uiDrawTextLayoutNumLines(uiDrawTextLayout *tl)
|
||||
{
|
||||
return pango_layout_get_line_count(tl->layout);
|
||||
}
|
||||
|
||||
void uiDrawTextLayoutLineByteRange(uiDrawTextLayout *tl, int line, size_t *start, size_t *end)
|
||||
{
|
||||
PangoLayoutLine *pll;
|
||||
|
||||
pll = pango_layout_get_line_readonly(tl->layout, line);
|
||||
*start = pll->start_index;
|
||||
*end = pll->start_index + pll->length;
|
||||
// TODO unref pll?
|
||||
}
|
||||
|
||||
|
||||
windows
|
||||
int uiDrawTextLayoutNumLines(uiDrawTextLayout *tl)
|
||||
{
|
||||
return 0;
|
||||
#if 0
|
||||
TODO
|
||||
return tl->nLines;
|
||||
#endif
|
||||
}
|
||||
|
||||
// DirectWrite doesn't provide a direct way to do this, so we have to do this manually
|
||||
// TODO does that comment still apply here or to the code at the top of this file?
|
||||
void uiDrawTextLayoutLineByteRange(uiDrawTextLayout *tl, int line, size_t *start, size_t *end)
|
||||
{
|
||||
#if 0
|
||||
TODO
|
||||
*start = tl->lineInfo[line].startPos;
|
||||
*start = tl->u16tou8[*start];
|
||||
*end = tl->lineInfo[line].endPos - tl->lineInfo[line].newlineCount;
|
||||
*end = tl->u16tou8[*end];
|
||||
#endif
|
||||
}
|
|
@ -0,0 +1,72 @@
|
|||
|
||||
typedef struct uiDrawTextLayoutLineMetrics uiDrawTextLayoutLineMetrics;
|
||||
|
||||
// Height will equal ParagraphSpacingBefore + LineHeightSpace + Ascent + Descent + Leading + LineSpacing + ParagraphSpacing.
|
||||
// The above values are listed in vertical order, from top to bottom.
|
||||
// Ascent + Descent + Leading will give you the typographic bounds
|
||||
// of the text. BaselineY is the boundary between Ascent and Descent.
|
||||
// X, Y, and BaselineY are all in the layout's coordinate system, so the
|
||||
// start point of the baseline will be at (X, BaselineY). All values are
|
||||
// nonnegative.
|
||||
struct uiDrawTextLayoutLineMetrics {
|
||||
// This describes the overall bounding box of the line.
|
||||
double X;
|
||||
double Y;
|
||||
double Width;
|
||||
double Height;
|
||||
|
||||
// This describes the typographic bounds of the line.
|
||||
double BaselineY;
|
||||
double Ascent;
|
||||
double Descent;
|
||||
double Leading;
|
||||
|
||||
// This describes any additional whitespace.
|
||||
// TODO come up with better names for these.
|
||||
double ParagraphSpacingBefore;
|
||||
double LineHeightSpace;
|
||||
double LineSpacing;
|
||||
double ParagraphSpacing;
|
||||
|
||||
// TODO trailing whitespace?
|
||||
};
|
||||
|
||||
// uiDrawTextLayoutNumLines() returns the number of lines in tl.
|
||||
// This number will always be greater than or equal to 1; a text
|
||||
// layout with no text only has one line.
|
||||
_UI_EXTERN int uiDrawTextLayoutNumLines(uiDrawTextLayout *tl);
|
||||
|
||||
// uiDrawTextLayoutLineByteRange() returns the byte indices of the
|
||||
// text that falls into the given line of tl as [start, end).
|
||||
_UI_EXTERN void uiDrawTextLayoutLineByteRange(uiDrawTextLayout *tl, int line, size_t *start, size_t *end);
|
||||
|
||||
_UI_EXTERN void uiDrawTextLayoutLineGetMetrics(uiDrawTextLayout *tl, int line, uiDrawTextLayoutLineMetrics *m);
|
||||
|
||||
// TODO rewrite this documentation
|
||||
|
||||
// uiDrawTextLayoutHitTest() returns the byte offset and line closest
|
||||
// to the given point. The point is relative to the top-left of the layout.
|
||||
// If the point is outside the layout itself, the closest point is chosen;
|
||||
// this allows the function to be used for cursor positioning with the
|
||||
// mouse. Do keep the returned line in mind if used in this way; the
|
||||
// user might click on the end of a line, at which point the cursor
|
||||
// might be at the trailing edge of the last grapheme on the line
|
||||
// (subject to the operating system's APIs).
|
||||
_UI_EXTERN void uiDrawTextLayoutHitTest(uiDrawTextLayout *tl, double x, double y, size_t *pos, int *line);
|
||||
|
||||
// uiDrawTextLayoutByteLocationInLine() returns the point offset
|
||||
// into the given line that the given byte position stands. This is
|
||||
// relative to the line's X position (as returned by
|
||||
// uiDrawTextLayoutLineGetMetrics()), which in turn is relative to
|
||||
// the top-left of the layout. This function can be used for cursor
|
||||
// positioning: if start and end are the start and end of the line
|
||||
// (as returned by uiDrawTextLayoutLineByteRange()), you will get
|
||||
// the correct offset, even if pos is at the end of the line. If pos is not
|
||||
// in the range [start, end], a negative value will be returned,
|
||||
// indicating you need to move the cursor to another line.
|
||||
// TODO make sure this works right for right-aligned and center-aligned lines and justified lines and RTL text
|
||||
_UI_EXTERN double uiDrawTextLayoutByteLocationInLine(uiDrawTextLayout *tl, size_t pos, int line);
|
||||
|
||||
_UI_EXTERN void uiDrawCaret(uiDrawContext *c, double x, double y, uiDrawTextLayout *layout, size_t pos, int *line);
|
||||
// TODO allow blinking
|
||||
// TODO allow secondary carets
|
|
@ -0,0 +1,171 @@
|
|||
diff --git a/darwin/attrstr.m b/darwin/attrstr.m
|
||||
index fd45ec25..86039fad 100644
|
||||
--- a/darwin/attrstr.m
|
||||
+++ b/darwin/attrstr.m
|
||||
@@ -403,8 +403,13 @@ static CTParagraphStyleRef mkParagraphStyle(uiDrawTextLayoutParams *p)
|
||||
return ps;
|
||||
}
|
||||
|
||||
-CFAttributedStringRef attrstrToCoreFoundation(uiDrawTextLayoutParams *p, NSArray **backgroundBlocks)
|
||||
+static const UniChar emptyChars[] = { 0x20, 0x0 };
|
||||
+static const CFIndex emptyCharCount = 1;
|
||||
+
|
||||
+CFAttributedStringRef attrstrToCoreFoundation(uiDrawTextLayoutParams *p, BOOL *isEmpty, NSArray **backgroundBlocks)
|
||||
{
|
||||
+ const UniChar *chars;
|
||||
+ CFIndex charCount;
|
||||
CFStringRef cfstr;
|
||||
CFMutableDictionaryRef defaultAttrs;
|
||||
CTFontRef defaultCTFont;
|
||||
@@ -413,7 +418,15 @@ CFAttributedStringRef attrstrToCoreFoundation(uiDrawTextLayoutParams *p, NSArray
|
||||
CFMutableAttributedStringRef mas;
|
||||
struct foreachParams fep;
|
||||
|
||||
- cfstr = CFStringCreateWithCharacters(NULL, attrstrUTF16(p->String), attrstrUTF16Len(p->String));
|
||||
+ *isEmpty = NO;
|
||||
+ chars = attrstrUTF16(p->String);
|
||||
+ charCount = attrstrUTF16Len(p->String);
|
||||
+ if (charCount == 0) {
|
||||
+ *isEmpty = YES;
|
||||
+ chars = emptyChars;
|
||||
+ charCount = emptyCharCount;
|
||||
+ }
|
||||
+ cfstr = CFStringCreateWithCharacters(NULL, chars, charCount);
|
||||
if (cfstr == NULL) {
|
||||
// TODO
|
||||
}
|
||||
diff --git a/darwin/drawtext.m b/darwin/drawtext.m
|
||||
index 1fa5920e..65912383 100644
|
||||
--- a/darwin/drawtext.m
|
||||
+++ b/darwin/drawtext.m
|
||||
@@ -2,13 +2,16 @@
|
||||
#import "uipriv_darwin.h"
|
||||
#import "draw.h"
|
||||
|
||||
-// TODO on an empty string nLines == 0
|
||||
-// we must prevent this somehow
|
||||
-// TODO in general, every function could be more robust, but we cannot have a situation where there are zero lines
|
||||
+// TODO in general, every function could be more robust
|
||||
// TODO what happens to extents if only whitespace?
|
||||
+// TODO for empty layouts:
|
||||
+// - check if alignment correct compared to other OSs, or expected behavior at all
|
||||
+// - double-check if uiAttributedString allows zero-length attributes; I forget if I did
|
||||
|
||||
struct uiDrawTextLayout {
|
||||
CFAttributedStringRef attrstr;
|
||||
+ // this is needed because Core Text will give us an empty line array on a frame made with an empty string
|
||||
+ BOOL isEmpty;
|
||||
|
||||
// the width as passed into uiDrawTextLayout constructors
|
||||
double width;
|
||||
@@ -41,7 +44,7 @@
|
||||
};
|
||||
|
||||
// TODO document that lines may or may not overlap because ours do in the case of multiple combining characters
|
||||
-static uiDrawTextLayoutLineMetrics *computeLineMetrics(CTFrameRef frame, CGSize size)
|
||||
+static uiDrawTextLayoutLineMetrics *computeLineMetrics(CTFrameRef frame, CGSize size, BOOL isEmpty)
|
||||
{
|
||||
uiDrawTextLayoutLineMetrics *metrics;
|
||||
CFArrayRef lines;
|
||||
@@ -79,6 +82,8 @@
|
||||
metrics[i].X = origins[i].x;
|
||||
metrics[i].Y = origins[i].y - descent - leading;
|
||||
metrics[i].Width = bounds.size.width;
|
||||
+ if (isEmpty)
|
||||
+ metrics[i].Width = 0;
|
||||
metrics[i].Height = ascent + descent + leading;
|
||||
|
||||
metrics[i].BaselineY = origins[i].y;
|
||||
@@ -117,7 +122,7 @@
|
||||
CGRect rect;
|
||||
|
||||
tl = uiNew(uiDrawTextLayout);
|
||||
- tl->attrstr = attrstrToCoreFoundation(p, &(tl->backgroundBlocks));
|
||||
+ tl->attrstr = attrstrToCoreFoundation(p, &(tl->isEmpty), &(tl->backgroundBlocks));
|
||||
range.location = 0;
|
||||
range.length = CFAttributedStringGetLength(tl->attrstr);
|
||||
tl->width = p->Width;
|
||||
@@ -152,7 +157,7 @@
|
||||
|
||||
tl->lines = CTFrameGetLines(tl->frame);
|
||||
tl->nLines = CFArrayGetCount(tl->lines);
|
||||
- tl->lineMetrics = computeLineMetrics(tl->frame, tl->size);
|
||||
+ tl->lineMetrics = computeLineMetrics(tl->frame, tl->size, tl->isEmpty);
|
||||
|
||||
// and finally copy the UTF-8/UTF-16 conversion tables
|
||||
tl->u8tou16 = attrstrCopyUTF8ToUTF16(p->String, &(tl->nUTF8));
|
||||
@@ -180,6 +185,9 @@ void uiDrawText(uiDrawContext *c, uiDrawTextLayout *tl, double x, double y)
|
||||
backgroundBlock b;
|
||||
CGAffineTransform textMatrix;
|
||||
|
||||
+ if (tl->isEmpty)
|
||||
+ return;
|
||||
+
|
||||
CGContextSaveGState(c->c);
|
||||
// save the text matrix because it's not part of the graphics state
|
||||
textMatrix = CGContextGetTextMatrix(c->c);
|
||||
@@ -216,6 +224,8 @@ void uiDrawText(uiDrawContext *c, uiDrawTextLayout *tl, double x, double y)
|
||||
void uiDrawTextLayoutExtents(uiDrawTextLayout *tl, double *width, double *height)
|
||||
{
|
||||
*width = tl->size.width;
|
||||
+ if (tl->isEmpty)
|
||||
+ *width = 0;
|
||||
*height = tl->size.height;
|
||||
}
|
||||
|
||||
@@ -233,6 +243,8 @@ void uiDrawTextLayoutLineByteRange(uiDrawTextLayout *tl, int line, size_t *start
|
||||
range = CTLineGetStringRange(lr);
|
||||
*start = tl->u16tou8[range.location];
|
||||
*end = tl->u16tou8[range.location + range.length];
|
||||
+ if (tl->isEmpty)
|
||||
+ *start = *end;
|
||||
}
|
||||
|
||||
void uiDrawTextLayoutLineGetMetrics(uiDrawTextLayout *tl, int line, uiDrawTextLayoutLineMetrics *m)
|
||||
@@ -262,14 +274,17 @@ void uiDrawTextLayoutHitTest(uiDrawTextLayout *tl, double x, double y, size_t *p
|
||||
*line = i;
|
||||
|
||||
ln = (CTLineRef) CFArrayGetValueAtIndex(tl->lines, i);
|
||||
- // note: according to the docs, we pass a y of 0 for this since the is the baseline of that line (the point is relative to the line)
|
||||
- // note: x is relative to the line origin
|
||||
x -= tl->lineMetrics[*line].X;
|
||||
- p = CTLineGetStringIndexForPosition(ln, CGPointMake(x, 0));
|
||||
- if (p == kCFNotFound) {
|
||||
- // TODO
|
||||
+ *pos = 0;
|
||||
+ if (!tl->isEmpty) {
|
||||
+ // note: according to the docs, we pass a y of 0 for this since the is the baseline of that line (the point is relative to the line)
|
||||
+ // note: x is relative to the line origin
|
||||
+ p = CTLineGetStringIndexForPosition(ln, CGPointMake(x, 0));
|
||||
+ if (p == kCFNotFound) {
|
||||
+ // TODO
|
||||
+ }
|
||||
+ *pos = tl->u16tou8[p];
|
||||
}
|
||||
- *pos = tl->u16tou8[p];
|
||||
}
|
||||
|
||||
double uiDrawTextLayoutByteLocationInLine(uiDrawTextLayout *tl, size_t pos, int line)
|
||||
@@ -282,6 +297,9 @@ void uiDrawTextLayoutHitTest(uiDrawTextLayout *tl, double x, double y, size_t *p
|
||||
return -1;
|
||||
lr = (CTLineRef) CFArrayGetValueAtIndex(tl->lines, line);
|
||||
range = CTLineGetStringRange(lr);
|
||||
+ // TODO is the behavior of this part correct?
|
||||
+ if (tl->isEmpty)
|
||||
+ range.length = 0;
|
||||
// note: >, not >=, because the position at end is valid!
|
||||
if (pos < range.location || pos > (range.location + range.length))
|
||||
return -1;
|
||||
diff --git a/darwin/uipriv_darwin.h b/darwin/uipriv_darwin.h
|
||||
index 0303c32c..d44ee410 100644
|
||||
--- a/darwin/uipriv_darwin.h
|
||||
+++ b/darwin/uipriv_darwin.h
|
||||
@@ -151,7 +151,7 @@ extern void fontdescFromCTFontDescriptor(CTFontDescriptorRef ctdesc, uiDrawFontD
|
||||
extern void initUnderlineColors(void);
|
||||
extern void uninitUnderlineColors(void);
|
||||
typedef void (^backgroundBlock)(uiDrawContext *c, uiDrawTextLayout *layout, double x, double y);
|
||||
-extern CFAttributedStringRef attrstrToCoreFoundation(uiDrawTextLayoutParams *p, NSArray **backgroundBlocks);
|
||||
+extern CFAttributedStringRef attrstrToCoreFoundation(uiDrawTextLayoutParams *p, BOOL *isEmpty, NSArray **backgroundBlocks);
|
||||
|
||||
// aat.m
|
||||
typedef void (^aatBlock)(uint16_t type, uint16_t selector);
|
|
@ -0,0 +1,217 @@
|
|||
// 6 september 2015
|
||||
#include "uipriv_unix.h"
|
||||
#include "draw.h"
|
||||
|
||||
struct uiDrawFontFamilies {
|
||||
PangoFontFamily **f;
|
||||
int n;
|
||||
};
|
||||
|
||||
uiDrawFontFamilies *uiDrawListFontFamilies(void)
|
||||
{
|
||||
uiDrawFontFamilies *ff;
|
||||
PangoFontMap *map;
|
||||
|
||||
ff = uiNew(uiDrawFontFamilies);
|
||||
map = pango_cairo_font_map_get_default();
|
||||
pango_font_map_list_families(map, &(ff->f), &(ff->n));
|
||||
// do not free map; it's a shared resource
|
||||
return ff;
|
||||
}
|
||||
|
||||
int uiDrawFontFamiliesNumFamilies(uiDrawFontFamilies *ff)
|
||||
{
|
||||
return ff->n;
|
||||
}
|
||||
|
||||
char *uiDrawFontFamiliesFamily(uiDrawFontFamilies *ff, int n)
|
||||
{
|
||||
PangoFontFamily *f;
|
||||
|
||||
f = ff->f[n];
|
||||
return uiUnixStrdupText(pango_font_family_get_name(f));
|
||||
}
|
||||
|
||||
void uiDrawFreeFontFamilies(uiDrawFontFamilies *ff)
|
||||
{
|
||||
g_free(ff->f);
|
||||
uiFree(ff);
|
||||
}
|
||||
|
||||
struct uiDrawTextFont {
|
||||
PangoFont *f;
|
||||
};
|
||||
|
||||
uiDrawTextFont *mkTextFont(PangoFont *f, gboolean ref)
|
||||
{
|
||||
uiDrawTextFont *font;
|
||||
|
||||
font = uiNew(uiDrawTextFont);
|
||||
font->f = f;
|
||||
if (ref)
|
||||
g_object_ref(font->f);
|
||||
return font;
|
||||
}
|
||||
|
||||
|
||||
|
||||
PangoFont *pangoDescToPangoFont(PangoFontDescription *pdesc)
|
||||
{
|
||||
PangoFont *f;
|
||||
PangoContext *context;
|
||||
|
||||
// in this case, the context is necessary for the metrics to be correct
|
||||
context = mkGenericPangoCairoContext();
|
||||
f = pango_font_map_load_font(pango_cairo_font_map_get_default(), context, pdesc);
|
||||
if (f == NULL) {
|
||||
// LONGTERM
|
||||
g_error("[libui] no match in pangoDescToPangoFont(); report to andlabs");
|
||||
}
|
||||
g_object_unref(context);
|
||||
return f;
|
||||
}
|
||||
|
||||
uiDrawTextFont *uiDrawLoadClosestFont(const uiDrawTextFontDescriptor *desc)
|
||||
{
|
||||
PangoFont *f;
|
||||
PangoFontDescription *pdesc;
|
||||
|
||||
pdesc = pango_font_description_new();
|
||||
pango_font_description_set_family(pdesc,
|
||||
desc->Family);
|
||||
pango_font_description_set_size(pdesc,
|
||||
(gint) (desc->Size * PANGO_SCALE));
|
||||
pango_font_description_set_weight(pdesc,
|
||||
pangoWeights[desc->Weight]);
|
||||
pango_font_description_set_style(pdesc,
|
||||
pangoItalics[desc->Italic]);
|
||||
pango_font_description_set_stretch(pdesc,
|
||||
pangoStretches[desc->Stretch]);
|
||||
f = pangoDescToPangoFont(pdesc);
|
||||
pango_font_description_free(pdesc);
|
||||
return mkTextFont(f, FALSE); // we hold the initial reference; no need to ref
|
||||
}
|
||||
|
||||
void uiDrawFreeTextFont(uiDrawTextFont *font)
|
||||
{
|
||||
g_object_unref(font->f);
|
||||
uiFree(font);
|
||||
}
|
||||
|
||||
uintptr_t uiDrawTextFontHandle(uiDrawTextFont *font)
|
||||
{
|
||||
return (uintptr_t) (font->f);
|
||||
}
|
||||
|
||||
void uiDrawTextFontDescribe(uiDrawTextFont *font, uiDrawTextFontDescriptor *desc)
|
||||
{
|
||||
PangoFontDescription *pdesc;
|
||||
|
||||
// this creates a copy; we free it later
|
||||
pdesc = pango_font_describe(font->f);
|
||||
|
||||
// TODO
|
||||
|
||||
pango_font_description_free(pdesc);
|
||||
}
|
||||
|
||||
// See https://developer.gnome.org/pango/1.30/pango-Cairo-Rendering.html#pango-Cairo-Rendering.description
|
||||
// Note that we convert to double before dividing to make sure the floating-point stuff is right
|
||||
#define pangoToCairo(pango) (((double) (pango)) / PANGO_SCALE)
|
||||
#define cairoToPango(cairo) ((gint) ((cairo) * PANGO_SCALE))
|
||||
|
||||
void uiDrawTextFontGetMetrics(uiDrawTextFont *font, uiDrawTextFontMetrics *metrics)
|
||||
{
|
||||
PangoFontMetrics *pm;
|
||||
|
||||
pm = pango_font_get_metrics(font->f, NULL);
|
||||
metrics->Ascent = pangoToCairo(pango_font_metrics_get_ascent(pm));
|
||||
metrics->Descent = pangoToCairo(pango_font_metrics_get_descent(pm));
|
||||
// Pango doesn't seem to expose this :( Use 0 and hope for the best.
|
||||
metrics->Leading = 0;
|
||||
metrics->UnderlinePos = pangoToCairo(pango_font_metrics_get_underline_position(pm));
|
||||
metrics->UnderlineThickness = pangoToCairo(pango_font_metrics_get_underline_thickness(pm));
|
||||
pango_font_metrics_unref(pm);
|
||||
}
|
||||
|
||||
// note: PangoCairoLayouts are tied to a given cairo_t, so we can't store one in this device-independent structure
|
||||
struct uiDrawTextLayout {
|
||||
char *s;
|
||||
ptrdiff_t *graphemes;
|
||||
PangoFont *defaultFont;
|
||||
double width;
|
||||
PangoAttrList *attrs;
|
||||
};
|
||||
|
||||
uiDrawTextLayout *uiDrawNewTextLayout(const char *text, uiDrawTextFont *defaultFont, double width)
|
||||
{
|
||||
uiDrawTextLayout *layout;
|
||||
PangoContext *context;
|
||||
|
||||
layout = uiNew(uiDrawTextLayout);
|
||||
layout->s = g_strdup(text);
|
||||
context = mkGenericPangoCairoContext();
|
||||
layout->graphemes = graphemes(layout->s, context);
|
||||
g_object_unref(context);
|
||||
layout->defaultFont = defaultFont->f;
|
||||
g_object_ref(layout->defaultFont); // retain a copy
|
||||
uiDrawTextLayoutSetWidth(layout, width);
|
||||
layout->attrs = pango_attr_list_new();
|
||||
return layout;
|
||||
}
|
||||
|
||||
void uiDrawFreeTextLayout(uiDrawTextLayout *layout)
|
||||
{
|
||||
pango_attr_list_unref(layout->attrs);
|
||||
g_object_unref(layout->defaultFont);
|
||||
uiFree(layout->graphemes);
|
||||
g_free(layout->s);
|
||||
uiFree(layout);
|
||||
}
|
||||
|
||||
void uiDrawTextLayoutSetWidth(uiDrawTextLayout *layout, double width)
|
||||
{
|
||||
layout->width = width;
|
||||
}
|
||||
|
||||
static void prepareLayout(uiDrawTextLayout *layout, PangoLayout *pl)
|
||||
{
|
||||
// again, this makes a copy
|
||||
desc = pango_font_describe(layout->defaultFont);
|
||||
|
||||
pango_layout_set_attributes(pl, layout->attrs);
|
||||
}
|
||||
|
||||
void uiDrawText(uiDrawContext *c, double x, double y, uiDrawTextLayout *layout)
|
||||
{
|
||||
PangoLayout *pl;
|
||||
|
||||
pl = pango_cairo_create_layout(c->cr);
|
||||
}
|
||||
|
||||
static void addAttr(uiDrawTextLayout *layout, PangoAttribute *attr, int startChar, int endChar)
|
||||
{
|
||||
attr->start_index = layout->graphemes[startChar];
|
||||
attr->end_index = layout->graphemes[endChar];
|
||||
pango_attr_list_insert(layout->attrs, attr);
|
||||
// pango_attr_list_insert() takes attr; we don't free it
|
||||
}
|
||||
|
||||
void uiDrawTextLayoutSetColor(uiDrawTextLayout *layout, int startChar, int endChar, double r, double g, double b, double a)
|
||||
{
|
||||
PangoAttribute *attr;
|
||||
guint16 rr, gg, bb, aa;
|
||||
|
||||
rr = (guint16) (r * 65535);
|
||||
gg = (guint16) (g * 65535);
|
||||
bb = (guint16) (b * 65535);
|
||||
aa = (guint16) (a * 65535);
|
||||
|
||||
attr = pango_attr_foreground_new(rr, gg, bb);
|
||||
addAttr(layout, attr, startChar, endChar);
|
||||
|
||||
// TODO what if aa == 0?
|
||||
attr = FUTURE_pango_attr_foreground_alpha_new(aa);
|
||||
if (attr != NULL)
|
||||
addAttr(layout, attr, startChar, endChar);
|
||||
}
|
Some files were not shown because too many files have changed in this diff Show More
Loading…
Reference in New Issue