mirror of
https://github.com/libretro/RetroArch.git
synced 2026-11-13 18:46:11 -10:00
Compare commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
8e8bdaaab3 | ||
|
|
6c91c39aae | ||
|
|
59aaabb9bf | ||
|
|
456e1b033f | ||
|
|
0d59994c42 | ||
|
|
ea9991f749 | ||
|
|
139d95c563 | ||
|
|
87b4576e78 | ||
|
|
e70c15e641 | ||
|
|
73e00e021a | ||
|
|
b4912db587 | ||
|
|
6fc48a39d9 | ||
|
|
6d3292945e | ||
|
|
9242cdeda9 | ||
|
|
7a402b97ec | ||
|
|
d4ffc6801f | ||
|
|
dff78cf0cc | ||
|
|
4e37b784d2 | ||
|
|
828f4aa8b9 | ||
|
|
6952d6bd12 | ||
|
|
366c73f429 | ||
|
|
5909d72a09 | ||
|
|
8e2689fca2 | ||
|
|
33a2d58891 | ||
|
|
04b8f0584e | ||
|
|
4af569fbcc | ||
|
|
e3818d50ac | ||
|
|
4dcb151f03 | ||
|
|
e56250c84f | ||
|
|
c08fc10ba4 | ||
|
|
a795237898 | ||
|
|
8dbafceebe | ||
|
|
47cf178309 | ||
|
|
adc3b4b726 | ||
|
|
e743875de2 | ||
|
|
8acd734be2 | ||
|
|
d0e24b02b4 | ||
|
|
a243278f53 | ||
|
|
887364062a | ||
|
|
d1f0f19824 | ||
|
|
b8008d74e5 | ||
|
|
2a1c8c7a6c | ||
|
|
ef83954376 | ||
|
|
7a44d29ed4 | ||
|
|
877d14b1e7 | ||
|
|
494e229b03 | ||
|
|
1093c1bac8 | ||
|
|
0eb78a26b3 | ||
|
|
442339a63e | ||
|
|
1a3d79b458 | ||
|
|
f54a834ada | ||
|
|
6804432fa7 | ||
|
|
6f4697d60f | ||
|
|
cf7fd7b34b | ||
|
|
5c7fd82a40 | ||
|
|
db48561289 | ||
|
|
0d86027cd5 | ||
|
|
e32ec709b3 | ||
|
|
89b695dae3 | ||
|
|
ef79711837 | ||
|
|
5c6b230e42 | ||
|
|
b2fd9c5b05 | ||
|
|
1887862a03 | ||
|
|
a66f9249df | ||
|
|
c438e556b1 | ||
|
|
990f746aee | ||
|
|
b760a407fd | ||
|
|
e34f8e0911 | ||
|
|
f1069ee7ed | ||
|
|
573cf1df1a | ||
|
|
09cf3262f1 | ||
|
|
ade279c0ba | ||
|
|
a73695aca9 | ||
|
|
31317f7cbf | ||
|
|
09c980c645 | ||
|
|
19efd41164 | ||
|
|
e4a8793dcc | ||
|
|
5de11039c6 | ||
|
|
aad8e551fe | ||
|
|
27966879b0 | ||
|
|
ea16a51342 | ||
|
|
1bcec096bb | ||
|
|
64686b46b1 | ||
|
|
c5c5af0507 | ||
|
|
14f7ee6116 | ||
|
|
b40f59a6ec | ||
|
|
950264e89e | ||
|
|
28ade72baf | ||
|
|
07dd7c8ccb | ||
|
|
e9d738226a | ||
|
|
db24e4da42 | ||
|
|
f608ac3556 | ||
|
|
a0859229f9 | ||
|
|
8fc6427d0e | ||
|
|
058116b8da | ||
|
|
00ae975c6f | ||
|
|
28147d8aa6 | ||
|
|
777308c42c | ||
|
|
736477d4e4 | ||
|
|
e060029d4c | ||
|
|
559fabeb08 | ||
|
|
4c877148fe | ||
|
|
648cf89893 | ||
|
|
c2c98184e6 | ||
|
|
147374c9c1 | ||
|
|
fd0c9fb9c1 | ||
|
|
24878694f8 | ||
|
|
e262ae3326 | ||
|
|
91d1b18124 | ||
|
|
8d4e06856b | ||
|
|
7c54cebfb3 | ||
|
|
d469be76a7 | ||
|
|
7165ba5245 | ||
|
|
81273cedcf | ||
|
|
0a9143b999 | ||
|
|
c8c3f9b842 | ||
|
|
e324b4a074 | ||
|
|
6a47658502 | ||
|
|
b906da5131 | ||
|
|
8e0e99ba66 | ||
|
|
b1daf18772 | ||
|
|
8424c26068 | ||
|
|
9dfb39c672 | ||
|
|
76899d0d33 | ||
|
|
42012389cd | ||
|
|
c9ad8cd1f9 | ||
|
|
02cfe1f7c0 | ||
|
|
f6458334b6 | ||
|
|
0ed4b97bbc | ||
|
|
f9765a98fb | ||
|
|
dce21063dd | ||
|
|
45cb6925cf | ||
|
|
2756217d9b | ||
|
|
6ec27a7e5d | ||
|
|
a0cbb1ee43 | ||
|
|
1666827ad4 | ||
|
|
2558ca2fb6 | ||
|
|
782b9eef0e | ||
|
|
950b77c0ca | ||
|
|
26a9648c4f | ||
|
|
c384f56e4f | ||
|
|
772c126f1e | ||
|
|
4177286dbb | ||
|
|
4dd3d897dc | ||
|
|
c4f7111aa3 | ||
|
|
b0805d5e1d | ||
|
|
8ca5c7558f | ||
|
|
45e823dec9 | ||
|
|
51ca9825dd | ||
|
|
cb2fd48bb6 | ||
|
|
f4283f6de0 | ||
|
|
a98e8bc915 | ||
|
|
9f760af6c6 | ||
|
|
2f9ad9c3fa | ||
|
|
ae2435cf16 | ||
|
|
f546020429 | ||
|
|
00412c9836 | ||
|
|
9c32d03c1e | ||
|
|
6611ccf3fa | ||
|
|
cfde480e82 | ||
|
|
39e72ae0f8 | ||
|
|
8bda6e8a3a | ||
|
|
f1ccd29e77 | ||
|
|
d037546a13 | ||
|
|
921418af03 | ||
|
|
61cc899867 | ||
|
|
ae5535c425 | ||
|
|
fc2637a696 | ||
|
|
80be260b68 | ||
|
|
74a22169ac | ||
|
|
cb63584511 | ||
|
|
e3015088c2 | ||
|
|
a443196726 | ||
|
|
a5bb61684c | ||
|
|
dad0fcbbe8 | ||
|
|
22139dc4a0 | ||
|
|
4e66ec28cb | ||
|
|
4a9c7384c7 | ||
|
|
29af83f8e3 | ||
|
|
16f59aba96 | ||
|
|
edb70936ae | ||
|
|
7a490aa6d6 | ||
|
|
805c2ad529 | ||
|
|
5d12368c83 | ||
|
|
ff98730906 | ||
|
|
abf95744ee | ||
|
|
530d79f8e3 | ||
|
|
d557deb70d | ||
|
|
d2f870a2a1 | ||
|
|
23d9637dc0 | ||
|
|
3310939983 | ||
|
|
34236932a3 | ||
|
|
5101895cc9 | ||
|
|
dfd2c45454 | ||
|
|
2f832d145f | ||
|
|
4e49155147 | ||
|
|
ee275c85d8 | ||
|
|
795ae524e9 | ||
|
|
a4a027be67 | ||
|
|
c3851d42e6 | ||
|
|
535dbe0aaa | ||
|
|
05558697ca | ||
|
|
3bac7cc7ef | ||
|
|
dccc9711d9 | ||
|
|
365cfd22ee | ||
|
|
9272d26269 | ||
|
|
cea3cab3f4 | ||
|
|
97de6deb56 | ||
|
|
1ecdbf4050 | ||
|
|
b943060797 | ||
|
|
ca80f13b2b | ||
|
|
3a5a2d9de6 | ||
|
|
aef66e3d9e | ||
|
|
63c0cae326 | ||
|
|
8d358899df | ||
|
|
c0cb042998 | ||
|
|
a37f91c2a1 | ||
|
|
ee76e54617 | ||
|
|
3f3b30b5dc | ||
|
|
988468f691 | ||
|
|
3c758609b5 | ||
|
|
e979eece1c | ||
|
|
383d810fd4 | ||
|
|
86a107afc9 | ||
|
|
d14fa0c8b9 | ||
|
|
060603b8d3 | ||
|
|
5bece2cbcc | ||
|
|
c5b36320df | ||
|
|
c07380293e | ||
|
|
8e69a348e6 | ||
|
|
b0ad2c4a2b | ||
|
|
f0606d60d2 | ||
|
|
6a36453f20 | ||
|
|
4ed715d0f3 | ||
|
|
23b2e4488a | ||
|
|
5a85c53174 | ||
|
|
cc06b8b62e | ||
|
|
0df4186191 | ||
|
|
4c31d41920 | ||
|
|
3d4e36455d | ||
|
|
c882e64653 | ||
|
|
b6cd19a83c | ||
|
|
10492c1ee7 | ||
|
|
08118cb445 | ||
|
|
7649164f51 | ||
|
|
c7615fdf00 | ||
|
|
6359fe44af | ||
|
|
2e1ad9684a | ||
|
|
09922f5ee7 | ||
|
|
46552de0f6 | ||
|
|
cfa0d74df9 | ||
|
|
b521236c3e | ||
|
|
7b09cd34db | ||
|
|
5d2ab5bf82 | ||
|
|
bf72dafe5c | ||
|
|
2e7b4242f7 | ||
|
|
56733c6f18 | ||
|
|
0afb629c81 | ||
|
|
af477ff317 | ||
|
|
86dbcaf563 | ||
|
|
ab936adf1c | ||
|
|
7c1d3991f8 | ||
|
|
a550f83452 | ||
|
|
02db71f529 | ||
|
|
c426f68379 | ||
|
|
8f616c13e4 | ||
|
|
0bc4254ab1 | ||
|
|
2cd38297c2 | ||
|
|
5caabab2eb | ||
|
|
ceed309b19 | ||
|
|
ba31285160 | ||
|
|
b345a4b860 | ||
|
|
999fd0f120 | ||
|
|
38b2f32dc0 | ||
|
|
c6379a7c62 | ||
|
|
c0e5f28498 | ||
|
|
c7e6f19920 | ||
|
|
7382a38485 | ||
|
|
6d4d9aa1fb | ||
|
|
912c7265e6 | ||
|
|
e52e70aa5a | ||
|
|
e3192ad3c5 | ||
|
|
ba36055632 | ||
|
|
1a7247c1bb | ||
|
|
8c8334e5a5 | ||
|
|
5a4b5da68e | ||
|
|
ace3401042 | ||
|
|
50ba42aa57 | ||
|
|
6b369775cf | ||
|
|
54ef8f1399 | ||
|
|
99a8d2ec86 | ||
|
|
169aea5ae1 | ||
|
|
740ad823ab | ||
|
|
c150b4f6dc | ||
|
|
4387dd7c15 | ||
|
|
86f7972aa8 | ||
|
|
e259a8c63b | ||
|
|
944eb01fb4 | ||
|
|
a8d3b205ca | ||
|
|
27d9b04080 | ||
|
|
8bb3852944 | ||
|
|
a17e23dc6c | ||
|
|
2cd552b760 | ||
|
|
cf8783644d | ||
|
|
a4192bf99b | ||
|
|
9858b94e87 | ||
|
|
7af556e200 | ||
|
|
70956dca6f | ||
|
|
2db79242c9 | ||
|
|
a973d5dc8a | ||
|
|
9862b75b4b | ||
|
|
32234b44d3 | ||
|
|
2aa095aa4b | ||
|
|
449a33cf17 | ||
|
|
b671f68e00 | ||
|
|
05c7bdedbe | ||
|
|
9195eeb1e1 | ||
|
|
2a7bb2def1 | ||
|
|
b1904bcfa6 | ||
|
|
c8e6a98651 | ||
|
|
24b3705fa1 | ||
|
|
aa4d68760b | ||
|
|
34af7f301d | ||
|
|
33f80371fc | ||
|
|
7ea86f2b16 | ||
|
|
93eb7b349a | ||
|
|
3de9bb9fe1 | ||
|
|
70e741a70b | ||
|
|
d11b9e80b4 | ||
|
|
d865c4cdcf | ||
|
|
e668d10a23 | ||
|
|
cbe2d9a941 | ||
|
|
fb303a4d40 | ||
|
|
6f5220d774 | ||
|
|
eff03a1fd5 | ||
|
|
40a4a5a733 | ||
|
|
ceaeee33e0 | ||
|
|
3a3d9cd41e | ||
|
|
f5360d0d82 | ||
|
|
2ef75cb300 | ||
|
|
a6b24b4d42 | ||
|
|
cacb69542c | ||
|
|
15b05fb057 | ||
|
|
150bc81ee1 | ||
|
|
2c092a882e | ||
|
|
38b9ecb1b1 | ||
|
|
3256af4ffe | ||
|
|
0996291d91 | ||
|
|
d78ea9cffc | ||
|
|
12c8a5d6a4 | ||
|
|
572f7bea2b | ||
|
|
14d66749cc | ||
|
|
3687a706b9 | ||
|
|
e7eac6004b | ||
|
|
7a20875668 | ||
|
|
3566c41b9c | ||
|
|
b8853b76a5 | ||
|
|
aed8ec7741 | ||
|
|
387d087fcd | ||
|
|
d05dfcce78 | ||
|
|
8ed147e1f3 | ||
|
|
d5d0f580b0 | ||
|
|
b5323b65b1 | ||
|
|
1b9ae701f5 | ||
|
|
fad37a3bd1 | ||
|
|
50b838d8ed | ||
|
|
a3b1b9f690 | ||
|
|
bbb2a7d6f4 | ||
|
|
c13c7d0df2 | ||
|
|
a60049de90 | ||
|
|
6506b71776 | ||
|
|
c072894a93 | ||
|
|
29d26f29a6 | ||
|
|
2a92b22128 | ||
|
|
252dc1b130 | ||
|
|
aa455a7239 | ||
|
|
adae0656eb | ||
|
|
2c587731bd | ||
|
|
4d231d8c27 | ||
|
|
266f7a3ad9 | ||
|
|
668ab0f0c4 | ||
|
|
2a0e34d66e | ||
|
|
e517e9b96a | ||
|
|
99aed1723f | ||
|
|
ad542822ed | ||
|
|
1b4d622bb4 | ||
|
|
9b71cdbb0f | ||
|
|
02381aabef | ||
|
|
790cf1c27e | ||
|
|
507379406d | ||
|
|
82913126ef | ||
|
|
432e5c02de | ||
|
|
8f93a6f379 | ||
|
|
106ae968a0 | ||
|
|
2008327742 | ||
|
|
2a8753d13c | ||
|
|
afa01c5421 | ||
|
|
4f4ef6e815 | ||
|
|
6f637df360 | ||
|
|
b64fe2adc2 | ||
|
|
1b8eb669d8 | ||
|
|
84e0251a86 | ||
|
|
a22062f4f9 | ||
|
|
32525ea0d3 | ||
|
|
d560660bdc | ||
|
|
f7f613a244 | ||
|
|
fcbcfab3d9 | ||
|
|
c2882164f9 | ||
|
|
c101b4ccb3 | ||
|
|
d889f594a3 | ||
|
|
e09075dc25 | ||
|
|
b8bbf77196 | ||
|
|
6804540635 | ||
|
|
08621cbd20 | ||
|
|
b35fdee119 | ||
|
|
826a3eff46 | ||
|
|
9c6b6ee4f0 | ||
|
|
24ae754901 | ||
|
|
dcd71b403f | ||
|
|
cfc7f4c8c7 | ||
|
|
58233137b0 | ||
|
|
70e85ad719 | ||
|
|
f5379815da | ||
|
|
2a4c857df9 | ||
|
|
df09b0dc9b | ||
|
|
a02cade1f2 | ||
|
|
12ae59a197 | ||
|
|
b9036e26a7 | ||
|
|
093c1c5a12 | ||
|
|
e66c2d937a | ||
|
|
d3d9229f1d | ||
|
|
43c42c28e6 | ||
|
|
69c05cd7b4 | ||
|
|
c1ce224bd7 | ||
|
|
b14593081d | ||
|
|
6ea0eae4bb | ||
|
|
4b29c9dcad | ||
|
|
15be2d6b34 | ||
|
|
9fe27117f2 | ||
|
|
75bdde358a | ||
|
|
8a3567ab61 | ||
|
|
058dc172c7 | ||
|
|
6bf26dc420 | ||
|
|
1cf6f2f20f | ||
|
|
bcaa24cf7b | ||
|
|
ecfb0dda43 | ||
|
|
3c4f13e9f5 | ||
|
|
2211f4391c | ||
|
|
730f8d8996 | ||
|
|
aa99f4f5ba | ||
|
|
d2cdc97e8f | ||
|
|
b75bb07329 | ||
|
|
7d71746b21 | ||
|
|
24107229fc | ||
|
|
63d36f658d | ||
|
|
9b1ab23aa1 | ||
|
|
c800477bc8 | ||
|
|
28ef2d6487 | ||
|
|
56e63d4460 | ||
|
|
2444d8b39c | ||
|
|
bcb97caa4e | ||
|
|
a7e19107ce | ||
|
|
5733d8ebd5 | ||
|
|
3a590f126d | ||
|
|
434bec8775 | ||
|
|
913eab54ff | ||
|
|
c3650f077a | ||
|
|
e067dac2a4 | ||
|
|
a62052eb0a | ||
|
|
abd22ae770 | ||
|
|
5e2e586f6e | ||
|
|
26711ec0d5 | ||
|
|
6ad1a5cc8f | ||
|
|
e73a7c24e2 | ||
|
|
e247867bd7 | ||
|
|
dc521b42d1 | ||
|
|
525c8f7c8e | ||
|
|
633cadcde7 | ||
|
|
912068f850 | ||
|
|
2ce1dbcf02 | ||
|
|
3a141cb891 | ||
|
|
2fda0b7e66 | ||
|
|
0dc0cc5bf0 | ||
|
|
b4902e7df5 | ||
|
|
95495cbf62 | ||
|
|
87c94982c2 | ||
|
|
e5d930ae0a | ||
|
|
ff3a0d9a3c | ||
|
|
2aa004a1ce | ||
|
|
344a4099eb | ||
|
|
c2a4fa83e8 | ||
|
|
15b1bb40a4 | ||
|
|
e2d8771baf | ||
|
|
790fcd2264 | ||
|
|
c576faae0c | ||
|
|
760fee18dd | ||
|
|
1521a8dd38 | ||
|
|
c9b88fa1ab | ||
|
|
708596ba84 | ||
|
|
d27252daed | ||
|
|
9d91a38602 | ||
|
|
22dee03c65 | ||
|
|
4eb0f21b8f | ||
|
|
21cb98e3fb | ||
|
|
8a618f8e95 | ||
|
|
1d2b5fb534 | ||
|
|
f6ca13b658 | ||
|
|
f8397a17c3 | ||
|
|
49753a7385 | ||
|
|
abba2f387e | ||
|
|
39b1e43cbc | ||
|
|
aed74a008c | ||
|
|
2a6ffd9c3b | ||
|
|
ffc393cafc | ||
|
|
260f9532e9 | ||
|
|
22eb2f8f3b | ||
|
|
933627353b | ||
|
|
5ac0141a9a | ||
|
|
d1dc2e9670 | ||
|
|
3c07209a1a | ||
|
|
e9d8525e29 | ||
|
|
b4b3887a1a | ||
|
|
58de712b77 | ||
|
|
fb33dcf17b | ||
|
|
edc64991f3 | ||
|
|
3e2c713893 | ||
|
|
645bae4931 | ||
|
|
60296c0b7d | ||
|
|
6995154a9a | ||
|
|
e9f9702a2d | ||
|
|
529ecf1e36 | ||
|
|
95f4910a94 | ||
|
|
0a7968761f | ||
|
|
4293e5b334 | ||
|
|
5636b0fdd6 | ||
|
|
c9a9880a36 | ||
|
|
fd11190e9d | ||
|
|
fe829e5eb0 | ||
|
|
bec00d986c | ||
|
|
768dbb5210 | ||
|
|
626b8b5dae | ||
|
|
5de9ba0bc2 | ||
|
|
4813019c58 | ||
|
|
0c7f209e65 | ||
|
|
ccd5dec266 | ||
|
|
5ba36ad3a1 | ||
|
|
36f700927e | ||
|
|
fa8db9b4d2 | ||
|
|
39679ccdce | ||
|
|
63d2e543af | ||
|
|
540fdf358c | ||
|
|
25c18bb3ce | ||
|
|
17d699c75b | ||
|
|
8fbcf34e02 | ||
|
|
175aaadb0d | ||
|
|
b4173ee0b4 | ||
|
|
e58eee37bc | ||
|
|
7c41e298a2 | ||
|
|
d561c7ff2d | ||
|
|
431f1b4b7f | ||
|
|
ddb4867c52 | ||
|
|
e1041030a2 | ||
|
|
b11620e1eb | ||
|
|
50b07ad8c4 | ||
|
|
ba71dda1b2 | ||
|
|
be98b0ea0b | ||
|
|
7cda786906 | ||
|
|
03ee897ae2 | ||
|
|
c16bb5ff5e | ||
|
|
c1c5ebf5f6 | ||
|
|
bfe8a8d48a | ||
|
|
2127f5202b | ||
|
|
e3e41e433e | ||
|
|
8b972a8d82 | ||
|
|
94af3addd0 | ||
|
|
f5a12d63cc | ||
|
|
688414c5bb | ||
|
|
d3868cc06f | ||
|
|
20e7ac4280 | ||
|
|
411ade7fb9 | ||
|
|
6bbe74d260 | ||
|
|
ff3a750e71 | ||
|
|
721a8f6d47 | ||
|
|
0ece49df50 | ||
|
|
c444659683 | ||
|
|
84e799fd09 | ||
|
|
e1392b0fd4 | ||
|
|
e34dc17859 | ||
|
|
15ca6c9507 | ||
|
|
56f0bc2dcc | ||
|
|
fc8392d1e7 | ||
|
|
9a382ce6f4 | ||
|
|
8487966c0b | ||
|
|
97063701f8 | ||
|
|
b74b0ceed5 | ||
|
|
5d1cfc47cc | ||
|
|
aaa590c817 | ||
|
|
57514668f3 | ||
|
|
af3b2f22ec | ||
|
|
737adeabe7 | ||
|
|
33c96bf6f2 | ||
|
|
69061352a7 | ||
|
|
38e5d062e8 | ||
|
|
0aa4edafcf | ||
|
|
57c20bdee7 | ||
|
|
b7666b56d1 | ||
|
|
89b345e262 | ||
|
|
b661466516 | ||
|
|
505d45fdc7 | ||
|
|
64cd82ceb2 | ||
|
|
aec2dcb62a | ||
|
|
bc64ad7774 | ||
|
|
02fb4cf718 | ||
|
|
4ad5f67663 | ||
|
|
e7d623f971 | ||
|
|
5c5c5ecbdf | ||
|
|
379546207f | ||
|
|
091eff9879 | ||
|
|
98beb5d64c |
No files matched your search
+110
-43
@@ -1,92 +1,159 @@
|
||||
# 1.6.1 (future)
|
||||
- AUDIO: Mute now no longer disables/enables audio but instead properly mutes the audio volume.
|
||||
Mute is also independent from the audio mixer volume.
|
||||
# 1.6.8 (future)
|
||||
|
||||
# 1.6.7 (future)
|
||||
- SCANNER: Fix directory scanning.
|
||||
- SCANNER: Fix file scanning.
|
||||
- COMMON: Fix 'Disk Image Append' option.
|
||||
- FREEBSD: Compatibility fixes for Video4Linux2 camera driver.
|
||||
- GUI: (MaterialUI) Add disk image append icons.
|
||||
- GUI: (MaterialUI) Improve word wrapping when menu icons are enabled.
|
||||
- GUI: (MaterialUI) Add User Interface -> Appearance -> Menu Icons Enable. You can turn on/off the icons on the lefthand side of the menu entries.
|
||||
- GUI: Performance optimizations for XMB menu driver - only calculates visible items.
|
||||
- LOCALIZATION: Update Italian translation.
|
||||
|
||||
# 1.6.6 (future)
|
||||
- 3DS: Fixes serious performance regression that affected every core; rewind was always implicitly enabled.
|
||||
- AUDIO: MOD/S3M/XM sound should now be properly mixed in with the core's sound.
|
||||
- GUI: Visual makeover of MaterialUI.
|
||||
- GUI: Added 'Music', 'Images' and 'Video' collection options to RGUI/MaterialUI.
|
||||
- GUI: Allow the user to add 'Favorites'.
|
||||
- GUI: Allow the user to rename entries.
|
||||
- GUI: Performance optimizations for XMB menu driver.
|
||||
- LOCALIZATION: Update Italian translation
|
||||
- INPUT: Overlay controller response - when we press buttons on the gamepad or keyboard, the corresponding buttons on the overlay will be highlighted as well.
|
||||
- NETBSD: Silence some compilation warnings.
|
||||
- COMMON: Fixed bug 'Deleting an entry from a playlist would not update the list view inside XMB'.
|
||||
- COMMON: Fix inet_ntop_compat on *nix
|
||||
- LOBBY: Add skeleton to add help descriptions to lobbies
|
||||
|
||||
# 1.6.5
|
||||
Skipped this one.
|
||||
|
||||
# 1.6.4
|
||||
|
||||
- ANDROID: Fire Stick & Fire TV remote overrides gamepad port 0 on button press and viceversa like SHIELD devices
|
||||
- ANDROID: Provide default save / system / state / screenshot locations
|
||||
- AUDIO: Audio mixer supports MOD/S3M/XM file types now!
|
||||
- INPUT: input swap override flag (for remotes) is cleared correctly
|
||||
- INPUT: allow specifying libretro device in remap files
|
||||
- INPUT: allow specifying analog dpad mode in remap files
|
||||
- INPUT: allow saving libretro device to remap files
|
||||
- INPUT: allow saving analog dpad mode to remap files
|
||||
- INPUT: allow removing core and game remap files from the menu
|
||||
- COMMON: Cores can now request to set a 'shared context'. You no longer need to explicitly enable 'Shared Hardware Context' for Citra/OpenLara/Dolphin.
|
||||
- COMMON: Add 'Delete Core' option to Core Information menu.
|
||||
- COMMON: Allow Max Timing Skew to be set to 0.
|
||||
- COMMON: Change the "content dir" behavior so it works on either a flag or an empty directory setting, now platform drivers can provide defaults for save / system / state / screenshot dirs and still allow the content dir functionality, these settings are under settings / saving and flagged as advanced
|
||||
- GUI: You can turn on/off 'Horizontal Animation' now for the XMB menu. Turning animations off can result in a performance boost.
|
||||
- GUI: Fix sublabel word-wrapping in XMB where multi-byte languages were cut off too soon
|
||||
- LOCALIZATION: Update Dutch translation
|
||||
- LOCALIZATION: Update Traditional Chinese translation
|
||||
- LOCALIZATION: Update Italian translation
|
||||
- LOCALIZATION: Update Russian translation
|
||||
- WINDOWS: Provide default save / system / state / screenshot locations
|
||||
- LOBBIES: Show what country the host is in
|
||||
- MENU: Enable OSD text rendering for gdi and libcaca drivers
|
||||
- WINDOWS 98/ME/2K: Set default directory for MSVC 2005 RetroArch version.
|
||||
- WII: Better V-Sync handling, backported from SuperrSonic.
|
||||
- WIIU: Exception handler rewritten.
|
||||
|
||||
# 1.6.3
|
||||
- IOS: Fix GL regression - 32bit color format cores were no longer rendering
|
||||
- CHEEVOS: Add support for N64 cheevos and other small fixes.
|
||||
- CHEEVOS: Add 'Achievements -> Achievements Verbose Mode'. Ability to display cheevos related messages in OSD, useful for RetroAchievements users.
|
||||
- AUDIO: Audio mixer's volume can now be independently increased/decreased, and muted.
|
||||
- SDL2: Fix 'SDL2 driver does not see the hat on wired Xbox 360 controller"
|
||||
- SCANNING: Fix PS1 game scanning
|
||||
- VITA: Add support for external USB if mounted
|
||||
- MENU: Add 'User Interface -> Views'. Ability to display/hide online updater and core updater
|
||||
options.
|
||||
- AUDIO: Mute now no longer disables/enables audio but instead properly mutes the audio volume. Mute is also independent from the audio mixer volume.
|
||||
- INPUT: Add mouse index selection; ability now to select between different mice
|
||||
- INPUT: Fix 'All Users Control Menu' setting
|
||||
- LINUX: Add a tinyalsa audio driver. Doesn't require asoundlib, should be self-contained and lower-level.
|
||||
- LOBBIES: Announce the RetroArch version too
|
||||
- LOCALIZATION: Add Traditional Chinese translation
|
||||
- LOCALIZATION: Update French translation
|
||||
- LOCALIZATION: Update Italian translation
|
||||
- LOCALIZATION: Update Japanese translation
|
||||
- LOCALIZATION: Update Russian translation
|
||||
- MENU: Add 'User Interface -> Views'. Ability to display/hide online updater and core updater options.
|
||||
- NETPLAY: Disconnecting one client shouldn't cause everyone to disconnect anymore
|
||||
- NETWORK: SSL/TLS support, disabled by default
|
||||
- SCANNER: Fix PS1 game scanning
|
||||
- SCANNER: Move content list builder into scanner task with progress, fixes menu freeze with large playlists
|
||||
- SDL2: Fix 'SDL2 driver does not see the hat on wired Xbox 360 controller"
|
||||
- SETTINGS: Fix regression 'Custom Viewport is no longer overridable per-core or per-game'
|
||||
- VITA: Add cheevos support
|
||||
- VITA: Add support for external USB if mounted
|
||||
- WAYLAND: Fix menu mouse input
|
||||
- WII: Add support for single-port 'PS1/PS2 to USB controller adapter'
|
||||
- INPUT: Fix 'All Users Control Menu' setting
|
||||
- INPUT: Add mouse index selection; ability now to select between different mice
|
||||
- SETTINGS: Fix regression 'Custom Viewport is no longer overridable per-core or per-game'
|
||||
|
||||
# 1.6.0
|
||||
- AUTOSAVE/SRAM - Fix bug #3829 / #4820 (https://github.com/libretro/RetroArch/issues/3829)
|
||||
- ENDIANNESS: Fixed database scanning. Should fix scanning on PS3/WiiU/Wii, etc.
|
||||
- NET: Fix bug #4703 (https://github.com/libretro/RetroArch/issues/4703)
|
||||
- ANDROID: Runtime permission checking
|
||||
- ANDROID: Allow remotes to retain OK/Cancel position when menu_swap_ok_cancel is enabled
|
||||
- ANDROID: Improve autoconf fallback
|
||||
- ANDROID: Improve shield portable/gamepad device grouping workaround
|
||||
- ANDROID: Allow remotes to retain OK/Cancel position when menu_swap_ok_cancel is enabled
|
||||
- LOCALIZATION: Update/finish French translation
|
||||
- LOCALIZATION: Update German translation
|
||||
- LOCALIZATION: Update Japanese translation
|
||||
- LOCALIZATION/GUI: Korean font should display properly now with XMB/MaterialUI's default font
|
||||
- LOCALIZATION: Update Russian translation
|
||||
- MENU: Improved rendering for XMB ribbon; using additive blending (Vulkan/GL)
|
||||
- OSX/MACOS: Fixes serious memory leak
|
||||
- WINDOWS: Added WASAPI audio driver for low-latency audio. Both shared and exclusive mode.
|
||||
- WINDOWS: Added RawInput input driver for low-latency, low-level input.
|
||||
- WINDOWS: Core mouse input should be relative again in cores
|
||||
- MISC: Various frontend optimizations.
|
||||
- VIDEO: Fix threaded video regression; tickering of menu entries would no longer work.
|
||||
- WII: Fix crashing issues which could occur with the dummy core
|
||||
- WIIU: HID Controller support
|
||||
- WIIU: XMB/MaterialUI menu driver support
|
||||
- WIIU: Initial network/netplay support
|
||||
- ANDROID: Runtime permission checking
|
||||
- AUDIO: Audio mixer support. Mix up to 8 streams with the game's audio.
|
||||
- AUTOSAVE/SRAM - Fix bug #3829 / #4820 (https://github.com/libretro/RetroArch/issues/3829)
|
||||
- ENDIANNESS: Fixed database scanning. Should fix scanning on PS3/WiiU/Wii, etc.
|
||||
- LOBBIES: Fallback to filename based matching if no CRC matches are found (for people making playlists by hand)
|
||||
- LOBBIES: GUI refinement, show stop hosting when a host has been started, show disconnect when playing as client
|
||||
- LOBBIES: if the game is already loaded it will try to connect directly instead of re-loading content (non-fullpath cores only)
|
||||
- LOBBIES: unify both netplay menus
|
||||
- LOCALIZATION/GUI: Korean font should display properly now with XMB/MaterialUI's default font
|
||||
- LOCALIZATION: Update German translation
|
||||
- LOCALIZATION: Update Japanese translation
|
||||
- LOCALIZATION: Update Russian translation
|
||||
- LOCALIZATION: Update/finish French translation
|
||||
- MENU: Improved rendering for XMB ribbon; using additive blending (Vulkan/GL)
|
||||
- MISC: Various frontend optimizations.
|
||||
- NET: Fix bug #4703 (https://github.com/libretro/RetroArch/issues/4703)
|
||||
- OSX/MACOS: Fixes serious memory leak
|
||||
- THUMBNAILS: Thumbnails show up now in Load Content -> Collection, Information -> Database
|
||||
- VITA: Fix slow I/O
|
||||
- VIDEO: Fix threaded video regression; tickering of menu entries would no longer work.
|
||||
- VITA: Fix 30fps menu (poke into input now instead of reading the entire input buffer which apparently is slow)
|
||||
- VITA: Fix frame throttle
|
||||
- VULKAN: Unicode font rendering support. Should fix bad character encoding for French characters, etc.
|
||||
- VITA: Fix slow I/O
|
||||
- VULKAN: Fix some crashes on loading some thumbnails
|
||||
- AUDIO: Audio mixer support. Mix up to 8 streams with the game's audio.
|
||||
- VULKAN: Unicode font rendering support. Should fix bad character encoding for French characters, etc.
|
||||
- WII: Fix crashing issues which could occur with the dummy core
|
||||
- WIIU: HID Controller support
|
||||
- WIIU: Initial network/netplay support
|
||||
- WIIU: XMB/MaterialUI menu driver support
|
||||
- WINDOWS: Added RawInput input driver for low-latency, low-level input.
|
||||
- WINDOWS: Added WASAPI audio driver for low-latency audio. Both shared and exclusive mode.
|
||||
- WINDOWS: Core mouse input should be relative again in cores
|
||||
|
||||
# 1.5.0
|
||||
- MOBILE: Single-tap for menu entry selection
|
||||
- MOBILE: Long-tap a setting to reset to default
|
||||
- ANDROID: Autoconf fallback
|
||||
- ANDROID: Mouse support / Emulated mouse support
|
||||
- AUTOCONF: Fix partial matches for pad name
|
||||
- CHEEVOS: Fix crashes in the cheevos description menu
|
||||
- CHEEVOS: WIP leaderboards support
|
||||
- COMMON: Threading fixes
|
||||
- COMMON: 9-slice texture drawing support
|
||||
- COMMON: Threading fixes
|
||||
- CORETEXT/APPLE: Ability to load menu display font drivers and loading of custom font.
|
||||
- DOS: Add keyboard driver
|
||||
- DOS: Improve color accuracy and scaling
|
||||
- GUI: Various settings are now only visible when advanced settings is enabled
|
||||
- GUI: Allow changing icon theme on the fly
|
||||
- GUI: Add a symbol page in the OSK
|
||||
- GUI: Allow changing icon theme on the fly
|
||||
- GUI: Better dialogs for XMB
|
||||
- GUI: Various settings are now only visible when advanced settings is enabled
|
||||
- LOCALIZATION: Add/update Korean translation
|
||||
- LOCALIZATION: Rewrite German translation
|
||||
- LOCALIZATION: Update several English sublabels
|
||||
- LOCALIZATION: Update several Japanese labels
|
||||
- MOBILE: Long-tap a setting to reset to default
|
||||
- MOBILE: Single-tap for menu entry selection
|
||||
- NET: Allow manual netplay content loading
|
||||
- NET: Announcing network games to the public lobby is optional now
|
||||
- NET: Bake in miniupnpc
|
||||
- NET: Fix netplay join for contentless cores
|
||||
- NET: Fix netplay rooms being pushed on the wrong tab
|
||||
- NET: Lan games show next to lobbies with (lan) and connect via the private IP address
|
||||
- NET: Use new lobby system with MITM support
|
||||
- NET: Fix netplay rooms being pushed on the wrong tab
|
||||
- NUKLEAR: Update to current version
|
||||
- SCANNER: Always add 7z & zip to supported extensions
|
||||
- VULKAN: Find supported composite alpha in swapchain
|
||||
- VULKAN: Add snow/bokeh shader pipeline effects - at parity with GL now
|
||||
- VULKAN: Find supported composite alpha in swapchain
|
||||
- WIIU: Keyboard support
|
||||
- WINDOWS: Logging to file no longer spawns an empty window
|
||||
- WINDOWS: Fix loading of core/content via file menu
|
||||
- WINDOWS: Logging to file no longer spawns an empty window
|
||||
|
||||
# 1.4.1
|
||||
+215
-2
@@ -91,7 +91,7 @@ ifeq ($(HAVE_SHADERPIPELINE), 1)
|
||||
CFLAGS += -DHAVE_SHADERPIPELINE
|
||||
endif
|
||||
|
||||
CFLAGS += -I$(LIBRETRO_COMM_DIR)/include
|
||||
CFLAGS += -I$(LIBRETRO_COMM_DIR)/include -I$(DEPS_DIR)
|
||||
|
||||
# Switches
|
||||
|
||||
@@ -175,6 +175,7 @@ OBJ += frontend/frontend.o \
|
||||
$(LIBRETRO_COMM_DIR)/file/retro_dirent.o \
|
||||
$(LIBRETRO_COMM_DIR)/streams/stdin_stream.o \
|
||||
$(LIBRETRO_COMM_DIR)/streams/file_stream.o \
|
||||
$(LIBRETRO_COMM_DIR)/streams/file_stream_transforms.o \
|
||||
$(LIBRETRO_COMM_DIR)/streams/interface_stream.o \
|
||||
$(LIBRETRO_COMM_DIR)/streams/memory_stream.o \
|
||||
$(LIBRETRO_COMM_DIR)/lists/string_list.o \
|
||||
@@ -273,7 +274,8 @@ OBJ += intl/msg_hash_de.o \
|
||||
intl/msg_hash_pt_pt.o \
|
||||
intl/msg_hash_ru.o \
|
||||
intl/msg_hash_vn.o \
|
||||
intl/msg_hash_chs.o
|
||||
intl/msg_hash_chs.o \
|
||||
intl/msg_hash_cht.o
|
||||
|
||||
endif
|
||||
|
||||
@@ -333,6 +335,203 @@ OBJ += libretro-db/bintree.o \
|
||||
tasks/task_database_cue.o
|
||||
endif
|
||||
|
||||
ifneq ($(C89_BUILD), 1)
|
||||
HAVE_LIBUI = 0
|
||||
HAVE_GTKPLUS = 0
|
||||
|
||||
ifeq ($(HAVE_SSL), 1)
|
||||
DEFINES += -DHAVE_SSL
|
||||
DEFINES += -DMBEDTLS_SSL_DEBUG_ALL
|
||||
|
||||
# MinGW requires this for some reason, even though the include paths are relative to the source
|
||||
INCLUDE_DIRS += -Ideps/mbedtls
|
||||
|
||||
OBJS_TLS_CRYPTO = deps/mbedtls/aes.o deps/mbedtls/aesni.o deps/mbedtls/arc4.o \
|
||||
deps/mbedtls/asn1parse.o deps/mbedtls/asn1write.o deps/mbedtls/base64.o \
|
||||
deps/mbedtls/bignum.o deps/mbedtls/blowfish.o deps/mbedtls/camellia.o \
|
||||
deps/mbedtls/ccm.o deps/mbedtls/cipher.o deps/mbedtls/cipher_wrap.o \
|
||||
deps/mbedtls/cmac.o deps/mbedtls/ctr_drbg.o deps/mbedtls/des.o \
|
||||
deps/mbedtls/dhm.o deps/mbedtls/ecdh.o deps/mbedtls/ecdsa.o \
|
||||
deps/mbedtls/ecjpake.o deps/mbedtls/ecp.o \
|
||||
deps/mbedtls/ecp_curves.o deps/mbedtls/entropy.o deps/mbedtls/entropy_poll.o \
|
||||
deps/mbedtls/error.o deps/mbedtls/gcm.o deps/mbedtls/havege.o \
|
||||
deps/mbedtls/hmac_drbg.o deps/mbedtls/md.o deps/mbedtls/md2.o \
|
||||
deps/mbedtls/md4.o deps/mbedtls/md5.o deps/mbedtls/md_wrap.o \
|
||||
deps/mbedtls/memory_buffer_alloc.o deps/mbedtls/oid.o \
|
||||
deps/mbedtls/padlock.o deps/mbedtls/pem.o deps/mbedtls/pk.o \
|
||||
deps/mbedtls/pk_wrap.o deps/mbedtls/pkcs12.o deps/mbedtls/pkcs5.o \
|
||||
deps/mbedtls/pkparse.o deps/mbedtls/pkwrite.o deps/mbedtls/platform.o \
|
||||
deps/mbedtls/ripemd160.o deps/mbedtls/rsa.o deps/mbedtls/sha1.o \
|
||||
deps/mbedtls/sha256.o deps/mbedtls/sha512.o deps/mbedtls/threading.o \
|
||||
deps/mbedtls/timing.o deps/mbedtls/version.o \
|
||||
deps/mbedtls/version_features.o deps/mbedtls/xtea.o
|
||||
|
||||
OBJS_TLS_X509 = deps/mbedtls/certs.o deps/mbedtls/pkcs11.o deps/mbedtls/x509.o \
|
||||
deps/mbedtls/x509_create.o deps/mbedtls/x509_crl.o deps/mbedtls/x509_crt.o \
|
||||
deps/mbedtls/x509_csr.o deps/mbedtls/x509write_crt.o deps/mbedtls/x509write_csr.o
|
||||
|
||||
OBJS_TLS = deps/mbedtls/debug.o deps/mbedtls/net_sockets.o \
|
||||
deps/mbedtls/ssl_cache.o deps/mbedtls/ssl_ciphersuites.o \
|
||||
deps/mbedtls/ssl_cli.o deps/mbedtls/ssl_cookie.o \
|
||||
deps/mbedtls/ssl_srv.o deps/mbedtls/ssl_ticket.o \
|
||||
deps/mbedtls/ssl_tls.o
|
||||
|
||||
OBJ += $(OBJS_TLS_CRYPTO) $(OBJS_TLS_X509) $(OBJS_TLS)
|
||||
endif
|
||||
|
||||
ifeq ($(HAVE_LIBUI), 1)
|
||||
DEFINES += -DHAVE_LIBUI
|
||||
ifneq ($(findstring Win32,$(OS)),)
|
||||
OBJ += deps/libui/windows/alloc.o \
|
||||
deps/libui/windows/area.o \
|
||||
deps/libui/windows/areadraw.o \
|
||||
deps/libui/windows/areaevents.o \
|
||||
deps/libui/windows/areascroll.o \
|
||||
deps/libui/windows/areautil.o \
|
||||
deps/libui/windows/box.o \
|
||||
deps/libui/windows/button.o \
|
||||
deps/libui/windows/checkbox.o \
|
||||
deps/libui/windows/colorbutton.o \
|
||||
deps/libui/windows/colordialog.o \
|
||||
deps/libui/windows/combobox.o \
|
||||
deps/libui/windows/container.o \
|
||||
deps/libui/windows/control.o \
|
||||
deps/libui/windows/d2dscratch.o \
|
||||
deps/libui/windows/datetimepicker.o \
|
||||
deps/libui/windows/debug.o \
|
||||
deps/libui/windows/draw.o \
|
||||
deps/libui/windows/drawmatrix.o \
|
||||
deps/libui/windows/drawpath.o \
|
||||
deps/libui/windows/drawtext.o \
|
||||
deps/libui/windows/dwrite.o \
|
||||
deps/libui/windows/editablecombo.o \
|
||||
deps/libui/windows/entry.o \
|
||||
deps/libui/windows/events.o \
|
||||
deps/libui/windows/fontbutton.o \
|
||||
deps/libui/windows/fontdialog.o \
|
||||
deps/libui/windows/form.o \
|
||||
deps/libui/windows/graphemes.o \
|
||||
deps/libui/windows/grid.o \
|
||||
deps/libui/windows/group.o \
|
||||
deps/libui/windows/init.o \
|
||||
deps/libui/windows/label.o \
|
||||
deps/libui/windows/main.o \
|
||||
deps/libui/windows/menu.o \
|
||||
deps/libui/windows/multilineentry.o \
|
||||
deps/libui/windows/parent.o \
|
||||
deps/libui/windows/progressbar.o \
|
||||
deps/libui/windows/radiobuttons.o \
|
||||
deps/libui/windows/separator.o \
|
||||
deps/libui/windows/sizing.o \
|
||||
deps/libui/windows/slider.o \
|
||||
deps/libui/windows/spinbox.o \
|
||||
deps/libui/windows/stddialogs.o \
|
||||
deps/libui/windows/tab.o \
|
||||
deps/libui/windows/tabpage.o \
|
||||
deps/libui/windows/text.o \
|
||||
deps/libui/windows/utf16.o \
|
||||
deps/libui/windows/utilwin.o \
|
||||
deps/libui/windows/window.o \
|
||||
deps/libui/windows/winpublic.o \
|
||||
deps/libui/windows/winutil.o
|
||||
LIBS += -luxtheme -ld2d1 -ldwrite -lusp10
|
||||
else
|
||||
ifneq ($(findstring Darwin,$(OS)),)
|
||||
OBJ += deps/libui/darwin/alloc.o \
|
||||
deps/libui/darwin/area.o \
|
||||
deps/libui/darwin/areaevents.o \
|
||||
deps/libui/darwin/autolayout.o \
|
||||
deps/libui/darwin/box.o \
|
||||
deps/libui/darwin/button.o \
|
||||
deps/libui/darwin/checkbox.o \
|
||||
deps/libui/darwin/colorbutton.o \
|
||||
deps/libui/darwin/combobox.o \
|
||||
deps/libui/darwin/control.o \
|
||||
deps/libui/darwin/datetimepicker.o \
|
||||
deps/libui/darwin/debug.o \
|
||||
deps/libui/darwin/draw.o \
|
||||
deps/libui/darwin/drawtext.o \
|
||||
deps/libui/darwin/editablecombo.o \
|
||||
deps/libui/darwin/entry.o \
|
||||
deps/libui/darwin/fontbutton.o \
|
||||
deps/libui/darwin/form.o \
|
||||
deps/libui/darwin/grid.o \
|
||||
deps/libui/darwin/group.o \
|
||||
deps/libui/darwin/image.o \
|
||||
deps/libui/darwin/label.o \
|
||||
deps/libui/darwin/main.o \
|
||||
deps/libui/darwin/map.o \
|
||||
deps/libui/darwin/menu.o \
|
||||
deps/libui/darwin/multilineentry.o \
|
||||
deps/libui/darwin/progressbar.o \
|
||||
deps/libui/darwin/radiobuttons.o \
|
||||
deps/libui/darwin/scrollview.o \
|
||||
deps/libui/darwin/separator.o \
|
||||
deps/libui/darwin/slider.o \
|
||||
deps/libui/darwin/spinbox.o \
|
||||
deps/libui/darwin/stddialogs.o \
|
||||
deps/libui/darwin/tab.o \
|
||||
deps/libui/darwin/text.o \
|
||||
deps/libui/darwin/util.o \
|
||||
deps/libui/darwin/window.o \
|
||||
deps/libui/darwin/winmoveresize.o
|
||||
else
|
||||
CFLAGS += -I/usr/include/glib-2.0 -I/usr/lib/x86_64-linux-gnu/glib-2.0/include -I/usr/include/gtk-3.0 -I/usr/include/pango-1.0 -I/usr/include/cairo -I/usr/include/gdk-pixbuf-2.0 -I/usr/include/atk-1.0
|
||||
LIBS += -lgtk-3 -lgdk-3 -lpangocairo-1.0 -lpango-1.0 -latk-1.0 -lcairo-gobject -lcairo -lgdk_pixbuf-2.0 -lgio-2.0 -lgobject-2.0 -lglib-2.0
|
||||
OBJ += deps/libui/unix/alloc.o \
|
||||
deps/libui/unix/area.o \
|
||||
deps/libui/unix/box.o \
|
||||
deps/libui/unix/button.o \
|
||||
deps/libui/unix/cellrendererbutton.o \
|
||||
deps/libui/unix/checkbox.o \
|
||||
deps/libui/unix/child.o \
|
||||
deps/libui/unix/colorbutton.o \
|
||||
deps/libui/unix/combobox.o \
|
||||
deps/libui/unix/control.o \
|
||||
deps/libui/unix/datetimepicker.o \
|
||||
deps/libui/unix/debug.o \
|
||||
deps/libui/unix/draw.o \
|
||||
deps/libui/unix/drawmatrix.o \
|
||||
deps/libui/unix/drawpath.o \
|
||||
deps/libui/unix/drawtext.o \
|
||||
deps/libui/unix/editablecombo.o \
|
||||
deps/libui/unix/entry.o \
|
||||
deps/libui/unix/fontbutton.o \
|
||||
deps/libui/unix/form.o \
|
||||
deps/libui/unix/future.o \
|
||||
deps/libui/unix/graphemes.o \
|
||||
deps/libui/unix/grid.o \
|
||||
deps/libui/unix/group.o \
|
||||
deps/libui/unix/image.o \
|
||||
deps/libui/unix/label.o \
|
||||
deps/libui/unix/main.o \
|
||||
deps/libui/unix/menu.o \
|
||||
deps/libui/unix/multilineentry.o \
|
||||
deps/libui/unix/progressbar.o \
|
||||
deps/libui/unix/radiobuttons.o \
|
||||
deps/libui/unix/separator.o \
|
||||
deps/libui/unix/slider.o \
|
||||
deps/libui/unix/spinbox.o \
|
||||
deps/libui/unix/stddialogs.o \
|
||||
deps/libui/unix/tab.o \
|
||||
deps/libui/unix/text.o \
|
||||
deps/libui/unix/util.o \
|
||||
deps/libui/unix/window.o
|
||||
endif
|
||||
endif
|
||||
|
||||
OBJ += deps/libui/common/areaevents.o \
|
||||
deps/libui/common/control.o \
|
||||
deps/libui/common/debug.o \
|
||||
deps/libui/common/matrix.o \
|
||||
deps/libui/common/shouldquit.o \
|
||||
deps/libui/common/userbugs.o
|
||||
|
||||
OBJ += deps/libui/libui_main.o
|
||||
endif
|
||||
endif
|
||||
|
||||
|
||||
# Miscellaneous
|
||||
|
||||
ifeq ($(HAVE_STDIN_CMD), 1)
|
||||
@@ -383,6 +582,11 @@ endif
|
||||
DEFINES += $(ALSA_CFLAGS)
|
||||
endif
|
||||
|
||||
ifeq ($(HAVE_TINYALSA), 1)
|
||||
OBJ += audio/drivers/tinyalsa.o
|
||||
DEFINES += -DHAVE_TINYALSA
|
||||
endif
|
||||
|
||||
ifeq ($(HAVE_ROAR), 1)
|
||||
OBJ += audio/drivers/roar.o
|
||||
LIBS += $(ROAR_LIBS)
|
||||
@@ -1119,6 +1323,11 @@ ifeq ($(HAVE_RBMP), 1)
|
||||
OBJ += $(LIBRETRO_COMM_DIR)/formats/bmp/rbmp.o
|
||||
endif
|
||||
|
||||
ifeq ($(HAVE_IBXM), 1)
|
||||
DEFINES += -DHAVE_IBXM
|
||||
OBJ += $(DEPS_DIR)/ibxm/ibxm.o
|
||||
endif
|
||||
|
||||
OBJ += $(LIBRETRO_COMM_DIR)/formats/bmp/rbmp_encode.o \
|
||||
$(LIBRETRO_COMM_DIR)/formats/json/jsonsax.o \
|
||||
$(LIBRETRO_COMM_DIR)/formats/json/jsonsax_full.o \
|
||||
@@ -1177,6 +1386,10 @@ ifeq ($(HAVE_NETWORKING), 1)
|
||||
tasks/task_wifi.o \
|
||||
tasks/task_netplay_find_content.o
|
||||
|
||||
ifeq ($(HAVE_SSL), 1)
|
||||
OBJ += $(LIBRETRO_COMM_DIR)/net/net_socket_ssl.o
|
||||
endif
|
||||
|
||||
ifneq ($(HAVE_SOCKET_LEGACY),1)
|
||||
OBJ += $(LIBRETRO_COMM_DIR)/net/net_ifinfo.o
|
||||
endif
|
||||
|
||||
@@ -25,6 +25,7 @@ OBJ := ctr/ctr_system.o \
|
||||
frontend/frontend_driver.o \
|
||||
frontend/drivers/platform_ctr.o \
|
||||
frontend/drivers/platform_null.o \
|
||||
libretro-common/encodings/encoding_utf.o \
|
||||
libretro-common/compat/compat_strcasestr.o \
|
||||
libretro-common/file/file_path.o \
|
||||
libretro-common/string/stdstring.o \
|
||||
|
||||
@@ -133,6 +133,7 @@ endif
|
||||
HAVE_RJPEG := 1
|
||||
HAVE_RBMP := 1
|
||||
HAVE_RTGA := 1
|
||||
HAVE_IBXM := 1
|
||||
HAVE_OVERLAY := 1
|
||||
HAVE_ZLIB := 1
|
||||
WANT_ZLIB := 1
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
RARCH_VERSION = "0.9.9.3"
|
||||
RARCH_VERSION = "1.6.7.0"
|
||||
|
||||
DEBUG = 0
|
||||
|
||||
|
||||
+1
-1
@@ -1,4 +1,4 @@
|
||||
RARCH_VERSION = "0.9.9.3"
|
||||
RARCH_VERSION = "1.6.7.0"
|
||||
|
||||
#which compiler to build with - GCC or SNC
|
||||
#set to GCC for debug builds for use with debugger
|
||||
|
||||
+1
-1
@@ -1,4 +1,4 @@
|
||||
RARCH_VERSION = "0.9.9.3"
|
||||
RARCH_VERSION = "1.6.7.0"
|
||||
|
||||
#which compiler to build with - GCC or SNC
|
||||
#set to GCC for debug builds for use with debugger
|
||||
|
||||
+13
-3
@@ -16,9 +16,20 @@ else ifneq ($(findstring MINGW,$(shell uname -a)),)
|
||||
system_platform = win
|
||||
endif
|
||||
|
||||
ifeq ($(DEBUG), 1)
|
||||
PPU_OPTIMIZE_LV := -O0 -g
|
||||
else
|
||||
PPU_OPTIMIZE_LV := -O2 -DNDEBUG
|
||||
endif
|
||||
|
||||
STRIP = $(CELL_SDK)/host-win32/ppu/bin/ppu-lv2-strip.exe
|
||||
|
||||
PPU_CFLAGS += -I. -Ilibretro-common/include -Ideps/libz -D__CELLOS_LV2__ -DIS_SALAMANDER -DRARCH_CONSOLE -DHAVE_SYSUTILS -DHAVE_SYSMODULES -DHAVE_RARCH_EXEC
|
||||
INCFLAGS = -I. -Idefines -Ilibretro-common/include -Ideps/libz
|
||||
DEFINES = -D__CELLOS_LV2__ -DIS_SALAMANDER -DRARCH_CONSOLE -DHAVE_SYSUTILS -DHAVE_SYSMODULES -DHAVE_RARCH_EXEC
|
||||
|
||||
PPU_CFLAGS := $(PPU_OPTIMIZE_LV) $(INCFLAGS) $(DEFINES)
|
||||
PPU_CXXFLAGS := $(PPU_OPTIMIZE_LV) $(INCFLAGS) $(DEFINES)
|
||||
|
||||
PPU_SRCS = frontend/frontend_salamander.c \
|
||||
frontend/frontend_driver.c \
|
||||
frontend/drivers/platform_ps3.c \
|
||||
@@ -29,6 +40,7 @@ PPU_SRCS = frontend/frontend_salamander.c \
|
||||
libretro-common/file/retro_dirent.c \
|
||||
libretro-common/hash/rhash.c \
|
||||
libretro-common/string/stdstring.c \
|
||||
libretro-common/encodings/encoding_utf.c \
|
||||
libretro-common/compat/compat_strl.c \
|
||||
libretro-common/compat/compat_strcasestr.c \
|
||||
libretro-common/streams/file_stream.c \
|
||||
@@ -60,8 +72,6 @@ endif
|
||||
|
||||
PPU_LDLIBS += -lm -lnet_stub -lnetctl_stub -lio_stub -lsysmodule_stub -lsysutil_stub -lsysutil_game_stub -lfs_stub -lsysutil_np_stub
|
||||
|
||||
PPU_OPTIMIZE_LV := -O2
|
||||
|
||||
MAKE_FSELF = $(CELL_SDK)/host-win32/bin/make_fself.exe
|
||||
|
||||
include $(CELL_MK_DIR)/sdk.target.mk
|
||||
+1
-1
@@ -1,4 +1,4 @@
|
||||
RARCH_VERSION = "0.9.9.3"
|
||||
RARCH_VERSION = "1.6.7.0"
|
||||
|
||||
DEBUG = 0
|
||||
HAVE_LOGGER = 0
|
||||
|
||||
@@ -40,6 +40,7 @@ OBJS = frontend/frontend_salamander.o \
|
||||
libretro-common/lists/string_list.o \
|
||||
libretro-common/lists/dir_list.o \
|
||||
libretro-common/file/retro_dirent.o \
|
||||
libretro-common/encodings/encoding_utf.o \
|
||||
libretro-common/compat/compat_strl.o \
|
||||
libretro-common/compat/compat_strcasestr.o \
|
||||
libretro-common/file/config_file.o \
|
||||
|
||||
@@ -45,6 +45,7 @@ else
|
||||
HAVE_THREADS := 1
|
||||
HAVE_LIBRETRODB := 1
|
||||
HAVE_CC_RESAMPLER := 1
|
||||
HAVE_CHEEVOS := 1
|
||||
RARCH_CONSOLE := 1
|
||||
|
||||
ifeq ($(DEBUG), 1)
|
||||
|
||||
@@ -41,6 +41,7 @@ OBJS = frontend/frontend_salamander.o \
|
||||
libretro-common/lists/string_list.o \
|
||||
libretro-common/lists/dir_list.o \
|
||||
libretro-common/file/retro_dirent.o \
|
||||
libretro-common/encodings/encoding_utf.o \
|
||||
libretro-common/compat/compat_strl.o \
|
||||
libretro-common/compat/compat_strcasestr.o \
|
||||
libretro-common/file/config_file.o \
|
||||
|
||||
@@ -51,6 +51,7 @@ OBJ = frontend/frontend_salamander.o \
|
||||
libretro-common/lists/dir_list.o \
|
||||
libretro-common/streams/file_stream.o \
|
||||
libretro-common/file/retro_dirent.o \
|
||||
libretro-common/encodings/encoding_utf.o \
|
||||
libretro-common/compat/compat_strl.o \
|
||||
libretro-common/compat/compat_strcasestr.o \
|
||||
libretro-common/file/config_file.o \
|
||||
|
||||
@@ -16,6 +16,7 @@ OBJ += frontend/frontend_salamander.o
|
||||
OBJ += frontend/frontend_driver.o
|
||||
OBJ += frontend/drivers/platform_wiiu.o
|
||||
OBJ += frontend/drivers/platform_null.o
|
||||
OBJ += libretro-common/encodings/encoding_utf.o
|
||||
OBJ += libretro-common/compat/compat_strcasestr.o
|
||||
OBJ += libretro-common/file/file_path.o
|
||||
OBJ += libretro-common/string/stdstring.o
|
||||
|
||||
@@ -55,6 +55,9 @@ static const audio_driver_t *audio_drivers[] = {
|
||||
&audio_alsathread,
|
||||
#endif
|
||||
#endif
|
||||
#ifdef HAVE_TINYALSA
|
||||
&audio_tinyalsa,
|
||||
#endif
|
||||
#if defined(HAVE_OSS) || defined(HAVE_OSS_BSD)
|
||||
&audio_oss,
|
||||
#endif
|
||||
@@ -911,6 +914,11 @@ bool audio_driver_mixer_extension_supported(const char *ext)
|
||||
|
||||
#ifdef HAVE_STB_VORBIS
|
||||
string_list_append(str_list, "ogg", attr);
|
||||
#endif
|
||||
#ifdef HAVE_IBXM
|
||||
string_list_append(str_list, "mod", attr);
|
||||
string_list_append(str_list, "s3m", attr);
|
||||
string_list_append(str_list, "xm", attr);
|
||||
#endif
|
||||
string_list_append(str_list, "wav", attr);
|
||||
|
||||
@@ -954,8 +962,12 @@ static void audio_mixer_play_stop_cb(audio_mixer_sound_t *sound, unsigned reason
|
||||
if (idx >= 0)
|
||||
{
|
||||
unsigned i = (unsigned)idx;
|
||||
|
||||
#if 0
|
||||
if (audio_mixer_streams[i].buf != NULL)
|
||||
free(audio_mixer_streams[i].buf);
|
||||
#endif
|
||||
|
||||
audio_mixer_streams[i].state = AUDIO_STREAM_STATE_NONE;
|
||||
audio_mixer_streams[i].volume = 0.0f;
|
||||
audio_mixer_streams[i].buf = NULL;
|
||||
@@ -1001,6 +1013,9 @@ bool audio_driver_mixer_add_stream(audio_mixer_stream_params_t *params)
|
||||
case AUDIO_MIXER_TYPE_OGG:
|
||||
handle = audio_mixer_load_ogg(buf, (int32_t)params->bufsize);
|
||||
break;
|
||||
case AUDIO_MIXER_TYPE_MOD:
|
||||
handle = audio_mixer_load_mod(buf, (int32_t)params->bufsize);
|
||||
break;
|
||||
case AUDIO_MIXER_TYPE_NONE:
|
||||
free(buf);
|
||||
return false;
|
||||
|
||||
@@ -259,6 +259,7 @@ extern audio_driver_t audio_rsound;
|
||||
extern audio_driver_t audio_oss;
|
||||
extern audio_driver_t audio_alsa;
|
||||
extern audio_driver_t audio_alsathread;
|
||||
extern audio_driver_t audio_tinyalsa;
|
||||
extern audio_driver_t audio_roar;
|
||||
extern audio_driver_t audio_openal;
|
||||
extern audio_driver_t audio_opensl;
|
||||
|
||||
@@ -17,6 +17,7 @@
|
||||
#define ALSA_PCM_NEW_HW_PARAMS_API
|
||||
#define ALSA_PCM_NEW_SW_PARAMS_API
|
||||
#include <sys/asoundlib.h>
|
||||
#include <retro_math.h>
|
||||
|
||||
#include "../audio_driver.h"
|
||||
|
||||
|
||||
@@ -17,6 +17,7 @@
|
||||
#include <string.h>
|
||||
#include <malloc.h>
|
||||
#include <retro_miscellaneous.h>
|
||||
#include <retro_timers.h>
|
||||
|
||||
#include "../audio_driver.h"
|
||||
|
||||
|
||||
@@ -31,6 +31,7 @@
|
||||
|
||||
#include <retro_inline.h>
|
||||
#include <retro_miscellaneous.h>
|
||||
#include <retro_timers.h>
|
||||
#include <rthreads/rthreads.h>
|
||||
#include <queues/fifo_queue.h>
|
||||
|
||||
@@ -321,7 +322,11 @@ static void *dsound_init(const char *device, unsigned rate, unsigned latency,
|
||||
|
||||
RARCH_LOG("DirectSound devices:\n");
|
||||
#ifndef _XBOX
|
||||
DirectSoundEnumerate(enumerate_cb, &dev);
|
||||
#ifdef UNICODE
|
||||
DirectSoundEnumerate((LPDSENUMCALLBACKW)enumerate_cb, &dev);
|
||||
#else
|
||||
DirectSoundEnumerate((LPDSENUMCALLBACKA)enumerate_cb, &dev);
|
||||
#endif
|
||||
#endif
|
||||
|
||||
if (DirectSoundCreate(dev.guid, &ds->ds, NULL) != DS_OK)
|
||||
|
||||
@@ -18,14 +18,11 @@
|
||||
#include <stdlib.h>
|
||||
#include <string.h>
|
||||
|
||||
#include <assert.h>
|
||||
|
||||
#include <jack/jack.h>
|
||||
#include <jack/types.h>
|
||||
#include <jack/ringbuffer.h>
|
||||
|
||||
#include <boolean.h>
|
||||
#include <retro_assert.h>
|
||||
#include <rthreads/rthreads.h>
|
||||
|
||||
#include "../audio_driver.h"
|
||||
@@ -76,7 +73,6 @@ static int process_cb(jack_nframes_t nframes, void *data)
|
||||
jack_nframes_t f;
|
||||
jack_default_audio_sample_t *out = (jack_default_audio_sample_t*)jack_port_get_buffer(jd->ports[i], nframes);
|
||||
|
||||
retro_assert(out);
|
||||
jack_ringbuffer_read(jd->buffer[i], (char*)out, min_avail * sizeof(jack_default_audio_sample_t));
|
||||
|
||||
for (f = min_avail; f < nframes; f++)
|
||||
|
||||
@@ -31,6 +31,7 @@
|
||||
#endif
|
||||
|
||||
#include <retro_miscellaneous.h>
|
||||
#include <retro_timers.h>
|
||||
|
||||
#include "../audio_driver.h"
|
||||
#include "../../verbosity.h"
|
||||
|
||||
+6
-2
@@ -120,7 +120,9 @@ static bool oss_stop(void *data)
|
||||
{
|
||||
int *fd = (int*)data;
|
||||
|
||||
ioctl(*fd, SNDCTL_DSP_RESET, 0);
|
||||
if (ioctl(*fd, SNDCTL_DSP_RESET, 0) < 0)
|
||||
return false;
|
||||
|
||||
oss_is_paused = true;
|
||||
return true;
|
||||
}
|
||||
@@ -155,7 +157,9 @@ static void oss_free(void *data)
|
||||
{
|
||||
int *fd = (int*)data;
|
||||
|
||||
ioctl(*fd, SNDCTL_DSP_RESET, 0);
|
||||
if (ioctl(*fd, SNDCTL_DSP_RESET, 0) < 0)
|
||||
return;
|
||||
|
||||
close(*fd);
|
||||
free(fd);
|
||||
}
|
||||
|
||||
@@ -26,6 +26,7 @@
|
||||
#include <rthreads/rthreads.h>
|
||||
#include <queues/fifo_queue.h>
|
||||
#include <retro_inline.h>
|
||||
#include <retro_math.h>
|
||||
|
||||
#include "../audio_driver.h"
|
||||
#include "../../verbosity.h"
|
||||
|
||||
File diff suppressed because it is too large.
Load diff
@@ -263,14 +263,12 @@ static void wasapi_set_format(WAVEFORMATEXTENSIBLE *wf,
|
||||
static IAudioClient *wasapi_init_client_sh(IMMDevice *device,
|
||||
bool *float_fmt, unsigned *rate, unsigned latency)
|
||||
{
|
||||
HRESULT hr;
|
||||
WAVEFORMATEXTENSIBLE wf;
|
||||
int i, j;
|
||||
IAudioClient *client = NULL;
|
||||
bool float_fmt_res = *float_fmt;
|
||||
unsigned rate_res = *rate;
|
||||
|
||||
hr = device->lpVtbl->Activate(device, &IID_IAudioClient,
|
||||
HRESULT hr = device->lpVtbl->Activate(device, &IID_IAudioClient,
|
||||
CLSCTX_ALL, NULL, (void**)&client);
|
||||
WASAPI_HR_CHECK(hr, "IMMDevice::Activate", return NULL);
|
||||
|
||||
@@ -331,7 +329,6 @@ error:
|
||||
static IAudioClient *wasapi_init_client_ex(IMMDevice *device,
|
||||
bool *float_fmt, unsigned *rate, unsigned latency)
|
||||
{
|
||||
HRESULT hr;
|
||||
WAVEFORMATEXTENSIBLE wf;
|
||||
int i, j;
|
||||
IAudioClient *client = NULL;
|
||||
@@ -340,8 +337,8 @@ static IAudioClient *wasapi_init_client_ex(IMMDevice *device,
|
||||
REFERENCE_TIME minimum_period = 0;
|
||||
REFERENCE_TIME buffer_duration = 0;
|
||||
UINT32 buffer_length = 0;
|
||||
|
||||
hr = device->lpVtbl->Activate(device, &IID_IAudioClient,
|
||||
HRESULT hr = device->lpVtbl->Activate(
|
||||
device, &IID_IAudioClient,
|
||||
CLSCTX_ALL, NULL, (void**)&client);
|
||||
WASAPI_HR_CHECK(hr, "IMMDevice::Activate", return NULL);
|
||||
|
||||
@@ -744,9 +741,9 @@ static ssize_t wasapi_write(void *wh, const void *data, size_t size)
|
||||
for (writen = 0, ir = -1; writen < size; writen += ir)
|
||||
{
|
||||
if (w->exclusive)
|
||||
ir = wasapi_write_ex(w, data + writen, size - writen);
|
||||
ir = wasapi_write_ex(w, (char*)data + writen, size - writen);
|
||||
else
|
||||
ir = wasapi_write_sh(w, data + writen, size - writen);
|
||||
ir = wasapi_write_sh(w, (char*)data + writen, size - writen);
|
||||
if (ir == -1)
|
||||
return -1;
|
||||
}
|
||||
|
||||
@@ -73,6 +73,7 @@
|
||||
#include <retro_inline.h>
|
||||
#include <retro_assert.h>
|
||||
#include <retro_miscellaneous.h>
|
||||
#include <retro_timers.h>
|
||||
|
||||
/*
|
||||
****************************************************************************
|
||||
|
||||
@@ -16,7 +16,9 @@
|
||||
*/
|
||||
|
||||
#include <stdio.h>
|
||||
#if !defined(__FreeBSD__) || __FreeBSD__ < 5
|
||||
#include <malloc.h>
|
||||
#endif
|
||||
#include <string.h>
|
||||
#include <assert.h>
|
||||
#include <stddef.h>
|
||||
@@ -28,7 +30,9 @@
|
||||
#include <sys/time.h>
|
||||
#include <sys/ioctl.h>
|
||||
|
||||
#ifndef __FreeBSD__
|
||||
#include <asm/types.h>
|
||||
#endif
|
||||
#include <linux/videodev2.h>
|
||||
|
||||
#include <memmap.h>
|
||||
|
||||
+192
-41
@@ -21,6 +21,7 @@
|
||||
#include <compat/strl.h>
|
||||
#include <rhash.h>
|
||||
#include <retro_miscellaneous.h>
|
||||
#include <retro_math.h>
|
||||
#include <net/net_http.h>
|
||||
#include <libretro.h>
|
||||
|
||||
@@ -96,7 +97,7 @@
|
||||
|
||||
#define CHEEVOS_SIX_MB ( 6 * 1024 * 1024)
|
||||
#define CHEEVOS_EIGHT_MB ( 8 * 1024 * 1024)
|
||||
#define CHEEVOS_SIZE_LIMIT (32 * 1024 * 1024)
|
||||
#define CHEEVOS_SIZE_LIMIT (64 * 1024 * 1024)
|
||||
|
||||
enum
|
||||
{
|
||||
@@ -169,6 +170,9 @@ enum
|
||||
CHEEVOS_COND_TYPE_STANDARD = 0,
|
||||
CHEEVOS_COND_TYPE_PAUSE_IF,
|
||||
CHEEVOS_COND_TYPE_RESET_IF,
|
||||
CHEEVOS_COND_TYPE_ADD_SOURCE,
|
||||
CHEEVOS_COND_TYPE_SUB_SOURCE,
|
||||
CHEEVOS_COND_TYPE_ADD_HITS,
|
||||
|
||||
CHEEVOS_COND_TYPE_LAST
|
||||
}; /* cheevos_cond_t.type */
|
||||
@@ -320,6 +324,9 @@ typedef struct
|
||||
{
|
||||
int console_id;
|
||||
bool core_supports;
|
||||
bool addrs_patched;
|
||||
int add_buffer;
|
||||
int add_hits;
|
||||
|
||||
cheevoset_t core;
|
||||
cheevoset_t unofficial;
|
||||
@@ -335,6 +342,9 @@ static cheevos_locals_t cheevos_locals =
|
||||
{
|
||||
/* console_id */ 0,
|
||||
/* core_supports */ true,
|
||||
/* addrs_patched */ false,
|
||||
/* add_buffer */ 0,
|
||||
/* add_hits */ 0,
|
||||
/* core */ {NULL, 0},
|
||||
/* unofficial */ {NULL, 0},
|
||||
/* leaderboards */ NULL,
|
||||
@@ -501,9 +511,12 @@ static void cheevos_log_cond(const cheevos_cond_t* cond)
|
||||
{
|
||||
RARCH_LOG("CHEEVOS condition %p\n", cond);
|
||||
RARCH_LOG("CHEEVOS type: %s\n",
|
||||
cond->type == CHEEVOS_COND_TYPE_STANDARD ? "standard" :
|
||||
cond->type == CHEEVOS_COND_TYPE_PAUSE_IF ? "pause" :
|
||||
cond->type == CHEEVOS_COND_TYPE_RESET_IF ? "reset" :
|
||||
cond->type == CHEEVOS_COND_TYPE_STANDARD ? "standard" :
|
||||
cond->type == CHEEVOS_COND_TYPE_PAUSE_IF ? "pause" :
|
||||
cond->type == CHEEVOS_COND_TYPE_RESET_IF ? "reset" :
|
||||
cond->type == CHEEVOS_COND_TYPE_ADD_SOURCE ? "add source" :
|
||||
cond->type == CHEEVOS_COND_TYPE_SUB_SOURCE ? "sub source" :
|
||||
cond->type == CHEEVOS_COND_TYPE_ADD_HITS ? "add hits" :
|
||||
"?"
|
||||
);
|
||||
RARCH_LOG("CHEEVOS req_hits: %u\n", cond->req_hits);
|
||||
@@ -627,6 +640,12 @@ static void cheevos_build_memaddr(const cheevos_condition_t* condition,
|
||||
cheevos_add_string(&aux, &left, "R:");
|
||||
else if (cond->type == CHEEVOS_COND_TYPE_PAUSE_IF)
|
||||
cheevos_add_string(&aux, &left, "P:");
|
||||
else if (cond->type == CHEEVOS_COND_TYPE_ADD_SOURCE)
|
||||
cheevos_add_string(&aux, &left, "A:");
|
||||
else if (cond->type == CHEEVOS_COND_TYPE_SUB_SOURCE)
|
||||
cheevos_add_string(&aux, &left, "B:");
|
||||
else if (cond->type == CHEEVOS_COND_TYPE_ADD_HITS)
|
||||
cheevos_add_string(&aux, &left, "C:");
|
||||
|
||||
cheevos_add_var(&cond->source, &aux, &left);
|
||||
|
||||
@@ -902,7 +921,7 @@ static unsigned cheevos_prefix_to_comp_size(char prefix)
|
||||
{
|
||||
/* Careful not to use ABCDEF here, this denotes part of an actual variable! */
|
||||
|
||||
switch( toupper( prefix ) )
|
||||
switch( toupper( (unsigned char)prefix ) )
|
||||
{
|
||||
case 'M':
|
||||
return CHEEVOS_VAR_SIZE_BIT_0;
|
||||
@@ -1032,9 +1051,18 @@ void cheevos_parse_guest_addr(cheevos_var_t *var, unsigned value)
|
||||
var->bank_id = -1;
|
||||
var->value = value;
|
||||
|
||||
if ( cheevos_locals.console_id == CHEEVOS_CONSOLE_NINTENDO
|
||||
&& var->value < 0x2000)
|
||||
var->value &= 0x07ff;
|
||||
switch (cheevos_locals.console_id)
|
||||
{
|
||||
case CHEEVOS_CONSOLE_NINTENDO:
|
||||
if (var->value < 0x2000)
|
||||
var->value &= 0x07ff;
|
||||
break;
|
||||
|
||||
case CHEEVOS_CONSOLE_GAMEBOY_COLOR:
|
||||
if (var->value >= 0xe000 && var->value <= 0xfdff)
|
||||
var->value -= 0x2000;
|
||||
break;
|
||||
}
|
||||
|
||||
if (system->mmaps.num_descriptors != 0)
|
||||
{
|
||||
@@ -1113,13 +1141,13 @@ static void cheevos_parse_var(cheevos_var_t *var, const char **memaddr)
|
||||
const char *str = *memaddr;
|
||||
unsigned base = 16;
|
||||
|
||||
if (toupper(*str) == 'D' && str[1] == '0' && toupper(str[2]) == 'X')
|
||||
if (toupper((unsigned char)*str) == 'D' && str[1] == '0' && toupper((unsigned char)str[2]) == 'X')
|
||||
{
|
||||
/* d0x + 4 hex digits */
|
||||
str += 3;
|
||||
var->type = CHEEVOS_VAR_TYPE_DELTA_MEM;
|
||||
}
|
||||
else if (*str == '0' && toupper(str[1]) == 'X')
|
||||
else if (*str == '0' && toupper((unsigned char)str[1]) == 'X')
|
||||
{
|
||||
/* 0x + 4 hex digits */
|
||||
str += 2;
|
||||
@@ -1129,11 +1157,11 @@ static void cheevos_parse_var(cheevos_var_t *var, const char **memaddr)
|
||||
{
|
||||
var->type = CHEEVOS_VAR_TYPE_VALUE_COMP;
|
||||
|
||||
if (toupper(*str) == 'H')
|
||||
if (toupper((unsigned char)*str) == 'H')
|
||||
str++;
|
||||
else
|
||||
{
|
||||
if (toupper(*str) == 'V')
|
||||
if (toupper((unsigned char)*str) == 'V')
|
||||
str++;
|
||||
|
||||
base = 10;
|
||||
@@ -1150,37 +1178,41 @@ static void cheevos_parse_var(cheevos_var_t *var, const char **memaddr)
|
||||
|
||||
var->value = (unsigned)strtol(str, &end, base);
|
||||
*memaddr = end;
|
||||
|
||||
switch (var->type)
|
||||
{
|
||||
case CHEEVOS_VAR_TYPE_ADDRESS:
|
||||
case CHEEVOS_VAR_TYPE_DELTA_MEM:
|
||||
cheevos_parse_guest_addr(var, var->value);
|
||||
#ifdef CHEEVOS_DUMP_ADDRS
|
||||
RARCH_LOG("CHEEVOS var %03d:%08X\n", var->bank_id + 1, var->value);
|
||||
#endif
|
||||
break;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
static void cheevos_parse_cond(cheevos_cond_t *cond, const char **memaddr)
|
||||
{
|
||||
const char* str = *memaddr;
|
||||
cond->type = CHEEVOS_COND_TYPE_STANDARD;
|
||||
|
||||
if (*str == 'R' && str[1] == ':')
|
||||
if (str[1] == ':')
|
||||
{
|
||||
cond->type = CHEEVOS_COND_TYPE_RESET_IF;
|
||||
str += 2;
|
||||
int skip = 2;
|
||||
|
||||
switch (*str)
|
||||
{
|
||||
case 'R':
|
||||
cond->type = CHEEVOS_COND_TYPE_RESET_IF;
|
||||
break;
|
||||
case 'P':
|
||||
cond->type = CHEEVOS_COND_TYPE_PAUSE_IF;
|
||||
break;
|
||||
case 'A':
|
||||
cond->type = CHEEVOS_COND_TYPE_ADD_SOURCE;
|
||||
break;
|
||||
case 'B':
|
||||
cond->type = CHEEVOS_COND_TYPE_SUB_SOURCE;
|
||||
break;
|
||||
case 'C':
|
||||
cond->type = CHEEVOS_COND_TYPE_ADD_HITS;
|
||||
break;
|
||||
default:
|
||||
skip = 0;
|
||||
break;
|
||||
}
|
||||
|
||||
str += skip;
|
||||
}
|
||||
else if (*str == 'P' && str[1] == ':')
|
||||
{
|
||||
cond->type = CHEEVOS_COND_TYPE_PAUSE_IF;
|
||||
str += 2;
|
||||
}
|
||||
else
|
||||
cond->type = CHEEVOS_COND_TYPE_STANDARD;
|
||||
|
||||
cheevos_parse_var(&cond->source, &str);
|
||||
cond->op = cheevos_parse_operator(&str);
|
||||
@@ -1340,9 +1372,7 @@ static void cheevos_free_condition(cheevos_condition_t* condition)
|
||||
if (condition->condsets)
|
||||
{
|
||||
for (i = 0; i < condition->count; i++)
|
||||
{
|
||||
free((void*)condition->condsets[i].conds);
|
||||
}
|
||||
|
||||
free((void*)condition->condsets);
|
||||
}
|
||||
@@ -1835,7 +1865,7 @@ static unsigned cheevos_get_var_value(cheevos_var_t *var)
|
||||
|
||||
static int cheevos_test_condition(cheevos_cond_t *cond)
|
||||
{
|
||||
unsigned sval = cheevos_get_var_value(&cond->source);
|
||||
unsigned sval = cheevos_get_var_value(&cond->source) + cheevos_locals.add_buffer;
|
||||
unsigned tval = cheevos_get_var_value(&cond->target);
|
||||
|
||||
switch (cond->op)
|
||||
@@ -1867,6 +1897,9 @@ static int cheevos_test_cond_set(const cheevos_condset_t *condset,
|
||||
const cheevos_cond_t *end = condset->conds + condset->count;
|
||||
cheevos_cond_t *cond = NULL;
|
||||
|
||||
cheevos_locals.add_buffer = 0;
|
||||
cheevos_locals.add_hits = 0;
|
||||
|
||||
/* Now, read all Pause conditions, and if any are true,
|
||||
* do not process further (retain old state). */
|
||||
|
||||
@@ -1894,6 +1927,32 @@ static int cheevos_test_cond_set(const cheevos_condset_t *condset,
|
||||
{
|
||||
if (cond->type != CHEEVOS_COND_TYPE_STANDARD)
|
||||
continue;
|
||||
|
||||
if (cond->type == CHEEVOS_COND_TYPE_ADD_SOURCE)
|
||||
{
|
||||
cheevos_locals.add_buffer += cheevos_get_var_value(&cond->source);
|
||||
set_valid = 1;
|
||||
continue;
|
||||
}
|
||||
|
||||
if (cond->type == CHEEVOS_COND_TYPE_SUB_SOURCE)
|
||||
{
|
||||
cheevos_locals.add_buffer -= cheevos_get_var_value(&cond->source);
|
||||
set_valid = 1;
|
||||
continue;
|
||||
}
|
||||
|
||||
if (cond->type == CHEEVOS_COND_TYPE_ADD_HITS)
|
||||
{
|
||||
if (cheevos_test_condition(cond))
|
||||
{
|
||||
cond->curr_hits++;
|
||||
*dirty_conds = 1;
|
||||
}
|
||||
|
||||
cheevos_locals.add_hits += cond->curr_hits;
|
||||
continue;
|
||||
}
|
||||
|
||||
if (cond->req_hits != 0 && cond->curr_hits >= cond->req_hits)
|
||||
continue;
|
||||
@@ -1908,13 +1967,16 @@ static int cheevos_test_cond_set(const cheevos_condset_t *condset,
|
||||
/* Process this logic, if this condition is true: */
|
||||
if (cond->req_hits == 0)
|
||||
; /* Not a hit-based requirement: ignore any additional logic! */
|
||||
else if (cond->curr_hits < cond->req_hits)
|
||||
else if ((cond->curr_hits + cheevos_locals.add_hits) < cond->req_hits)
|
||||
cond_valid = 0; /* Not entirely valid yet! */
|
||||
|
||||
if (match_any)
|
||||
break;
|
||||
}
|
||||
|
||||
cheevos_locals.add_buffer = 0;
|
||||
cheevos_locals.add_hits = 0;
|
||||
|
||||
/* Sequential or non-sequential? */
|
||||
set_valid &= cond_valid;
|
||||
}
|
||||
@@ -2010,7 +2072,7 @@ static void cheevos_url_encode(const char *str, char *encoded, size_t len)
|
||||
{
|
||||
while (*str)
|
||||
{
|
||||
if ( isalnum(*str) || *str == '-'
|
||||
if ( isalnum((unsigned char)*str) || *str == '-'
|
||||
|| *str == '_' || *str == '.'
|
||||
|| *str == '~')
|
||||
{
|
||||
@@ -2586,10 +2648,65 @@ bool cheevos_toggle_hardcore_mode(void)
|
||||
return true;
|
||||
}
|
||||
|
||||
static void cheevos_patch_addresses(cheevoset_t* set)
|
||||
{
|
||||
unsigned i, j, k;
|
||||
cheevo_t* cheevo = set->cheevos;
|
||||
|
||||
for (i = set->count; i != 0; i--, cheevo++)
|
||||
{
|
||||
cheevos_condset_t* condset = cheevo->condition.condsets;
|
||||
|
||||
for (j = cheevo->condition.count; j != 0; j--, condset++)
|
||||
{
|
||||
cheevos_cond_t* cond = condset->conds;
|
||||
|
||||
for (k = condset->count; k != 0; k--, cond++)
|
||||
{
|
||||
switch (cond->source.type)
|
||||
{
|
||||
case CHEEVOS_VAR_TYPE_ADDRESS:
|
||||
case CHEEVOS_VAR_TYPE_DELTA_MEM:
|
||||
cheevos_parse_guest_addr(&cond->source, cond->source.value);
|
||||
#ifdef CHEEVOS_DUMP_ADDRS
|
||||
RARCH_LOG("CHEEVOS var %03d:%08X\n", cond->source.bank_id + 1, cond->source.value);
|
||||
#endif
|
||||
break;
|
||||
|
||||
default:
|
||||
break;
|
||||
}
|
||||
|
||||
switch (cond->target.type)
|
||||
{
|
||||
case CHEEVOS_VAR_TYPE_ADDRESS:
|
||||
case CHEEVOS_VAR_TYPE_DELTA_MEM:
|
||||
cheevos_parse_guest_addr(&cond->target, cond->target.value);
|
||||
#ifdef CHEEVOS_DUMP_ADDRS
|
||||
RARCH_LOG("CHEEVOS var %03d:%08X\n", cond->target.bank_id + 1, cond->target.value);
|
||||
#endif
|
||||
break;
|
||||
|
||||
default:
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void cheevos_test(void)
|
||||
{
|
||||
settings_t *settings = config_get_ptr();
|
||||
|
||||
if (!cheevos_locals.addrs_patched)
|
||||
{
|
||||
cheevos_patch_addresses(&cheevos_locals.core);
|
||||
cheevos_patch_addresses(&cheevos_locals.unofficial);
|
||||
|
||||
cheevos_locals.addrs_patched = true;
|
||||
}
|
||||
|
||||
cheevos_test_cheevo_set(&cheevos_locals.core);
|
||||
|
||||
if (settings->bools.cheevos_test_unofficial)
|
||||
@@ -2740,6 +2857,8 @@ static int cheevos_iterate(coro_t* coro)
|
||||
};
|
||||
|
||||
CORO_ENTER()
|
||||
|
||||
cheevos_locals.addrs_patched = false;
|
||||
|
||||
SETTINGS = config_get_ptr();
|
||||
|
||||
@@ -2913,7 +3032,6 @@ static int cheevos_iterate(coro_t* coro)
|
||||
RARCH_ERR("CHEEVOS error loading achievements.\n");
|
||||
CORO_STOP();
|
||||
}
|
||||
|
||||
#endif
|
||||
|
||||
#ifdef CHEEVOS_SAVE_JSON
|
||||
@@ -2943,6 +3061,30 @@ static int cheevos_iterate(coro_t* coro)
|
||||
* Outputs:
|
||||
*/
|
||||
CORO_GOSUB(PLAYING);
|
||||
|
||||
if(SETTINGS->bools.cheevos_verbose_enable)
|
||||
{
|
||||
const cheevo_t* cheevo = cheevos_locals.core.cheevos;
|
||||
const cheevo_t* end = cheevo + cheevos_locals.core.count;
|
||||
int number_of_unlocked = cheevos_locals.core.count;
|
||||
int mode;
|
||||
char msg[256];
|
||||
|
||||
if(SETTINGS->bools.cheevos_hardcore_mode_enable)
|
||||
mode = CHEEVOS_ACTIVE_HARDCORE;
|
||||
else
|
||||
mode = CHEEVOS_ACTIVE_SOFTCORE;
|
||||
|
||||
for(; cheevo < end; cheevo++)
|
||||
if(cheevo->active & mode)
|
||||
number_of_unlocked--;
|
||||
|
||||
snprintf(msg, sizeof(msg), "You have %d of %d achievements unlocked.",
|
||||
number_of_unlocked, cheevos_locals.core.count);
|
||||
msg[sizeof(msg) - 1] = 0;
|
||||
runloop_msg_queue_push(msg, 0, 6 * 60, false);
|
||||
}
|
||||
|
||||
CORO_STOP();
|
||||
|
||||
/**************************************************************************
|
||||
@@ -3264,7 +3406,16 @@ static int cheevos_iterate(coro_t* coro)
|
||||
free((void*)JSON);
|
||||
|
||||
if (!res)
|
||||
{
|
||||
if(SETTINGS->bools.cheevos_verbose_enable)
|
||||
{
|
||||
char msg[256];
|
||||
snprintf(msg, sizeof(msg), "RetroAchievements: logged in as \"%s\".", SETTINGS->arrays.cheevos_username);
|
||||
msg[sizeof(msg) - 1] = 0;
|
||||
runloop_msg_queue_push(msg, 0, 3 * 60, false);
|
||||
}
|
||||
CORO_RET();
|
||||
}
|
||||
}
|
||||
|
||||
runloop_msg_queue_push("Retro Achievements login error.", 0, 5 * 60, false);
|
||||
|
||||
@@ -1,5 +1,6 @@
|
||||
/* RetroArch - A frontend for libretro.
|
||||
* Copyright (C) 2011-2017 - Daniel De Matteis
|
||||
* Copyright (C) 2015-2017 - Andrés Suárez
|
||||
* Copyright (C) 2016-2017 - Brad Parker
|
||||
*
|
||||
* RetroArch is free software: you can redistribute it and/or modify it under the terms
|
||||
@@ -88,6 +89,8 @@
|
||||
#define DEFAULT_NETWORK_CMD_PORT 55355
|
||||
#define STDIN_BUF_SIZE 4096
|
||||
|
||||
extern int libui_main(void);
|
||||
|
||||
struct command
|
||||
{
|
||||
#ifdef HAVE_STDIN_CMD
|
||||
@@ -1051,6 +1054,7 @@ static void command_event_deinit_core(bool reinit)
|
||||
|
||||
command_event(CMD_EVENT_DISABLE_OVERRIDES, NULL);
|
||||
command_event(CMD_EVENT_RESTORE_DEFAULT_SHADER_PRESET, NULL);
|
||||
command_event(CMD_EVENT_RESTORE_REMAPS, NULL);
|
||||
}
|
||||
|
||||
static void command_event_init_cheats(void)
|
||||
@@ -1224,7 +1228,7 @@ static bool command_event_init_core(enum rarch_core_type *data)
|
||||
rarch_ctl(RARCH_CTL_UNSET_OVERRIDES_ACTIVE, NULL);
|
||||
}
|
||||
|
||||
/* Auto-remap: apply shader preset files */
|
||||
/* Auto-shaders: apply shader preset files */
|
||||
if(settings->bools.auto_shaders_enable)
|
||||
config_load_shader_preset();
|
||||
|
||||
@@ -1285,6 +1289,12 @@ static void command_event_restore_default_shader_preset(void)
|
||||
path_clear(RARCH_PATH_DEFAULT_SHADER_PRESET);
|
||||
}
|
||||
|
||||
static void command_event_restore_remaps(void)
|
||||
{
|
||||
if (rarch_ctl(RARCH_CTL_IS_REMAPS_GAME_ACTIVE, NULL))
|
||||
input_remapping_set_defaults();
|
||||
}
|
||||
|
||||
static bool command_event_save_auto_state(void)
|
||||
{
|
||||
char savestate_name_auto[PATH_MAX_LENGTH] = {0};
|
||||
@@ -1795,6 +1805,7 @@ bool command_event(enum event_command cmd, void *data)
|
||||
command_event(CMD_EVENT_AUTOSAVE_STATE, NULL);
|
||||
command_event(CMD_EVENT_DISABLE_OVERRIDES, NULL);
|
||||
command_event(CMD_EVENT_RESTORE_DEFAULT_SHADER_PRESET, NULL);
|
||||
command_event(CMD_EVENT_RESTORE_REMAPS, NULL);
|
||||
|
||||
if (is_inited)
|
||||
if (!task_push_start_dummy_core(&content_info))
|
||||
@@ -1854,9 +1865,17 @@ bool command_event(enum event_command cmd, void *data)
|
||||
if (settings->bools.cheevos_hardcore_mode_enable)
|
||||
return false;
|
||||
#endif
|
||||
|
||||
if (settings->bools.rewind_enable)
|
||||
state_manager_event_init((unsigned)settings->rewind_buffer_size);
|
||||
{
|
||||
#ifdef HAVE_NETWORKING
|
||||
/* Only enable state manager if netplay is not underway
|
||||
TODO: Add a setting for these tweaks */
|
||||
if (!netplay_driver_ctl(RARCH_NETPLAY_CTL_IS_ENABLED, NULL))
|
||||
#endif
|
||||
{
|
||||
state_manager_event_init((unsigned)settings->rewind_buffer_size);
|
||||
}
|
||||
}
|
||||
}
|
||||
break;
|
||||
case CMD_EVENT_REWIND_TOGGLE:
|
||||
@@ -1878,10 +1897,21 @@ bool command_event(enum event_command cmd, void *data)
|
||||
case CMD_EVENT_AUTOSAVE_INIT:
|
||||
command_event(CMD_EVENT_AUTOSAVE_DEINIT, NULL);
|
||||
#ifdef HAVE_THREADS
|
||||
if (autosave_init())
|
||||
runloop_set(RUNLOOP_ACTION_AUTOSAVE);
|
||||
else
|
||||
runloop_unset(RUNLOOP_ACTION_AUTOSAVE);
|
||||
{
|
||||
#ifdef HAVE_NETWORKING
|
||||
/* Only enable state manager if netplay is not underway
|
||||
TODO: Add a setting for these tweaks */
|
||||
settings_t *settings = config_get_ptr();
|
||||
if (settings->uints.autosave_interval != 0
|
||||
&& !netplay_driver_ctl(RARCH_NETPLAY_CTL_IS_ENABLED, NULL))
|
||||
#endif
|
||||
{
|
||||
if (autosave_init())
|
||||
runloop_set(RUNLOOP_ACTION_AUTOSAVE);
|
||||
else
|
||||
runloop_unset(RUNLOOP_ACTION_AUTOSAVE);
|
||||
}
|
||||
}
|
||||
#endif
|
||||
break;
|
||||
case CMD_EVENT_AUTOSAVE_STATE:
|
||||
@@ -1965,6 +1995,13 @@ bool command_event(enum event_command cmd, void *data)
|
||||
}
|
||||
g_defaults.content_history = NULL;
|
||||
|
||||
if (g_defaults.content_favorites)
|
||||
{
|
||||
playlist_write_file(g_defaults.content_favorites);
|
||||
playlist_free(g_defaults.content_favorites);
|
||||
}
|
||||
g_defaults.content_favorites = NULL;
|
||||
|
||||
if (g_defaults.music_history)
|
||||
{
|
||||
playlist_write_file(g_defaults.music_history);
|
||||
@@ -2008,6 +2045,13 @@ bool command_event(enum event_command cmd, void *data)
|
||||
settings->paths.path_content_history,
|
||||
content_history_size);
|
||||
|
||||
RARCH_LOG("%s: [%s].\n",
|
||||
msg_hash_to_str(MSG_LOADING_HISTORY_FILE),
|
||||
settings->paths.path_content_favorites);
|
||||
g_defaults.content_favorites = playlist_init(
|
||||
settings->paths.path_content_favorites,
|
||||
content_history_size);
|
||||
|
||||
RARCH_LOG("%s: [%s].\n",
|
||||
msg_hash_to_str(MSG_LOADING_HISTORY_FILE),
|
||||
settings->paths.path_content_music_history);
|
||||
@@ -2107,7 +2151,7 @@ bool command_event(enum event_command cmd, void *data)
|
||||
* need to make sure to keep a copy */
|
||||
struct retro_hw_render_callback hwr_copy;
|
||||
struct retro_hw_render_callback *hwr = video_driver_get_hw_context();
|
||||
const struct retro_hw_render_context_negotiation_interface *iface =
|
||||
const struct retro_hw_render_context_negotiation_interface *iface =
|
||||
video_driver_get_context_negotiation_interface();
|
||||
memcpy(&hwr_copy, hwr, sizeof(hwr_copy));
|
||||
|
||||
@@ -2140,6 +2184,18 @@ bool command_event(enum event_command cmd, void *data)
|
||||
if (ui_companion_is_on_foreground())
|
||||
ui_companion_driver_toggle();
|
||||
break;
|
||||
case CMD_EVENT_ADD_TO_FAVORITES:
|
||||
playlist_push(
|
||||
g_defaults.content_favorites,
|
||||
path_get(RARCH_PATH_CONTENT),
|
||||
NULL,
|
||||
file_path_str(FILE_PATH_DETECT),
|
||||
file_path_str(FILE_PATH_DETECT),
|
||||
NULL,
|
||||
NULL
|
||||
);
|
||||
playlist_write_file(g_defaults.content_favorites);
|
||||
break;
|
||||
case CMD_EVENT_RESTART_RETROARCH:
|
||||
if (!frontend_driver_set_fork(FRONTEND_FORK_RESTART))
|
||||
return false;
|
||||
@@ -2180,7 +2236,7 @@ bool command_event(enum event_command cmd, void *data)
|
||||
RARCH_LOG("%s\n", msg_hash_to_str(MSG_PAUSED));
|
||||
command_event(CMD_EVENT_AUDIO_STOP, NULL);
|
||||
|
||||
runloop_msg_queue_push(msg_hash_to_str(MSG_PAUSED), 1,
|
||||
runloop_msg_queue_push(msg_hash_to_str(MSG_PAUSED), 1,
|
||||
1, true);
|
||||
|
||||
if (!is_idle)
|
||||
@@ -2263,13 +2319,20 @@ bool command_event(enum event_command cmd, void *data)
|
||||
|
||||
command_event(CMD_EVENT_NETPLAY_DEINIT, NULL);
|
||||
|
||||
if (!init_netplay(NULL, hostname ? hostname :
|
||||
if (!init_netplay(NULL, hostname ? hostname :
|
||||
settings->paths.netplay_server,
|
||||
settings->uints.netplay_port))
|
||||
{
|
||||
command_event(CMD_EVENT_NETPLAY_DEINIT, NULL);
|
||||
return false;
|
||||
}
|
||||
|
||||
/* Disable rewind & sram autosave if it was enabled
|
||||
TODO: Add a setting for these tweaks */
|
||||
state_manager_event_deinit();
|
||||
#ifdef HAVE_THREADS
|
||||
autosave_deinit();
|
||||
#endif
|
||||
}
|
||||
break;
|
||||
/* init netplay via lobby when content is loaded */
|
||||
@@ -2282,12 +2345,12 @@ bool command_event(enum event_command cmd, void *data)
|
||||
|
||||
command_event(CMD_EVENT_NETPLAY_DEINIT, NULL);
|
||||
|
||||
RARCH_LOG("[netplay] connecting to %s:%d\n",
|
||||
hostname->elems[0].data, !string_is_empty(hostname->elems[1].data)
|
||||
RARCH_LOG("[netplay] connecting to %s:%d\n",
|
||||
hostname->elems[0].data, !string_is_empty(hostname->elems[1].data)
|
||||
? atoi(hostname->elems[1].data) : 55435);
|
||||
|
||||
if (!init_netplay(NULL, hostname->elems[0].data,
|
||||
!string_is_empty(hostname->elems[1].data)
|
||||
if (!init_netplay(NULL, hostname->elems[0].data,
|
||||
!string_is_empty(hostname->elems[1].data)
|
||||
? atoi(hostname->elems[1].data) : 55435))
|
||||
{
|
||||
command_event(CMD_EVENT_NETPLAY_DEINIT, NULL);
|
||||
@@ -2296,6 +2359,13 @@ bool command_event(enum event_command cmd, void *data)
|
||||
}
|
||||
|
||||
string_list_free(hostname);
|
||||
|
||||
/* Disable rewind if it was enabled
|
||||
TODO: Add a setting for these tweaks */
|
||||
state_manager_event_deinit();
|
||||
#ifdef HAVE_THREADS
|
||||
autosave_deinit();
|
||||
#endif
|
||||
}
|
||||
break;
|
||||
/* init netplay via lobby when content is not loaded */
|
||||
@@ -2308,12 +2378,12 @@ bool command_event(enum event_command cmd, void *data)
|
||||
|
||||
command_event(CMD_EVENT_NETPLAY_DEINIT, NULL);
|
||||
|
||||
RARCH_LOG("[netplay] connecting to %s:%d\n",
|
||||
hostname->elems[0].data, !string_is_empty(hostname->elems[1].data)
|
||||
RARCH_LOG("[netplay] connecting to %s:%d\n",
|
||||
hostname->elems[0].data, !string_is_empty(hostname->elems[1].data)
|
||||
? atoi(hostname->elems[1].data) : 55435);
|
||||
|
||||
if (!init_netplay_deferred(hostname->elems[0].data,
|
||||
!string_is_empty(hostname->elems[1].data)
|
||||
!string_is_empty(hostname->elems[1].data)
|
||||
? atoi(hostname->elems[1].data) : 55435))
|
||||
{
|
||||
command_event(CMD_EVENT_NETPLAY_DEINIT, NULL);
|
||||
@@ -2322,6 +2392,13 @@ bool command_event(enum event_command cmd, void *data)
|
||||
}
|
||||
|
||||
string_list_free(hostname);
|
||||
|
||||
/* Disable rewind if it was enabled
|
||||
TODO: Add a setting for these tweaks */
|
||||
state_manager_event_deinit();
|
||||
#ifdef HAVE_THREADS
|
||||
autosave_deinit();
|
||||
#endif
|
||||
}
|
||||
break;
|
||||
case CMD_EVENT_NETPLAY_FLIP_PLAYERS:
|
||||
@@ -2493,7 +2570,7 @@ bool command_event(enum event_command cmd, void *data)
|
||||
{
|
||||
static bool game_focus_state = false;
|
||||
intptr_t mode = (intptr_t)data;
|
||||
|
||||
|
||||
/* mode = -1: restores current game focus state
|
||||
* mode = 1: force set game focus, instead of toggling
|
||||
* any other: toggle
|
||||
@@ -2551,9 +2628,17 @@ bool command_event(enum event_command cmd, void *data)
|
||||
case CMD_EVENT_DISABLE_OVERRIDES:
|
||||
command_event_disable_overrides();
|
||||
break;
|
||||
case CMD_EVENT_RESTORE_REMAPS:
|
||||
command_event_restore_remaps();
|
||||
break;
|
||||
case CMD_EVENT_RESTORE_DEFAULT_SHADER_PRESET:
|
||||
command_event_restore_default_shader_preset();
|
||||
break;
|
||||
case CMD_EVENT_LIBUI_TEST:
|
||||
#if HAVE_LIBUI
|
||||
libui_main();
|
||||
#endif
|
||||
break;
|
||||
case CMD_EVENT_NONE:
|
||||
return false;
|
||||
}
|
||||
|
||||
@@ -134,6 +134,8 @@ enum event_command
|
||||
CMD_EVENT_REBOOT,
|
||||
/* Resume RetroArch when in menu. */
|
||||
CMD_EVENT_RESUME,
|
||||
/* Add a playlist entry to favorites. */
|
||||
CMD_EVENT_ADD_TO_FAVORITES,
|
||||
/* Toggles pause. */
|
||||
CMD_EVENT_PAUSE_TOGGLE,
|
||||
/* Pauses RetroArch. */
|
||||
@@ -218,7 +220,9 @@ enum event_command
|
||||
CMD_EVENT_MIXER_VOLUME_UP,
|
||||
CMD_EVENT_MIXER_VOLUME_DOWN,
|
||||
CMD_EVENT_DISABLE_OVERRIDES,
|
||||
CMD_EVENT_RESTORE_DEFAULT_SHADER_PRESET
|
||||
CMD_EVENT_RESTORE_REMAPS,
|
||||
CMD_EVENT_RESTORE_DEFAULT_SHADER_PRESET,
|
||||
CMD_EVENT_LIBUI_TEST
|
||||
};
|
||||
|
||||
bool command_set_shader(const char *arg);
|
||||
|
||||
+30
-2
@@ -58,6 +58,10 @@ static bool bundle_assets_extract_enable = true;
|
||||
static bool bundle_assets_extract_enable = false;
|
||||
#endif
|
||||
|
||||
#ifdef HAVE_MATERIALUI
|
||||
static bool materialui_icons_enable = true;
|
||||
#endif
|
||||
|
||||
static const bool def_history_list_enable = true;
|
||||
static const bool def_playlist_entry_remove = true;
|
||||
|
||||
@@ -250,6 +254,7 @@ static bool xmb_shadows_enable = false;
|
||||
static bool xmb_shadows_enable = true;
|
||||
#endif
|
||||
static bool xmb_show_settings = true;
|
||||
static bool xmb_show_favorites = true;
|
||||
#ifdef HAVE_IMAGEVIEWER
|
||||
static bool xmb_show_images = true;
|
||||
#endif
|
||||
@@ -266,6 +271,8 @@ static bool xmb_show_add = true;
|
||||
#endif
|
||||
#endif
|
||||
|
||||
static float menu_framebuffer_opacity = 0.900;
|
||||
|
||||
static float menu_wallpaper_opacity = 0.300;
|
||||
|
||||
static float menu_footer_opacity = 1.000;
|
||||
@@ -295,6 +302,11 @@ static bool default_auto_shaders_enable = true;
|
||||
static bool default_sort_savefiles_enable = false;
|
||||
static bool default_sort_savestates_enable = false;
|
||||
|
||||
static bool default_savestates_in_content_dir = false;
|
||||
static bool default_savefiles_in_content_dir = false;
|
||||
static bool default_systemfiles_in_content_dir = false;
|
||||
static bool default_screenshots_in_content_dir = false;
|
||||
|
||||
#if defined(__CELLOS_LV2__) || defined(_XBOX1) || defined(_XBOX360)
|
||||
static unsigned menu_toggle_gamepad_combo = INPUT_TOGGLE_L3_R3;
|
||||
#elif defined(VITA)
|
||||
@@ -308,6 +320,8 @@ static unsigned input_backtouch_enable = false;
|
||||
static unsigned input_backtouch_toggle = false;
|
||||
#endif
|
||||
|
||||
static bool show_physical_inputs = true;
|
||||
|
||||
static bool all_users_control_menu = false;
|
||||
|
||||
#if defined(ANDROID) || defined(_WIN32)
|
||||
@@ -604,10 +618,24 @@ static char buildbot_server_url[] = "http://bot.libretro.com/nightly/apple/osx/x
|
||||
static char buildbot_server_url[] = "http://buildbot.libretro.com/nightly/apple/osx/ppc/latest/";
|
||||
#endif
|
||||
#elif defined(_WIN32) && !defined(_XBOX)
|
||||
#if _MSC_VER == 1600
|
||||
#if defined(__x86_64__)
|
||||
static char buildbot_server_url[] = "http://buildbot.libretro.com/nightly/win-x86_64/latest/";
|
||||
static char buildbot_server_url[] = "http://buildbot.libretro.com/nightly/windows-msvc2010/x86_64/latest/";
|
||||
#elif defined(__i386__) || defined(__i486__) || defined(__i686__) || defined(_M_IX86) || defined(_M_IA64)
|
||||
static char buildbot_server_url[] = "http://buildbot.libretro.com/nightly/win-x86/latest/";
|
||||
static char buildbot_server_url[] = "http://buildbot.libretro.com/nightly/windows-msvc2010/x86/latest/";
|
||||
#endif
|
||||
#elif _MSC_VER == 1400
|
||||
#if defined(__x86_64__)
|
||||
static char buildbot_server_url[] = "http://buildbot.libretro.com/nightly/windows-msvc2005/x86_64/latest/";
|
||||
#elif defined(__i386__) || defined(__i486__) || defined(__i686__) || defined(_M_IX86) || defined(_M_IA64)
|
||||
static char buildbot_server_url[] = "http://buildbot.libretro.com/nightly/windows-msvc2005/x86/latest/";
|
||||
#endif
|
||||
#else
|
||||
#if defined(__x86_64__)
|
||||
static char buildbot_server_url[] = "http://buildbot.libretro.com/nightly/windows/x86_64/latest/";
|
||||
#elif defined(__i386__) || defined(__i486__) || defined(__i686__) || defined(_M_IX86) || defined(_M_IA64)
|
||||
static char buildbot_server_url[] = "http://buildbot.libretro.com/nightly/windows/x86/latest/";
|
||||
#endif
|
||||
#endif
|
||||
#elif defined(__linux__)
|
||||
#if defined(__x86_64__)
|
||||
|
||||
@@ -134,6 +134,12 @@ static const bool _alsa_supp = true;
|
||||
static const bool _alsa_supp = false;
|
||||
#endif
|
||||
|
||||
#ifdef HAVE_TINYALSA
|
||||
static const bool _tinyalsa_supp = true;
|
||||
#else
|
||||
static const bool _tinyalsa_supp = false;
|
||||
#endif
|
||||
|
||||
#ifdef HAVE_COREAUDIO
|
||||
static const bool _coreaudio_supp = true;
|
||||
#else
|
||||
|
||||
+82
-38
@@ -2,6 +2,7 @@
|
||||
* Copyright (C) 2010-2014 - Hans-Kristian Arntzen
|
||||
* Copyright (C) 2011-2017 - Daniel De Matteis
|
||||
* Copyright (C) 2014-2017 - Jean-André Santoni
|
||||
* Copyright (C) 2015-2017 - Andrés Suárez
|
||||
* Copyright (C) 2016-2017 - Brad Parker
|
||||
*
|
||||
* RetroArch is free software: you can redistribute it and/or modify it under the terms
|
||||
@@ -52,7 +53,7 @@
|
||||
/* All config related settings go here. */
|
||||
|
||||
struct config_bool_setting
|
||||
{
|
||||
{
|
||||
const char *ident;
|
||||
bool *ptr;
|
||||
bool def_enable;
|
||||
@@ -61,7 +62,7 @@ struct config_bool_setting
|
||||
};
|
||||
|
||||
struct config_int_setting
|
||||
{
|
||||
{
|
||||
const char *ident;
|
||||
int *ptr;
|
||||
bool def_enable;
|
||||
@@ -70,7 +71,7 @@ struct config_int_setting
|
||||
};
|
||||
|
||||
struct config_uint_setting
|
||||
{
|
||||
{
|
||||
const char *ident;
|
||||
unsigned *ptr;
|
||||
bool def_enable;
|
||||
@@ -79,7 +80,7 @@ struct config_uint_setting
|
||||
};
|
||||
|
||||
struct config_float_setting
|
||||
{
|
||||
{
|
||||
const char *ident;
|
||||
float *ptr;
|
||||
bool def_enable;
|
||||
@@ -88,7 +89,7 @@ struct config_float_setting
|
||||
};
|
||||
|
||||
struct config_array_setting
|
||||
{
|
||||
{
|
||||
const char *ident;
|
||||
char *ptr;
|
||||
bool def_enable;
|
||||
@@ -97,7 +98,7 @@ struct config_array_setting
|
||||
};
|
||||
|
||||
struct config_path_setting
|
||||
{
|
||||
{
|
||||
const char *ident;
|
||||
char *ptr;
|
||||
bool def_enable;
|
||||
@@ -139,6 +140,7 @@ enum audio_driver_enum
|
||||
AUDIO_OSS,
|
||||
AUDIO_ALSA,
|
||||
AUDIO_ALSATHREAD,
|
||||
AUDIO_TINYALSA,
|
||||
AUDIO_ROAR,
|
||||
AUDIO_AL,
|
||||
AUDIO_SL,
|
||||
@@ -312,6 +314,8 @@ static enum audio_driver_enum AUDIO_DEFAULT_DRIVER = AUDIO_PULSE;
|
||||
static enum audio_driver_enum AUDIO_DEFAULT_DRIVER = AUDIO_ALSATHREAD;
|
||||
#elif defined(HAVE_ALSA)
|
||||
static enum audio_driver_enum AUDIO_DEFAULT_DRIVER = AUDIO_ALSA;
|
||||
#elif defined(HAVE_TINYALSA)
|
||||
static enum audio_driver_enum AUDIO_DEFAULT_DRIVER = AUDIO_TINYALSA;
|
||||
#elif defined(HAVE_OSS)
|
||||
static enum audio_driver_enum AUDIO_DEFAULT_DRIVER = AUDIO_OSS;
|
||||
#elif defined(HAVE_JACK)
|
||||
@@ -482,7 +486,7 @@ static enum menu_driver_enum MENU_DEFAULT_DRIVER = MENU_NULL;
|
||||
tmp[count].def = default_setting; \
|
||||
tmp[count].handle = handle_setting; \
|
||||
count++; \
|
||||
}
|
||||
}
|
||||
|
||||
#define SETTING_BOOL(key, configval, default_enable, default_setting, handle_setting) \
|
||||
GENERAL_SETTING(key, configval, default_enable, default_setting, struct config_bool_setting, handle_setting)
|
||||
@@ -546,6 +550,8 @@ const char *config_get_default_audio(void)
|
||||
return "alsa";
|
||||
case AUDIO_ALSATHREAD:
|
||||
return "alsathread";
|
||||
case AUDIO_TINYALSA:
|
||||
return "tinyalsa";
|
||||
case AUDIO_ROAR:
|
||||
return "roar";
|
||||
case AUDIO_COREAUDIO:
|
||||
@@ -1009,11 +1015,13 @@ static struct config_path_setting *populate_settings_path(settings_t *settings,
|
||||
SETTING_PATH("cheat_database_path",
|
||||
settings->paths.path_cheat_database, false, NULL, true);
|
||||
#ifdef HAVE_MENU
|
||||
SETTING_PATH("menu_wallpaper",
|
||||
SETTING_PATH("menu_wallpaper",
|
||||
settings->paths.path_menu_wallpaper, false, NULL, true);
|
||||
#endif
|
||||
SETTING_PATH("content_history_path",
|
||||
settings->paths.path_content_history, false, NULL, true);
|
||||
SETTING_PATH("content_favorites_path",
|
||||
settings->paths.path_content_favorites, false, NULL, true);
|
||||
SETTING_PATH("content_music_history_path",
|
||||
settings->paths.path_content_music_history, false, NULL, true);
|
||||
SETTING_PATH("content_video_history_path",
|
||||
@@ -1028,7 +1036,7 @@ static struct config_path_setting *populate_settings_path(settings_t *settings,
|
||||
settings->paths.path_font, false, NULL, true);
|
||||
SETTING_PATH("cursor_directory",
|
||||
settings->paths.directory_cursor, false, NULL, true);
|
||||
SETTING_PATH("content_history_dir",
|
||||
SETTING_PATH("content_history_dir",
|
||||
settings->paths.directory_content_history, false, NULL, true);
|
||||
SETTING_PATH("screenshot_directory",
|
||||
settings->paths.directory_screenshot, true, NULL, true);
|
||||
@@ -1058,7 +1066,7 @@ static struct config_path_setting *populate_settings_path(settings_t *settings,
|
||||
settings->paths.directory_autoconfig, false, NULL, true);
|
||||
SETTING_PATH("audio_filter_dir",
|
||||
settings->paths.directory_audio_filter, true, NULL, true);
|
||||
SETTING_PATH("savefile_directory",
|
||||
SETTING_PATH("savefile_directory",
|
||||
dir_get_ptr(RARCH_DIR_SAVEFILE), true, NULL, false);
|
||||
SETTING_PATH("savestate_directory",
|
||||
dir_get_ptr(RARCH_DIR_SAVESTATE), true, NULL, false);
|
||||
@@ -1073,11 +1081,11 @@ static struct config_path_setting *populate_settings_path(settings_t *settings,
|
||||
settings->paths.directory_overlay, true, NULL, true);
|
||||
#endif
|
||||
#ifndef HAVE_DYNAMIC
|
||||
SETTING_PATH("libretro_path",
|
||||
SETTING_PATH("libretro_path",
|
||||
path_get_ptr(RARCH_PATH_CORE), false, NULL, false);
|
||||
#endif
|
||||
SETTING_PATH(
|
||||
"screenshot_directory",
|
||||
"screenshot_directory",
|
||||
settings->paths.directory_screenshot, true, NULL, false);
|
||||
|
||||
if (global)
|
||||
@@ -1175,6 +1183,7 @@ static struct config_bool_setting *populate_settings_bool(settings_t *settings,
|
||||
#endif
|
||||
SETTING_BOOL("menu_throttle_framerate", &settings->bools.menu_throttle_framerate, true, true, false);
|
||||
SETTING_BOOL("menu_linear_filter", &settings->bools.menu_linear_filter, true, true, false);
|
||||
SETTING_BOOL("menu_horizontal_animation", &settings->bools.menu_horizontal_animation, true, true, false);
|
||||
SETTING_BOOL("dpi_override_enable", &settings->bools.menu_dpi_override_enable, true, menu_dpi_override_enable, false);
|
||||
SETTING_BOOL("menu_pause_libretro", &settings->bools.menu_pause_libretro, true, true, false);
|
||||
SETTING_BOOL("menu_mouse_enable", &settings->bools.menu_mouse_enable, true, def_mouse_enable, false);
|
||||
@@ -1183,9 +1192,13 @@ static struct config_bool_setting *populate_settings_bool(settings_t *settings,
|
||||
SETTING_BOOL("menu_battery_level_enable", &settings->bools.menu_battery_level_enable, true, true, false);
|
||||
SETTING_BOOL("menu_core_enable", &settings->bools.menu_core_enable, true, true, false);
|
||||
SETTING_BOOL("menu_dynamic_wallpaper_enable", &settings->bools.menu_dynamic_wallpaper_enable, true, false, false);
|
||||
#ifdef HAVE_MATERIALUI
|
||||
SETTING_BOOL("materialui_icons_enable", &settings->bools.menu_materialui_icons_enable, true, materialui_icons_enable, false);
|
||||
#endif
|
||||
#ifdef HAVE_XMB
|
||||
SETTING_BOOL("xmb_shadows_enable", &settings->bools.menu_xmb_shadows_enable, true, xmb_shadows_enable, false);
|
||||
SETTING_BOOL("xmb_show_settings", &settings->bools.menu_xmb_show_settings, true, xmb_show_settings, false);
|
||||
SETTING_BOOL("xmb_show_favorites", &settings->bools.menu_xmb_show_favorites, true, xmb_show_favorites, false);
|
||||
#ifdef HAVE_IMAGEVIEWER
|
||||
SETTING_BOOL("xmb_show_images", &settings->bools.menu_xmb_show_images, true, xmb_show_images, false);
|
||||
#endif
|
||||
@@ -1199,14 +1212,14 @@ static struct config_bool_setting *populate_settings_bool(settings_t *settings,
|
||||
SETTING_BOOL("xmb_show_netplay", &settings->bools.menu_xmb_show_netplay, true, xmb_show_netplay, false);
|
||||
#endif
|
||||
SETTING_BOOL("xmb_show_history", &settings->bools.menu_xmb_show_history, true, xmb_show_history, false);
|
||||
#ifdef HAVE_LIBRETRODB
|
||||
#ifdef HAVE_LIBRETRODB
|
||||
SETTING_BOOL("xmb_show_add", &settings->bools.menu_xmb_show_add, true, xmb_show_add, false);
|
||||
#endif
|
||||
#endif
|
||||
SETTING_BOOL("filter_by_current_core", &settings->bools.filter_by_current_core, false, false /* TODO */, false);
|
||||
SETTING_BOOL("rgui_show_start_screen", &settings->bools.menu_show_start_screen, false, false /* TODO */, false);
|
||||
SETTING_BOOL("menu_navigation_wraparound_enable", &settings->bools.menu_navigation_wraparound_enable, true, true, false);
|
||||
SETTING_BOOL("menu_navigation_browser_filter_supported_extensions_enable",
|
||||
SETTING_BOOL("menu_navigation_browser_filter_supported_extensions_enable",
|
||||
&settings->bools.menu_navigation_browser_filter_supported_extensions_enable, true, true, false);
|
||||
SETTING_BOOL("menu_show_advanced_settings", &settings->bools.menu_show_advanced_settings, true, show_advanced_settings, false);
|
||||
#endif
|
||||
@@ -1214,10 +1227,12 @@ static struct config_bool_setting *populate_settings_bool(settings_t *settings,
|
||||
SETTING_BOOL("cheevos_enable", &settings->bools.cheevos_enable, true, cheevos_enable, false);
|
||||
SETTING_BOOL("cheevos_test_unofficial", &settings->bools.cheevos_test_unofficial, true, false, false);
|
||||
SETTING_BOOL("cheevos_hardcore_mode_enable", &settings->bools.cheevos_hardcore_mode_enable, true, false, false);
|
||||
SETTING_BOOL("cheevos_verbose_enable", &settings->bools.cheevos_verbose_enable, true, false, false);
|
||||
#endif
|
||||
#ifdef HAVE_OVERLAY
|
||||
SETTING_BOOL("input_overlay_enable", &settings->bools.input_overlay_enable, true, config_overlay_enable_default(), false);
|
||||
SETTING_BOOL("input_overlay_enable_autopreferred", &settings->bools.input_overlay_enable_autopreferred, true, true, false);
|
||||
SETTING_BOOL("input_overlay_show_physical_inputs", &settings->bools.input_overlay_show_physical_inputs, true, false, false);
|
||||
SETTING_BOOL("input_overlay_hide_in_menu", &settings->bools.input_overlay_hide_in_menu, true, overlay_hide_in_menu, false);
|
||||
#endif
|
||||
#ifdef HAVE_COMMAND
|
||||
@@ -1252,6 +1267,11 @@ static struct config_bool_setting *populate_settings_bool(settings_t *settings,
|
||||
SETTING_BOOL("audio_wasapi_float_format", &settings->bools.audio_wasapi_float_format, true, wasapi_float_format, false);
|
||||
#endif
|
||||
|
||||
SETTING_BOOL("savestates_in_content_dir", &settings->bools.savestates_in_content_dir, true, default_savestates_in_content_dir, false);
|
||||
SETTING_BOOL("savefiles_in_content_dir", &settings->bools.savefiles_in_content_dir, true, default_savefiles_in_content_dir, false);
|
||||
SETTING_BOOL("systemfiles_in_content_dir", &settings->bools.systemfiles_in_content_dir, true, default_systemfiles_in_content_dir, false);
|
||||
SETTING_BOOL("screenshots_in_content_dir", &settings->bools.screenshots_in_content_dir, true, default_screenshots_in_content_dir, false);
|
||||
|
||||
if (global)
|
||||
{
|
||||
SETTING_BOOL("custom_bgm_enable", &global->console.sound.system_bgm_enable, true, false, false);
|
||||
@@ -1280,6 +1300,7 @@ static struct config_float_setting *populate_settings_float(settings_t *settings
|
||||
#endif
|
||||
#ifdef HAVE_MENU
|
||||
SETTING_FLOAT("menu_wallpaper_opacity", &settings->floats.menu_wallpaper_opacity, true, menu_wallpaper_opacity, false);
|
||||
SETTING_FLOAT("menu_framebuffer_opacity", &settings->floats.menu_framebuffer_opacity, true, menu_framebuffer_opacity, false);
|
||||
SETTING_FLOAT("menu_footer_opacity", &settings->floats.menu_footer_opacity, true, menu_footer_opacity, false);
|
||||
SETTING_FLOAT("menu_header_opacity", &settings->floats.menu_header_opacity, true, menu_header_opacity, false);
|
||||
#endif
|
||||
@@ -1360,6 +1381,7 @@ static struct config_uint_setting *populate_settings_uint(settings_t *settings,
|
||||
#endif
|
||||
SETTING_UINT("bundle_assets_extract_version_current", &settings->uints.bundle_assets_extract_version_current, true, 0, false);
|
||||
SETTING_UINT("bundle_assets_extract_last_version", &settings->uints.bundle_assets_extract_last_version, true, 0, false);
|
||||
SETTING_UINT("input_overlay_show_physical_inputs_port", &settings->uints.input_overlay_show_physical_inputs_port, true, 0, false);
|
||||
|
||||
*size = count;
|
||||
|
||||
@@ -1640,6 +1662,7 @@ static void config_set_defaults(void)
|
||||
|
||||
*settings->paths.path_core_options = '\0';
|
||||
*settings->paths.path_content_history = '\0';
|
||||
*settings->paths.path_content_favorites = '\0';
|
||||
*settings->paths.path_content_music_history = '\0';
|
||||
*settings->paths.path_content_image_history = '\0';
|
||||
*settings->paths.path_content_video_history = '\0';
|
||||
@@ -1917,7 +1940,7 @@ static config_file_t *open_default_config_file(void)
|
||||
RARCH_WARN("Created new config file in: \"%s\".\n", conf_path);
|
||||
}
|
||||
#elif !defined(RARCH_CONSOLE)
|
||||
bool has_application_data =
|
||||
bool has_application_data =
|
||||
fill_pathname_application_data(application_data,
|
||||
sizeof(application_data));
|
||||
|
||||
@@ -1960,7 +1983,7 @@ static config_file_t *open_default_config_file(void)
|
||||
|
||||
skeleton_conf[0] = '\0';
|
||||
|
||||
// Build a retroarch.cfg path from the global config directory (/etc).
|
||||
/* Build a retroarch.cfg path from the global config directory (/etc). */
|
||||
fill_pathname_join(skeleton_conf, GLOBAL_CONFIG_DIR,
|
||||
file_path_str(FILE_PATH_MAIN_CONFIG), sizeof(skeleton_conf));
|
||||
|
||||
@@ -2093,7 +2116,7 @@ static bool check_shader_compatibility(enum file_path_enum enum_idx)
|
||||
return true;
|
||||
}
|
||||
|
||||
if (string_is_equal_fast(settings->arrays.video_driver, "gl", 2) ||
|
||||
if (string_is_equal_fast(settings->arrays.video_driver, "gl", 2) ||
|
||||
string_is_equal_fast(settings->arrays.video_driver, "d3d", 3)
|
||||
)
|
||||
{
|
||||
@@ -2164,7 +2187,7 @@ static void config_get_hex_base(config_file_t *conf,
|
||||
* Loads a config file and reads all the values into memory.
|
||||
*
|
||||
*/
|
||||
static bool config_load_file(const char *path, bool set_defaults,
|
||||
static bool config_load_file(const char *path, bool set_defaults,
|
||||
settings_t *settings)
|
||||
{
|
||||
unsigned i;
|
||||
@@ -2467,6 +2490,25 @@ static bool config_load_file(const char *path, bool set_defaults,
|
||||
}
|
||||
}
|
||||
|
||||
if (string_is_empty(settings->paths.path_content_favorites))
|
||||
{
|
||||
if (string_is_empty(settings->paths.directory_content_history))
|
||||
{
|
||||
fill_pathname_resolve_relative(
|
||||
settings->paths.path_content_favorites,
|
||||
path_get(RARCH_PATH_CONFIG),
|
||||
file_path_str(FILE_PATH_CONTENT_FAVORITES),
|
||||
sizeof(settings->paths.path_content_favorites));
|
||||
}
|
||||
else
|
||||
{
|
||||
fill_pathname_join(settings->paths.path_content_favorites,
|
||||
settings->paths.directory_content_history,
|
||||
file_path_str(FILE_PATH_CONTENT_FAVORITES),
|
||||
sizeof(settings->paths.path_content_favorites));
|
||||
}
|
||||
}
|
||||
|
||||
if (string_is_empty(settings->paths.path_content_music_history))
|
||||
{
|
||||
if (string_is_empty(settings->paths.directory_content_history))
|
||||
@@ -2700,10 +2742,10 @@ end:
|
||||
* This function only has an effect if a game-specific or core-specific
|
||||
* configuration file exists at respective locations.
|
||||
*
|
||||
* core-specific: $CONFIG_DIR/$CORE_NAME/$CORE_NAME.cfg
|
||||
* core-specific: $CONFIG_DIR/$CORE_NAME/$CORE_NAME.cfg
|
||||
* fallback: $CURRENT_CFG_LOCATION/$CORE_NAME/$CORE_NAME.cfg
|
||||
*
|
||||
* game-specific: $CONFIG_DIR/$CORE_NAME/$ROM_NAME.cfg
|
||||
* game-specific: $CONFIG_DIR/$CORE_NAME/$ROM_NAME.cfg
|
||||
* fallback: $CURRENT_CFG_LOCATION/$CORE_NAME/$GAME_NAME.cfg
|
||||
*
|
||||
* Returns: false if there was an error or no action was performed.
|
||||
@@ -2799,7 +2841,7 @@ bool config_load_override(void)
|
||||
/* Re-load the configuration with any overrides that might have been found */
|
||||
buf[0] = '\0';
|
||||
|
||||
/* Store the libretro_path we're using since it will be
|
||||
/* Store the libretro_path we're using since it will be
|
||||
* overwritten by the override when reloading. */
|
||||
strlcpy(buf, path_get(RARCH_PATH_CORE), sizeof(buf));
|
||||
|
||||
@@ -2919,6 +2961,7 @@ bool config_load_remap(void)
|
||||
if (input_remapping_load_file(new_conf, game_path))
|
||||
{
|
||||
runloop_msg_queue_push("Game remap file loaded.", 1, 100, true);
|
||||
rarch_ctl(RARCH_CTL_SET_REMAPS_GAME_ACTIVE, NULL);
|
||||
return true;
|
||||
}
|
||||
}
|
||||
@@ -2938,6 +2981,7 @@ bool config_load_remap(void)
|
||||
if (input_remapping_load_file(new_conf, core_path))
|
||||
{
|
||||
runloop_msg_queue_push("Core remap file loaded.", 1, 100, true);
|
||||
rarch_ctl(RARCH_CTL_SET_REMAPS_CORE_ACTIVE, NULL);
|
||||
return true;
|
||||
}
|
||||
}
|
||||
@@ -3125,7 +3169,6 @@ static void save_keybind_hat(config_file_t *conf, const char *key,
|
||||
break;
|
||||
|
||||
default:
|
||||
retro_assert(0);
|
||||
break;
|
||||
}
|
||||
|
||||
@@ -3247,7 +3290,7 @@ static void save_keybinds_user(config_file_t *conf, unsigned user)
|
||||
*/
|
||||
void config_load(void)
|
||||
{
|
||||
/* Flush out some states that could have been
|
||||
/* Flush out some states that could have been
|
||||
* set by core environment variables */
|
||||
core_unset_input_descriptors();
|
||||
|
||||
@@ -3437,7 +3480,7 @@ bool config_save_file(const char *path)
|
||||
|
||||
#ifdef HAVE_MENU
|
||||
config_set_path(conf, "xmb_font",
|
||||
!string_is_empty(settings->paths.path_menu_xmb_font)
|
||||
!string_is_empty(settings->paths.path_menu_xmb_font)
|
||||
? settings->paths.path_menu_xmb_font : "");
|
||||
#endif
|
||||
|
||||
@@ -3628,7 +3671,7 @@ bool config_save_overrides(int override_type)
|
||||
fill_pathname_application_special(config_directory, sizeof(config_directory),
|
||||
APPLICATION_SPECIAL_DIRECTORY_CONFIG);
|
||||
|
||||
fill_pathname_join(override_directory, config_directory, core_name,
|
||||
fill_pathname_join(override_directory, config_directory, core_name,
|
||||
sizeof(override_directory));
|
||||
|
||||
if(!path_file_exists(override_directory))
|
||||
@@ -3685,9 +3728,9 @@ bool config_save_overrides(int override_type)
|
||||
{
|
||||
if ((*bool_settings[i].ptr) != (*bool_overrides[i].ptr))
|
||||
{
|
||||
RARCH_LOG(" original: %s=%d\n",
|
||||
RARCH_LOG(" original: %s=%d\n",
|
||||
bool_settings[i].ident, (*bool_settings[i].ptr));
|
||||
RARCH_LOG(" override: %s=%d\n",
|
||||
RARCH_LOG(" override: %s=%d\n",
|
||||
bool_overrides[i].ident, (*bool_overrides[i].ptr));
|
||||
config_set_bool(conf, bool_overrides[i].ident,
|
||||
(*bool_overrides[i].ptr));
|
||||
@@ -3697,9 +3740,9 @@ bool config_save_overrides(int override_type)
|
||||
{
|
||||
if ((*int_settings[i].ptr) != (*int_overrides[i].ptr))
|
||||
{
|
||||
RARCH_LOG(" original: %s=%d\n",
|
||||
RARCH_LOG(" original: %s=%d\n",
|
||||
int_settings[i].ident, (*int_settings[i].ptr));
|
||||
RARCH_LOG(" override: %s=%d\n",
|
||||
RARCH_LOG(" override: %s=%d\n",
|
||||
int_overrides[i].ident, (*int_overrides[i].ptr));
|
||||
config_set_int(conf, int_overrides[i].ident,
|
||||
(*int_overrides[i].ptr));
|
||||
@@ -3709,9 +3752,9 @@ bool config_save_overrides(int override_type)
|
||||
{
|
||||
if ((*uint_settings[i].ptr) != (*uint_overrides[i].ptr))
|
||||
{
|
||||
RARCH_LOG(" original: %s=%d\n",
|
||||
RARCH_LOG(" original: %s=%d\n",
|
||||
uint_settings[i].ident, (*uint_settings[i].ptr));
|
||||
RARCH_LOG(" override: %s=%d\n",
|
||||
RARCH_LOG(" override: %s=%d\n",
|
||||
uint_overrides[i].ident, (*uint_overrides[i].ptr));
|
||||
config_set_int(conf, uint_overrides[i].ident,
|
||||
(*uint_overrides[i].ptr));
|
||||
@@ -3721,9 +3764,9 @@ bool config_save_overrides(int override_type)
|
||||
{
|
||||
if ((*float_settings[i].ptr) != (*float_overrides[i].ptr))
|
||||
{
|
||||
RARCH_LOG(" original: %s=%f\n",
|
||||
RARCH_LOG(" original: %s=%f\n",
|
||||
float_settings[i].ident, *float_settings[i].ptr);
|
||||
RARCH_LOG(" override: %s=%f\n",
|
||||
RARCH_LOG(" override: %s=%f\n",
|
||||
float_overrides[i].ident, *float_overrides[i].ptr);
|
||||
config_set_float(conf, float_overrides[i].ident,
|
||||
*float_overrides[i].ptr);
|
||||
@@ -3734,9 +3777,9 @@ bool config_save_overrides(int override_type)
|
||||
{
|
||||
if (!string_is_equal(array_settings[i].ptr, array_overrides[i].ptr))
|
||||
{
|
||||
RARCH_LOG(" original: %s=%s\n",
|
||||
RARCH_LOG(" original: %s=%s\n",
|
||||
array_settings[i].ident, array_settings[i].ptr);
|
||||
RARCH_LOG(" override: %s=%s\n",
|
||||
RARCH_LOG(" override: %s=%s\n",
|
||||
array_overrides[i].ident, array_overrides[i].ptr);
|
||||
config_set_string(conf, array_overrides[i].ident,
|
||||
array_overrides[i].ptr);
|
||||
@@ -3747,9 +3790,9 @@ bool config_save_overrides(int override_type)
|
||||
{
|
||||
if (!string_is_equal(path_settings[i].ptr, path_overrides[i].ptr))
|
||||
{
|
||||
RARCH_LOG(" original: %s=%s\n",
|
||||
RARCH_LOG(" original: %s=%s\n",
|
||||
path_settings[i].ident, path_settings[i].ptr);
|
||||
RARCH_LOG(" override: %s=%s\n",
|
||||
RARCH_LOG(" override: %s=%s\n",
|
||||
path_overrides[i].ident, path_overrides[i].ptr);
|
||||
config_set_path(conf, path_overrides[i].ident,
|
||||
path_overrides[i].ptr);
|
||||
@@ -3771,7 +3814,8 @@ bool config_save_overrides(int override_type)
|
||||
snprintf(cfg, sizeof(cfg), "input_player%u_joypad_index", i + 1);
|
||||
config_set_int(conf, cfg, overrides->uints.input_joypad_map[i]);
|
||||
}
|
||||
if (input_config_get_device(i) != overrides->uints.input_libretro_device[i])
|
||||
|
||||
if (settings->uints.input_libretro_device[i] != overrides->uints.input_libretro_device[i])
|
||||
{
|
||||
snprintf(cfg, sizeof(cfg), "input_libretro_device_p%u", i + 1);
|
||||
config_set_int(conf, cfg, overrides->uints.input_libretro_device[i]);
|
||||
|
||||
@@ -88,6 +88,7 @@ typedef struct settings
|
||||
bool input_overlay_enable;
|
||||
bool input_overlay_enable_autopreferred;
|
||||
bool input_overlay_hide_in_menu;
|
||||
bool input_overlay_show_physical_inputs;
|
||||
bool input_descriptor_label_show;
|
||||
bool input_descriptor_hide_unbound;
|
||||
bool input_all_users_control_menu;
|
||||
@@ -119,10 +120,13 @@ typedef struct settings
|
||||
bool menu_show_advanced_settings;
|
||||
bool menu_throttle_framerate;
|
||||
bool menu_linear_filter;
|
||||
bool menu_horizontal_animation;
|
||||
bool menu_show_online_updater;
|
||||
bool menu_show_core_updater;
|
||||
bool menu_materialui_icons_enable;
|
||||
bool menu_xmb_shadows_enable;
|
||||
bool menu_xmb_show_settings;
|
||||
bool menu_xmb_show_favorites;
|
||||
bool menu_xmb_show_images;
|
||||
bool menu_xmb_show_music;
|
||||
bool menu_xmb_show_video;
|
||||
@@ -158,6 +162,7 @@ typedef struct settings
|
||||
bool cheevos_enable;
|
||||
bool cheevos_test_unofficial;
|
||||
bool cheevos_hardcore_mode_enable;
|
||||
bool cheevos_verbose_enable;
|
||||
#endif
|
||||
|
||||
/* Camera */
|
||||
@@ -208,6 +213,11 @@ typedef struct settings
|
||||
bool sort_savestates_enable;
|
||||
bool config_save_on_exit;
|
||||
bool show_hidden_files;
|
||||
|
||||
bool savefiles_in_content_dir;
|
||||
bool savestates_in_content_dir;
|
||||
bool screenshots_in_content_dir;
|
||||
bool systemfiles_in_content_dir;
|
||||
#ifdef HAVE_LAKKA
|
||||
bool ssh_enable;
|
||||
bool samba_enable;
|
||||
@@ -229,6 +239,7 @@ typedef struct settings
|
||||
float video_msg_color_b;
|
||||
|
||||
float menu_wallpaper_opacity;
|
||||
float menu_framebuffer_opacity;
|
||||
float menu_footer_opacity;
|
||||
float menu_header_opacity;
|
||||
|
||||
@@ -321,6 +332,8 @@ typedef struct settings
|
||||
|
||||
unsigned camera_width;
|
||||
unsigned camera_height;
|
||||
|
||||
unsigned input_overlay_show_physical_inputs_port;
|
||||
} uints;
|
||||
|
||||
struct
|
||||
@@ -372,6 +385,7 @@ typedef struct settings
|
||||
char path_softfilter_plugin[PATH_MAX_LENGTH];
|
||||
char path_core_options[PATH_MAX_LENGTH];
|
||||
char path_content_history[PATH_MAX_LENGTH];
|
||||
char path_content_favorites[PATH_MAX_LENGTH];
|
||||
char path_content_music_history[PATH_MAX_LENGTH];
|
||||
char path_content_image_history[PATH_MAX_LENGTH];
|
||||
char path_content_video_history[PATH_MAX_LENGTH];
|
||||
@@ -388,6 +402,7 @@ typedef struct settings
|
||||
char directory_video_filter[PATH_MAX_LENGTH];
|
||||
char directory_video_shader[PATH_MAX_LENGTH];
|
||||
char directory_content_history[PATH_MAX_LENGTH];
|
||||
char directory_content_favorites[PATH_MAX_LENGTH];
|
||||
char directory_libretro[PATH_MAX_LENGTH];
|
||||
char directory_cursor[PATH_MAX_LENGTH];
|
||||
char directory_input_remapping[PATH_MAX_LENGTH];
|
||||
|
||||
@@ -22,6 +22,7 @@
|
||||
#include <net/net_socket.h>
|
||||
|
||||
#include <retro_miscellaneous.h>
|
||||
#include <retro_timers.h>
|
||||
|
||||
#include <libretro.h>
|
||||
|
||||
|
||||
+8
-2
@@ -399,7 +399,7 @@ static int database_cursor_close(libretrodb_t *db, libretrodb_cursor_t *cur)
|
||||
}
|
||||
|
||||
database_info_handle_t *database_info_dir_init(const char *dir,
|
||||
enum database_type type)
|
||||
enum database_type type, retro_task_t *task)
|
||||
{
|
||||
unsigned i;
|
||||
database_info_handle_t *db = (database_info_handle_t*)
|
||||
@@ -423,6 +423,9 @@ database_info_handle_t *database_info_dir_init(const char *dir,
|
||||
{
|
||||
const char *path = db->list->elems[i].data;
|
||||
|
||||
if (task)
|
||||
task_set_progress(task, (i / (float)db->list->size) * 100);
|
||||
|
||||
if (path_is_compressed_file(path) && !path_contains_compressed_file(path))
|
||||
{
|
||||
struct string_list *archive_list = path_is_compressed_file(path) ?
|
||||
@@ -469,7 +472,7 @@ error:
|
||||
}
|
||||
|
||||
database_info_handle_t *database_info_file_init(const char *path,
|
||||
enum database_type type)
|
||||
enum database_type type, retro_task_t *task)
|
||||
{
|
||||
union string_list_elem_attr attr;
|
||||
database_info_handle_t *db = (database_info_handle_t*)
|
||||
@@ -501,6 +504,9 @@ database_info_handle_t *database_info_file_init(const char *path,
|
||||
char new_path[PATH_MAX_LENGTH];
|
||||
size_t path_len = strlen(path);
|
||||
|
||||
if (task)
|
||||
task_set_progress(task, (i / (float)archive_list->size) * 100);
|
||||
|
||||
new_path[0] = '\0';
|
||||
|
||||
strlcpy(new_path, path, sizeof(new_path));
|
||||
|
||||
+3
-2
@@ -23,6 +23,7 @@
|
||||
|
||||
#include <file/archive_file.h>
|
||||
#include <retro_common_api.h>
|
||||
#include <queues/task_queue.h>
|
||||
|
||||
RETRO_BEGIN_DECLS
|
||||
|
||||
@@ -125,10 +126,10 @@ database_info_list_t *database_info_list_new(const char *rdb_path,
|
||||
void database_info_list_free(database_info_list_t *list);
|
||||
|
||||
database_info_handle_t *database_info_dir_init(const char *dir,
|
||||
enum database_type type);
|
||||
enum database_type type, retro_task_t *task);
|
||||
|
||||
database_info_handle_t *database_info_file_init(const char *path,
|
||||
enum database_type type);
|
||||
enum database_type type, retro_task_t *task);
|
||||
|
||||
void database_info_free(database_info_handle_t *handle);
|
||||
|
||||
|
||||
@@ -102,6 +102,7 @@ struct defaults
|
||||
|
||||
#ifndef IS_SALAMANDER
|
||||
playlist_t *content_history;
|
||||
playlist_t *content_favorites;
|
||||
#ifdef HAVE_IMAGEVIEWER
|
||||
playlist_t *image_history;
|
||||
#endif
|
||||
|
||||
Vendored
+21
@@ -0,0 +1,21 @@
|
||||
|
||||
Micromod (c)2017 [email protected]
|
||||
|
||||
A good-quality player library for the ProTracker MOD music format
|
||||
for Javascript (HTML5 Web Audio), Java, ANSI C (SDL) and Pascal (SDL).
|
||||
|
||||
Also hosted here is IBXM, a player library for the ProTracker MOD,
|
||||
Scream Tracker 3 S3M, and FastTracker 2 XM music formats for Javascript
|
||||
(HTML5 Web Audio), Java and ANSI C.
|
||||
|
||||
The Java version of Micromod contains a powerful command-line tool for
|
||||
the creation of MOD files from textual MT files and WAV samples.
|
||||
There is some basic documentation built-in to the tool and some example
|
||||
MT files are contained in the songs directory. Some knowledge of the
|
||||
ProTracker MOD format, audio synthesis and "tracking" is assumed.
|
||||
|
||||
If you have any questions or feedback please feel free to
|
||||
contact me at the above email address!
|
||||
|
||||
Cheers,
|
||||
Martin
|
||||
Vendored
+1922
File diff suppressed because it is too large.
Load diff
Vendored
+71
@@ -0,0 +1,71 @@
|
||||
|
||||
/* ibxm/ac mod/xm/s3m replay (c)[email protected] */
|
||||
|
||||
#ifndef __IBXM_H__
|
||||
#define __IBXM_H__
|
||||
|
||||
extern const char *IBXM_VERSION;
|
||||
|
||||
struct data {
|
||||
char *buffer;
|
||||
int length;
|
||||
};
|
||||
|
||||
struct sample {
|
||||
char name[ 32 ];
|
||||
int loop_start, loop_length;
|
||||
short volume, panning, rel_note, fine_tune, *data;
|
||||
};
|
||||
|
||||
struct envelope {
|
||||
char enabled, sustain, looped, num_points;
|
||||
short sustain_tick, loop_start_tick, loop_end_tick;
|
||||
short points_tick[ 16 ], points_ampl[ 16 ];
|
||||
};
|
||||
|
||||
struct instrument {
|
||||
int num_samples, vol_fadeout;
|
||||
char name[ 32 ], key_to_sample[ 97 ];
|
||||
char vib_type, vib_sweep, vib_depth, vib_rate;
|
||||
struct envelope vol_env, pan_env;
|
||||
struct sample *samples;
|
||||
};
|
||||
|
||||
struct pattern {
|
||||
int num_channels, num_rows;
|
||||
char *data;
|
||||
};
|
||||
|
||||
struct module {
|
||||
char name[ 32 ];
|
||||
int num_channels, num_instruments;
|
||||
int num_patterns, sequence_len, restart_pos;
|
||||
int default_gvol, default_speed, default_tempo, c2_rate, gain;
|
||||
int linear_periods, fast_vol_slides;
|
||||
unsigned char *default_panning, *sequence;
|
||||
struct pattern *patterns;
|
||||
struct instrument *instruments;
|
||||
};
|
||||
|
||||
/* Allocate and initialize a module from the specified data, returns NULL on error.
|
||||
Message should point to a 64-character buffer to receive error messages. */
|
||||
struct module* module_load( struct data *data, char *message );
|
||||
/* Deallocate the specified module. */
|
||||
void dispose_module( struct module *module );
|
||||
/* Allocate and initialize a replay with the specified module and sampling rate. */
|
||||
struct replay* new_replay( struct module *module, int sample_rate, int interpolation );
|
||||
/* Deallocate the specified replay. */
|
||||
void dispose_replay( struct replay *replay );
|
||||
/* Returns the song duration in samples at the current sampling rate. */
|
||||
int replay_calculate_duration( struct replay *replay );
|
||||
/* Seek to approximately the specified sample position.
|
||||
The actual sample position reached is returned. */
|
||||
int replay_seek( struct replay *replay, int sample_pos );
|
||||
/* Set the pattern in the sequence to play. The tempo is reset to the default. */
|
||||
void replay_set_sequence_pos( struct replay *replay, int pos );
|
||||
/* Generates audio and returns the number of stereo samples written into mix_buf. */
|
||||
int replay_get_audio( struct replay *replay, int *mix_buf );
|
||||
/* Returns the length of the output buffer required by replay_get_audio(). */
|
||||
int calculate_mix_buf_len( int sample_rate );
|
||||
|
||||
#endif
|
||||
Vendored
+37
@@ -0,0 +1,37 @@
|
||||
---
|
||||
Copyright (c) 2015, Martin Cameron
|
||||
All rights reserved.
|
||||
|
||||
Redistribution and use in source and binary forms, with or
|
||||
without modification, are permitted provided that the
|
||||
following conditions are met:
|
||||
|
||||
* Redistributions of source code must retain the above
|
||||
copyright notice, this list of conditions and the
|
||||
following disclaimer.
|
||||
|
||||
* Redistributions in binary form must reproduce the
|
||||
above copyright notice, this list of conditions and the
|
||||
following disclaimer in the documentation and/or other
|
||||
materials provided with the distribution.
|
||||
|
||||
* Neither the name of the organization nor the names of
|
||||
its contributors may be used to endorse or promote
|
||||
products derived from this software without specific
|
||||
prior written permission.
|
||||
|
||||
THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND
|
||||
CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES,
|
||||
INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
|
||||
MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
|
||||
DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR
|
||||
CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT,
|
||||
INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
|
||||
(INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE
|
||||
GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR
|
||||
BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
|
||||
LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
|
||||
(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT
|
||||
OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
|
||||
POSSIBILITY OF SUCH DAMAGE.
|
||||
---
|
||||
Vendored
+9
@@ -0,0 +1,9 @@
|
||||
Copyright (c) 2014 Pietro Gagliardi
|
||||
|
||||
Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions:
|
||||
|
||||
The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software.
|
||||
|
||||
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
|
||||
|
||||
(this is called the MIT License or Expat License; see http://www.opensource.org/licenses/MIT)
|
||||
Vendored
+16
@@ -0,0 +1,16 @@
|
||||
# 3 june 2016
|
||||
|
||||
list(APPEND _LIBUI_SOURCES
|
||||
common/areaevents.c
|
||||
common/control.c
|
||||
common/debug.c
|
||||
common/matrix.c
|
||||
common/shouldquit.c
|
||||
common/userbugs.c
|
||||
)
|
||||
set(_LIBUI_SOURCES ${_LIBUI_SOURCES} PARENT_SCOPE)
|
||||
|
||||
list(APPEND _LIBUI_INCLUDEDIRS
|
||||
common
|
||||
)
|
||||
set(_LIBUI_INCLUDEDIRS ${_LIBUI_INCLUDEDIRS} PARENT_SCOPE)
|
||||
Vendored
+167
@@ -0,0 +1,167 @@
|
||||
// 29 march 2014
|
||||
#include "../ui.h"
|
||||
#include "uipriv.h"
|
||||
|
||||
/*
|
||||
Windows and GTK+ have a limit of 2 and 3 clicks, respectively, natively supported. Fortunately, we can simulate the double/triple-click behavior to build higher-order clicks. We can use the same algorithm Windows uses on both:
|
||||
http://blogs.msdn.com/b/oldnewthing/archive/2004/10/18/243925.aspx
|
||||
For GTK+, we pull the double-click time and double-click distance, which work the same as the equivalents on Windows (so the distance is in all directions), from the GtkSettings system.
|
||||
|
||||
On GTK+ this will also allow us to discard the GDK_BUTTON_2PRESS and GDK_BUTTON_3PRESS events, so the button press stream will be just like on other platforms.
|
||||
|
||||
Thanks to mclasen, garnacho_, halfline, and tristan in irc.gimp.net/#gtk+.
|
||||
*/
|
||||
|
||||
// x, y, xdist, ydist, and c.rect must have the same units
|
||||
// so must time, maxTime, and c.prevTime
|
||||
int clickCounterClick(clickCounter *c, int button, int x, int y, uintptr_t time, uintptr_t maxTime, int32_t xdist, int32_t ydist)
|
||||
{
|
||||
// different button than before? if so, don't count
|
||||
if (button != c->curButton)
|
||||
c->count = 0;
|
||||
|
||||
// (x, y) in the allowed region for a double-click? if not, don't count
|
||||
if (x < c->rectX0)
|
||||
c->count = 0;
|
||||
if (y < c->rectY0)
|
||||
c->count = 0;
|
||||
if (x >= c->rectX1)
|
||||
c->count = 0;
|
||||
if (y >= c->rectY1)
|
||||
c->count = 0;
|
||||
|
||||
// too slow? if so, don't count
|
||||
// note the below expression; time > (c.prevTime + maxTime) can overflow!
|
||||
if ((time - c->prevTime) > maxTime) // too slow; don't count
|
||||
c->count = 0;
|
||||
|
||||
c->count++; // if either of the above ifs happened, this will make the click count 1; otherwise it will make the click count 2, 3, 4, 5, ...
|
||||
|
||||
// now we need to update the internal structures for the next test
|
||||
c->curButton = button;
|
||||
c->prevTime = time;
|
||||
c->rectX0 = x - xdist;
|
||||
c->rectY0 = y - ydist;
|
||||
c->rectX1 = x + xdist;
|
||||
c->rectY1 = y + ydist;
|
||||
|
||||
return c->count;
|
||||
}
|
||||
|
||||
void clickCounterReset(clickCounter *c)
|
||||
{
|
||||
c->curButton = 0;
|
||||
c->rectX0 = 0;
|
||||
c->rectY0 = 0;
|
||||
c->rectX1 = 0;
|
||||
c->rectY1 = 0;
|
||||
c->prevTime = 0;
|
||||
c->count = 0;
|
||||
}
|
||||
|
||||
/*
|
||||
For position independence across international keyboard layouts, typewriter keys are read using scancodes (which are always set 1).
|
||||
Windows provides the scancodes directly in the LPARAM.
|
||||
GTK+ provides the scancodes directly from the underlying window system via GdkEventKey.hardware_keycode.
|
||||
On X11, this is scancode + 8 (because X11 keyboard codes have a range of [8,255]).
|
||||
Wayland is guaranteed to give the same result (thanks ebassi in irc.gimp.net/#gtk+).
|
||||
On Linux, where evdev is used instead of polling scancodes directly from the keyboard, evdev's typewriter section key code constants are the same as scancodes anyway, so the rules above apply.
|
||||
Typewriter section scancodes are the same across international keyboards with some exceptions that have been accounted for (see KeyEvent's documentation); see http://www.quadibloc.com/comp/scan.htm for details.
|
||||
Non-typewriter keys can be handled safely using constants provided by the respective backend API.
|
||||
|
||||
Because GTK+ keysyms may or may not obey Num Lock, we also handle the 0-9 and . keys on the numeric keypad with scancodes (they match too).
|
||||
*/
|
||||
|
||||
// use uintptr_t to be safe; the size of the scancode/hardware key code field on each platform is different
|
||||
static const struct {
|
||||
uintptr_t scancode;
|
||||
char equiv;
|
||||
} scancodeKeys[] = {
|
||||
{ 0x02, '1' },
|
||||
{ 0x03, '2' },
|
||||
{ 0x04, '3' },
|
||||
{ 0x05, '4' },
|
||||
{ 0x06, '5' },
|
||||
{ 0x07, '6' },
|
||||
{ 0x08, '7' },
|
||||
{ 0x09, '8' },
|
||||
{ 0x0A, '9' },
|
||||
{ 0x0B, '0' },
|
||||
{ 0x0C, '-' },
|
||||
{ 0x0D, '=' },
|
||||
{ 0x0E, '\b' },
|
||||
{ 0x0F, '\t' },
|
||||
{ 0x10, 'q' },
|
||||
{ 0x11, 'w' },
|
||||
{ 0x12, 'e' },
|
||||
{ 0x13, 'r' },
|
||||
{ 0x14, 't' },
|
||||
{ 0x15, 'y' },
|
||||
{ 0x16, 'u' },
|
||||
{ 0x17, 'i' },
|
||||
{ 0x18, 'o' },
|
||||
{ 0x19, 'p' },
|
||||
{ 0x1A, '[' },
|
||||
{ 0x1B, ']' },
|
||||
{ 0x1C, '\n' },
|
||||
{ 0x1E, 'a' },
|
||||
{ 0x1F, 's' },
|
||||
{ 0x20, 'd' },
|
||||
{ 0x21, 'f' },
|
||||
{ 0x22, 'g' },
|
||||
{ 0x23, 'h' },
|
||||
{ 0x24, 'j' },
|
||||
{ 0x25, 'k' },
|
||||
{ 0x26, 'l' },
|
||||
{ 0x27, ';' },
|
||||
{ 0x28, '\'' },
|
||||
{ 0x29, '`' },
|
||||
{ 0x2B, '\\' },
|
||||
{ 0x2C, 'z' },
|
||||
{ 0x2D, 'x' },
|
||||
{ 0x2E, 'c' },
|
||||
{ 0x2F, 'v' },
|
||||
{ 0x30, 'b' },
|
||||
{ 0x31, 'n' },
|
||||
{ 0x32, 'm' },
|
||||
{ 0x33, ',' },
|
||||
{ 0x34, '.' },
|
||||
{ 0x35, '/' },
|
||||
{ 0x39, ' ' },
|
||||
{ 0xFFFF, 0 },
|
||||
};
|
||||
|
||||
static const struct {
|
||||
uintptr_t scancode;
|
||||
uiExtKey equiv;
|
||||
} scancodeExtKeys[] = {
|
||||
{ 0x47, uiExtKeyN7 },
|
||||
{ 0x48, uiExtKeyN8 },
|
||||
{ 0x49, uiExtKeyN9 },
|
||||
{ 0x4B, uiExtKeyN4 },
|
||||
{ 0x4C, uiExtKeyN5 },
|
||||
{ 0x4D, uiExtKeyN6 },
|
||||
{ 0x4F, uiExtKeyN1 },
|
||||
{ 0x50, uiExtKeyN2 },
|
||||
{ 0x51, uiExtKeyN3 },
|
||||
{ 0x52, uiExtKeyN0 },
|
||||
{ 0x53, uiExtKeyNDot },
|
||||
{ 0xFFFF, 0 },
|
||||
};
|
||||
|
||||
int fromScancode(uintptr_t scancode, uiAreaKeyEvent *ke)
|
||||
{
|
||||
int i;
|
||||
|
||||
for (i = 0; scancodeKeys[i].scancode != 0xFFFF; i++)
|
||||
if (scancodeKeys[i].scancode == scancode) {
|
||||
ke->Key = scancodeKeys[i].equiv;
|
||||
return 1;
|
||||
}
|
||||
for (i = 0; scancodeExtKeys[i].scancode != 0xFFFF; i++)
|
||||
if (scancodeExtKeys[i].scancode == scancode) {
|
||||
ke->ExtKey = scancodeExtKeys[i].equiv;
|
||||
return 1;
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
Vendored
+101
@@ -0,0 +1,101 @@
|
||||
// 26 may 2015
|
||||
#include "../ui.h"
|
||||
#include "uipriv.h"
|
||||
|
||||
void uiControlDestroy(uiControl *c)
|
||||
{
|
||||
(*(c->Destroy))(c);
|
||||
}
|
||||
|
||||
uintptr_t uiControlHandle(uiControl *c)
|
||||
{
|
||||
return (*(c->Handle))(c);
|
||||
}
|
||||
|
||||
uiControl *uiControlParent(uiControl *c)
|
||||
{
|
||||
return (*(c->Parent))(c);
|
||||
}
|
||||
|
||||
void uiControlSetParent(uiControl *c, uiControl *parent)
|
||||
{
|
||||
(*(c->SetParent))(c, parent);
|
||||
}
|
||||
|
||||
int uiControlToplevel(uiControl *c)
|
||||
{
|
||||
return (*(c->Toplevel))(c);
|
||||
}
|
||||
|
||||
int uiControlVisible(uiControl *c)
|
||||
{
|
||||
return (*(c->Visible))(c);
|
||||
}
|
||||
|
||||
void uiControlShow(uiControl *c)
|
||||
{
|
||||
(*(c->Show))(c);
|
||||
}
|
||||
|
||||
void uiControlHide(uiControl *c)
|
||||
{
|
||||
(*(c->Hide))(c);
|
||||
}
|
||||
|
||||
int uiControlEnabled(uiControl *c)
|
||||
{
|
||||
return (*(c->Enabled))(c);
|
||||
}
|
||||
|
||||
void uiControlEnable(uiControl *c)
|
||||
{
|
||||
(*(c->Enable))(c);
|
||||
}
|
||||
|
||||
void uiControlDisable(uiControl *c)
|
||||
{
|
||||
(*(c->Disable))(c);
|
||||
}
|
||||
|
||||
#define uiControlSignature 0x7569436F
|
||||
|
||||
uiControl *uiAllocControl(size_t size, uint32_t OSsig, uint32_t typesig, const char *typenamestr)
|
||||
{
|
||||
uiControl *c;
|
||||
|
||||
c = (uiControl *) uiAlloc(size, typenamestr);
|
||||
c->Signature = uiControlSignature;
|
||||
c->OSSignature = OSsig;
|
||||
c->TypeSignature = typesig;
|
||||
return c;
|
||||
}
|
||||
|
||||
void uiFreeControl(uiControl *c)
|
||||
{
|
||||
if (uiControlParent(c) != NULL)
|
||||
userbug("You cannot destroy a uiControl while it still has a parent. (control: %p)", c);
|
||||
uiFree(c);
|
||||
}
|
||||
|
||||
void uiControlVerifySetParent(uiControl *c, uiControl *parent)
|
||||
{
|
||||
uiControl *curParent;
|
||||
|
||||
if (uiControlToplevel(c))
|
||||
userbug("You cannot give a toplevel uiControl a parent. (control: %p)", c);
|
||||
curParent = uiControlParent(c);
|
||||
if (parent != NULL && curParent != NULL)
|
||||
userbug("You cannot give a uiControl a parent while it already has one. (control: %p; current parent: %p; new parent: %p)", c, curParent, parent);
|
||||
if (parent == NULL && curParent == NULL)
|
||||
implbug("attempt to double unparent uiControl %p", c);
|
||||
}
|
||||
|
||||
int uiControlEnabledToUser(uiControl *c)
|
||||
{
|
||||
while (c != NULL) {
|
||||
if (!uiControlEnabled(c))
|
||||
return 0;
|
||||
c = uiControlParent(c);
|
||||
}
|
||||
return 1;
|
||||
}
|
||||
Vendored
+25
@@ -0,0 +1,25 @@
|
||||
// 24 april 2016
|
||||
|
||||
#define uiAreaSignature 0x41726561
|
||||
#define uiBoxSignature 0x426F784C
|
||||
#define uiButtonSignature 0x42746F6E
|
||||
#define uiCheckboxSignature 0x43686B62
|
||||
#define uiColorButtonSignature 0x436F6C42
|
||||
#define uiComboboxSignature 0x436F6D62
|
||||
#define uiDateTimePickerSignature 0x44545069
|
||||
#define uiEditableComboboxSignature 0x45644362
|
||||
#define uiEntrySignature 0x456E7472
|
||||
#define uiFontButtonSignature 0x466F6E42
|
||||
#define uiFormSignature 0x466F726D
|
||||
#define uiGridSignature 0x47726964
|
||||
#define uiGroupSignature 0x47727062
|
||||
#define uiLabelSignature 0x4C61626C
|
||||
#define uiMultilineEntrySignature 0x4D6C6E45
|
||||
#define uiProgressBarSignature 0x50426172
|
||||
#define uiRadioButtonsSignature 0x5264696F
|
||||
#define uiSeparatorSignature 0x53657061
|
||||
#define uiSliderSignature 0x536C6964
|
||||
#define uiSpinboxSignature 0x5370696E
|
||||
#define uiTabSignature 0x54616273
|
||||
#define uiTableSignature 0x5461626C
|
||||
#define uiWindowSignature 0x57696E64
|
||||
Vendored
+21
@@ -0,0 +1,21 @@
|
||||
// 13 may 2016
|
||||
#include "../ui.h"
|
||||
#include "uipriv.h"
|
||||
|
||||
void _implbug(const char *file, const char *line, const char *func, const char *format, ...)
|
||||
{
|
||||
va_list ap;
|
||||
|
||||
va_start(ap, format);
|
||||
realbug(file, line, func, "POSSIBLE IMPLEMENTATION BUG; CONTACT ANDLABS:\n", format, ap);
|
||||
va_end(ap);
|
||||
}
|
||||
|
||||
void _userbug(const char *file, const char *line, const char *func, const char *format, ...)
|
||||
{
|
||||
va_list ap;
|
||||
|
||||
va_start(ap, format);
|
||||
realbug(file, line, func, "You have a bug: ", format, ap);
|
||||
va_end(ap);
|
||||
}
|
||||
Vendored
+50
@@ -0,0 +1,50 @@
|
||||
// 11 october 2015
|
||||
#include <math.h>
|
||||
#include "../ui.h"
|
||||
#include "uipriv.h"
|
||||
|
||||
void uiDrawMatrixSetIdentity(uiDrawMatrix *m)
|
||||
{
|
||||
m->M11 = 1;
|
||||
m->M12 = 0;
|
||||
m->M21 = 0;
|
||||
m->M22 = 1;
|
||||
m->M31 = 0;
|
||||
m->M32 = 0;
|
||||
}
|
||||
|
||||
// The rest of this file provides basic utilities in case the platform doesn't provide any of its own for these tasks.
|
||||
// Keep these as minimal as possible. They should generally not call other fallbacks.
|
||||
|
||||
// see https://msdn.microsoft.com/en-us/library/windows/desktop/ff684171%28v=vs.85%29.aspx#skew_transform
|
||||
// TODO see if there's a way we can avoid the multiplication
|
||||
void fallbackSkew(uiDrawMatrix *m, double x, double y, double xamount, double yamount)
|
||||
{
|
||||
uiDrawMatrix n;
|
||||
|
||||
uiDrawMatrixSetIdentity(&n);
|
||||
// TODO explain this
|
||||
n.M12 = tan(yamount);
|
||||
n.M21 = tan(xamount);
|
||||
n.M31 = -y * tan(xamount);
|
||||
n.M32 = -x * tan(yamount);
|
||||
uiDrawMatrixMultiply(m, &n);
|
||||
}
|
||||
|
||||
void scaleCenter(double xCenter, double yCenter, double *x, double *y)
|
||||
{
|
||||
*x = xCenter - (*x * xCenter);
|
||||
*y = yCenter - (*y * yCenter);
|
||||
}
|
||||
|
||||
// the basic algorithm is from cairo
|
||||
// but it's the same algorithm as the transform point, just without M31 and M32 taken into account, so let's just do that instead
|
||||
void fallbackTransformSize(uiDrawMatrix *m, double *x, double *y)
|
||||
{
|
||||
uiDrawMatrix m2;
|
||||
|
||||
m2 = *m;
|
||||
m2.M31 = 0;
|
||||
m2.M32 = 0;
|
||||
uiDrawMatrixTransformPoint(&m2, x, y);
|
||||
}
|
||||
Vendored
+22
@@ -0,0 +1,22 @@
|
||||
// 9 may 2015
|
||||
#include "../ui.h"
|
||||
#include "uipriv.h"
|
||||
|
||||
static int defaultOnShouldQuit(void *data)
|
||||
{
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int (*onShouldQuit)(void *) = defaultOnShouldQuit;
|
||||
static void *onShouldQuitData;
|
||||
|
||||
void uiOnShouldQuit(int (*f)(void *), void *data)
|
||||
{
|
||||
onShouldQuit = f;
|
||||
onShouldQuitData = data;
|
||||
}
|
||||
|
||||
int shouldQuit(void)
|
||||
{
|
||||
return (*onShouldQuit)(onShouldQuitData);
|
||||
}
|
||||
Vendored
+58
@@ -0,0 +1,58 @@
|
||||
// 6 april 2015
|
||||
#ifdef __cplusplus
|
||||
extern "C" {
|
||||
#endif
|
||||
|
||||
#include <stdarg.h>
|
||||
#include "controlsigs.h"
|
||||
|
||||
extern uiInitOptions options;
|
||||
|
||||
extern void *uiAlloc(size_t, const char *);
|
||||
#define uiNew(T) ((T *) uiAlloc(sizeof (T), #T))
|
||||
extern void *uiRealloc(void *, size_t, const char *);
|
||||
extern void uiFree(void *);
|
||||
|
||||
// ugh, this was only introduced in MSVC 2015...
|
||||
#ifdef _MSC_VER
|
||||
#define __func__ __FUNCTION__
|
||||
#endif
|
||||
extern void realbug(const char *file, const char *line, const char *func, const char *prefix, const char *format, va_list ap);
|
||||
#define _ns2(s) #s
|
||||
#define _ns(s) _ns2(s)
|
||||
extern void _implbug(const char *file, const char *line, const char *func, const char *format, ...);
|
||||
#define implbug(...) _implbug(__FILE__, _ns(__LINE__), __func__, __VA_ARGS__)
|
||||
extern void _userbug(const char *file, const char *line, const char *func, const char *format, ...);
|
||||
#define userbug(...) _userbug(__FILE__, _ns(__LINE__), __func__, __VA_ARGS__)
|
||||
|
||||
// control.c
|
||||
extern uiControl *newControl(size_t size, uint32_t OSsig, uint32_t typesig, const char *typenamestr);
|
||||
|
||||
// shouldquit.c
|
||||
extern int shouldQuit(void);
|
||||
|
||||
// areaevents.c
|
||||
typedef struct clickCounter clickCounter;
|
||||
// you should call Reset() to zero-initialize a new instance
|
||||
// it doesn't matter that all the non-count fields are zero: the first click will fail the curButton test straightaway, so it'll return 1 and set the rest of the structure accordingly
|
||||
struct clickCounter {
|
||||
int curButton;
|
||||
int rectX0;
|
||||
int rectY0;
|
||||
int rectX1;
|
||||
int rectY1;
|
||||
uintptr_t prevTime;
|
||||
int count;
|
||||
};
|
||||
int clickCounterClick(clickCounter *c, int button, int x, int y, uintptr_t time, uintptr_t maxTime, int32_t xdist, int32_t ydist);
|
||||
extern void clickCounterReset(clickCounter *);
|
||||
extern int fromScancode(uintptr_t, uiAreaKeyEvent *);
|
||||
|
||||
// matrix.c
|
||||
extern void fallbackSkew(uiDrawMatrix *, double, double, double, double);
|
||||
extern void scaleCenter(double, double, double *, double *);
|
||||
extern void fallbackTransformSize(uiDrawMatrix *, double *, double *);
|
||||
|
||||
#ifdef __cplusplus
|
||||
}
|
||||
#endif
|
||||
Vendored
+8
@@ -0,0 +1,8 @@
|
||||
// 22 may 2016
|
||||
#include "../ui.h"
|
||||
#include "uipriv.h"
|
||||
|
||||
void uiUserBugCannotSetParentOnToplevel(const char *type)
|
||||
{
|
||||
userbug("You cannot make a %s a child of another uiControl,", type);
|
||||
}
|
||||
Vendored
+79
@@ -0,0 +1,79 @@
|
||||
# 3 june 2016
|
||||
|
||||
list(APPEND _LIBUI_SOURCES
|
||||
darwin/alloc.m
|
||||
darwin/area.m
|
||||
darwin/areaevents.m
|
||||
darwin/autolayout.m
|
||||
darwin/box.m
|
||||
darwin/button.m
|
||||
darwin/checkbox.m
|
||||
darwin/colorbutton.m
|
||||
darwin/combobox.m
|
||||
darwin/control.m
|
||||
darwin/datetimepicker.m
|
||||
darwin/debug.m
|
||||
darwin/draw.m
|
||||
darwin/drawtext.m
|
||||
darwin/editablecombo.m
|
||||
darwin/entry.m
|
||||
darwin/fontbutton.m
|
||||
darwin/form.m
|
||||
darwin/grid.m
|
||||
darwin/group.m
|
||||
darwin/image.m
|
||||
darwin/label.m
|
||||
darwin/main.m
|
||||
darwin/map.m
|
||||
darwin/menu.m
|
||||
darwin/multilineentry.m
|
||||
darwin/progressbar.m
|
||||
darwin/radiobuttons.m
|
||||
darwin/scrollview.m
|
||||
darwin/separator.m
|
||||
darwin/slider.m
|
||||
darwin/spinbox.m
|
||||
darwin/stddialogs.m
|
||||
darwin/tab.m
|
||||
darwin/text.m
|
||||
darwin/util.m
|
||||
darwin/window.m
|
||||
darwin/winmoveresize.m
|
||||
)
|
||||
set(_LIBUI_SOURCES ${_LIBUI_SOURCES} PARENT_SCOPE)
|
||||
|
||||
list(APPEND _LIBUI_INCLUDEDIRS
|
||||
darwin
|
||||
)
|
||||
set(_LIBUI_INCLUDEDIRS _LIBUI_INCLUDEDIRS PARENT_SCOPE)
|
||||
|
||||
set(_LIBUINAME libui PARENT_SCOPE)
|
||||
if(NOT BUILD_SHARED_LIBS)
|
||||
set(_LIBUINAME libui-temporary PARENT_SCOPE)
|
||||
endif()
|
||||
# thanks to Mr-Hide in irc.freenode.net/#cmake
|
||||
macro(_handle_static)
|
||||
set_target_properties(${_LIBUINAME} PROPERTIES
|
||||
ARCHIVE_OUTPUT_DIRECTORY "${PROJECT_BINARY_DIR}")
|
||||
set(_aname $<TARGET_FILE:${_LIBUINAME}>)
|
||||
set(_lname libui-combined.list)
|
||||
set(_oname libui-combined.o)
|
||||
add_custom_command(
|
||||
OUTPUT ${_oname}
|
||||
COMMAND
|
||||
nm -m ${_aname} | sed -E -n "'s/^[0-9a-f]* \\([A-Z_]+,[a-z_]+\\) external //p'" > ${_lname}
|
||||
COMMAND
|
||||
ld -exported_symbols_list ${_lname} -r -all_load ${_aname} -o ${_oname}
|
||||
COMMENT "Removing hidden symbols")
|
||||
add_library(libui STATIC ${_oname})
|
||||
# otherwise cmake won't know which linker to use
|
||||
set_target_properties(libui PROPERTIES
|
||||
LINKER_LANGUAGE C)
|
||||
set(_aname)
|
||||
set(_lname)
|
||||
set(_oname)
|
||||
endmacro()
|
||||
|
||||
set(_LIBUI_LIBS
|
||||
objc "-framework Foundation" "-framework AppKit"
|
||||
PARENT_SCOPE)
|
||||
Vendored
+89
@@ -0,0 +1,89 @@
|
||||
// 4 december 2014
|
||||
#import <stdlib.h>
|
||||
#import "uipriv_darwin.h"
|
||||
|
||||
static NSMutableArray *allocations;
|
||||
NSMutableArray *delegates;
|
||||
|
||||
void initAlloc(void)
|
||||
{
|
||||
allocations = [NSMutableArray new];
|
||||
delegates = [NSMutableArray new];
|
||||
}
|
||||
|
||||
#define UINT8(p) ((uint8_t *) (p))
|
||||
#define PVOID(p) ((void *) (p))
|
||||
#define EXTRA (sizeof (size_t) + sizeof (const char **))
|
||||
#define DATA(p) PVOID(UINT8(p) + EXTRA)
|
||||
#define BASE(p) PVOID(UINT8(p) - EXTRA)
|
||||
#define SIZE(p) ((size_t *) (p))
|
||||
#define CCHAR(p) ((const char **) (p))
|
||||
#define TYPE(p) CCHAR(UINT8(p) + sizeof (size_t))
|
||||
|
||||
void uninitAlloc(void)
|
||||
{
|
||||
NSMutableString *str;
|
||||
NSValue *v;
|
||||
|
||||
[delegates release];
|
||||
if ([allocations count] == 0) {
|
||||
[allocations release];
|
||||
return;
|
||||
}
|
||||
str = [NSMutableString new];
|
||||
for (v in allocations) {
|
||||
void *ptr;
|
||||
|
||||
ptr = [v pointerValue];
|
||||
[str appendString:[NSString stringWithFormat:@"%p %s\n", ptr, *TYPE(ptr)]];
|
||||
}
|
||||
userbug("Some data was leaked; either you left a uiControl lying around or there's a bug in libui itself. Leaked data:\n%s", [str UTF8String]);
|
||||
[str release];
|
||||
}
|
||||
|
||||
void *uiAlloc(size_t size, const char *type)
|
||||
{
|
||||
void *out;
|
||||
|
||||
out = malloc(EXTRA + size);
|
||||
if (out == NULL) {
|
||||
fprintf(stderr, "memory exhausted in uiAlloc()\n");
|
||||
abort();
|
||||
}
|
||||
memset(DATA(out), 0, size);
|
||||
*SIZE(out) = size;
|
||||
*TYPE(out) = type;
|
||||
[allocations addObject:[NSValue valueWithPointer:out]];
|
||||
return DATA(out);
|
||||
}
|
||||
|
||||
void *uiRealloc(void *p, size_t new, const char *type)
|
||||
{
|
||||
void *out;
|
||||
size_t *s;
|
||||
|
||||
if (p == NULL)
|
||||
return uiAlloc(new, type);
|
||||
p = BASE(p);
|
||||
out = realloc(p, EXTRA + new);
|
||||
if (out == NULL) {
|
||||
fprintf(stderr, "memory exhausted in uiRealloc()\n");
|
||||
abort();
|
||||
}
|
||||
s = SIZE(out);
|
||||
if (new <= *s)
|
||||
memset(((uint8_t *) DATA(out)) + *s, 0, new - *s);
|
||||
*s = new;
|
||||
[allocations removeObject:[NSValue valueWithPointer:p]];
|
||||
[allocations addObject:[NSValue valueWithPointer:out]];
|
||||
return DATA(out);
|
||||
}
|
||||
|
||||
void uiFree(void *p)
|
||||
{
|
||||
if (p == NULL)
|
||||
implbug("attempt to uiFree(NULL)");
|
||||
p = BASE(p);
|
||||
free(p);
|
||||
[allocations removeObject:[NSValue valueWithPointer:p]];
|
||||
}
|
||||
Vendored
+475
@@ -0,0 +1,475 @@
|
||||
// 9 september 2015
|
||||
#import "uipriv_darwin.h"
|
||||
|
||||
// 10.8 fixups
|
||||
#define NSEventModifierFlags NSUInteger
|
||||
|
||||
@interface areaView : NSView {
|
||||
uiArea *libui_a;
|
||||
NSTrackingArea *libui_ta;
|
||||
NSSize libui_ss;
|
||||
BOOL libui_enabled;
|
||||
}
|
||||
- (id)initWithFrame:(NSRect)r area:(uiArea *)a;
|
||||
- (uiModifiers)parseModifiers:(NSEvent *)e;
|
||||
- (void)doMouseEvent:(NSEvent *)e;
|
||||
- (int)sendKeyEvent:(uiAreaKeyEvent *)ke;
|
||||
- (int)doKeyDownUp:(NSEvent *)e up:(int)up;
|
||||
- (int)doKeyDown:(NSEvent *)e;
|
||||
- (int)doKeyUp:(NSEvent *)e;
|
||||
- (int)doFlagsChanged:(NSEvent *)e;
|
||||
- (void)setupNewTrackingArea;
|
||||
- (void)setScrollingSize:(NSSize)s;
|
||||
- (BOOL)isEnabled;
|
||||
- (void)setEnabled:(BOOL)e;
|
||||
@end
|
||||
|
||||
struct uiArea {
|
||||
uiDarwinControl c;
|
||||
NSView *view; // either sv or area depending on whether it is scrolling
|
||||
NSScrollView *sv;
|
||||
areaView *area;
|
||||
struct scrollViewData *d;
|
||||
uiAreaHandler *ah;
|
||||
BOOL scrolling;
|
||||
NSEvent *dragevent;
|
||||
};
|
||||
|
||||
@implementation areaView
|
||||
|
||||
- (id)initWithFrame:(NSRect)r area:(uiArea *)a
|
||||
{
|
||||
self = [super initWithFrame:r];
|
||||
if (self) {
|
||||
self->libui_a = a;
|
||||
[self setupNewTrackingArea];
|
||||
self->libui_ss = r.size;
|
||||
self->libui_enabled = YES;
|
||||
}
|
||||
return self;
|
||||
}
|
||||
|
||||
- (void)drawRect:(NSRect)r
|
||||
{
|
||||
uiArea *a = self->libui_a;
|
||||
CGContextRef c;
|
||||
uiAreaDrawParams dp;
|
||||
|
||||
c = (CGContextRef) [[NSGraphicsContext currentContext] graphicsPort];
|
||||
// see draw.m under text for why we need the height
|
||||
dp.Context = newContext(c, [self bounds].size.height);
|
||||
|
||||
dp.AreaWidth = 0;
|
||||
dp.AreaHeight = 0;
|
||||
if (!a->scrolling) {
|
||||
dp.AreaWidth = [self frame].size.width;
|
||||
dp.AreaHeight = [self frame].size.height;
|
||||
}
|
||||
|
||||
dp.ClipX = r.origin.x;
|
||||
dp.ClipY = r.origin.y;
|
||||
dp.ClipWidth = r.size.width;
|
||||
dp.ClipHeight = r.size.height;
|
||||
|
||||
// no need to save or restore the graphics state to reset transformations; Cocoa creates a brand-new context each time
|
||||
(*(a->ah->Draw))(a->ah, a, &dp);
|
||||
|
||||
freeContext(dp.Context);
|
||||
}
|
||||
|
||||
- (BOOL)isFlipped
|
||||
{
|
||||
return YES;
|
||||
}
|
||||
|
||||
- (BOOL)acceptsFirstResponder
|
||||
{
|
||||
return YES;
|
||||
}
|
||||
|
||||
- (uiModifiers)parseModifiers:(NSEvent *)e
|
||||
{
|
||||
NSEventModifierFlags mods;
|
||||
uiModifiers m;
|
||||
|
||||
m = 0;
|
||||
mods = [e modifierFlags];
|
||||
if ((mods & NSControlKeyMask) != 0)
|
||||
m |= uiModifierCtrl;
|
||||
if ((mods & NSAlternateKeyMask) != 0)
|
||||
m |= uiModifierAlt;
|
||||
if ((mods & NSShiftKeyMask) != 0)
|
||||
m |= uiModifierShift;
|
||||
if ((mods & NSCommandKeyMask) != 0)
|
||||
m |= uiModifierSuper;
|
||||
return m;
|
||||
}
|
||||
|
||||
- (void)setupNewTrackingArea
|
||||
{
|
||||
self->libui_ta = [[NSTrackingArea alloc] initWithRect:[self bounds]
|
||||
options:(NSTrackingMouseEnteredAndExited |
|
||||
NSTrackingMouseMoved |
|
||||
NSTrackingActiveAlways |
|
||||
NSTrackingInVisibleRect |
|
||||
NSTrackingEnabledDuringMouseDrag)
|
||||
owner:self
|
||||
userInfo:nil];
|
||||
[self addTrackingArea:self->libui_ta];
|
||||
}
|
||||
|
||||
- (void)updateTrackingAreas
|
||||
{
|
||||
[self removeTrackingArea:self->libui_ta];
|
||||
[self->libui_ta release];
|
||||
[self setupNewTrackingArea];
|
||||
}
|
||||
|
||||
// capture on drag is done automatically on OS X
|
||||
- (void)doMouseEvent:(NSEvent *)e
|
||||
{
|
||||
uiArea *a = self->libui_a;
|
||||
uiAreaMouseEvent me;
|
||||
NSPoint point;
|
||||
int buttonNumber;
|
||||
NSUInteger pmb;
|
||||
unsigned int i, max;
|
||||
|
||||
// this will convert point to drawing space
|
||||
// thanks swillits in irc.freenode.net/#macdev
|
||||
point = [self convertPoint:[e locationInWindow] fromView:nil];
|
||||
me.X = point.x;
|
||||
me.Y = point.y;
|
||||
|
||||
me.AreaWidth = 0;
|
||||
me.AreaHeight = 0;
|
||||
if (!a->scrolling) {
|
||||
me.AreaWidth = [self frame].size.width;
|
||||
me.AreaHeight = [self frame].size.height;
|
||||
}
|
||||
|
||||
buttonNumber = [e buttonNumber] + 1;
|
||||
// swap button numbers 2 and 3 (right and middle)
|
||||
if (buttonNumber == 2)
|
||||
buttonNumber = 3;
|
||||
else if (buttonNumber == 3)
|
||||
buttonNumber = 2;
|
||||
|
||||
me.Down = 0;
|
||||
me.Up = 0;
|
||||
me.Count = 0;
|
||||
switch ([e type]) {
|
||||
case NSLeftMouseDown:
|
||||
case NSRightMouseDown:
|
||||
case NSOtherMouseDown:
|
||||
me.Down = buttonNumber;
|
||||
me.Count = [e clickCount];
|
||||
break;
|
||||
case NSLeftMouseUp:
|
||||
case NSRightMouseUp:
|
||||
case NSOtherMouseUp:
|
||||
me.Up = buttonNumber;
|
||||
break;
|
||||
case NSLeftMouseDragged:
|
||||
case NSRightMouseDragged:
|
||||
case NSOtherMouseDragged:
|
||||
// we include the button that triggered the dragged event in the Held fields
|
||||
buttonNumber = 0;
|
||||
break;
|
||||
}
|
||||
|
||||
me.Modifiers = [self parseModifiers:e];
|
||||
|
||||
pmb = [NSEvent pressedMouseButtons];
|
||||
me.Held1To64 = 0;
|
||||
if (buttonNumber != 1 && (pmb & 1) != 0)
|
||||
me.Held1To64 |= 1;
|
||||
if (buttonNumber != 2 && (pmb & 4) != 0)
|
||||
me.Held1To64 |= 2;
|
||||
if (buttonNumber != 3 && (pmb & 2) != 0)
|
||||
me.Held1To64 |= 4;
|
||||
// buttons 4..32
|
||||
// https://developer.apple.com/library/mac/documentation/Carbon/Reference/QuartzEventServicesRef/index.html#//apple_ref/c/tdef/CGMouseButton says Quartz only supports up to 32 buttons
|
||||
max = 32;
|
||||
for (i = 4; i <= max; i++) {
|
||||
uint64_t j;
|
||||
|
||||
if (buttonNumber == i)
|
||||
continue;
|
||||
j = 1 << (i - 1);
|
||||
if ((pmb & j) != 0)
|
||||
me.Held1To64 |= j;
|
||||
}
|
||||
|
||||
if (self->libui_enabled) {
|
||||
// and allow dragging here
|
||||
a->dragevent = e;
|
||||
(*(a->ah->MouseEvent))(a->ah, a, &me);
|
||||
a->dragevent = nil;
|
||||
}
|
||||
}
|
||||
|
||||
#define mouseEvent(name) \
|
||||
- (void)name:(NSEvent *)e \
|
||||
{ \
|
||||
[self doMouseEvent:e]; \
|
||||
}
|
||||
mouseEvent(mouseMoved)
|
||||
mouseEvent(mouseDragged)
|
||||
mouseEvent(rightMouseDragged)
|
||||
mouseEvent(otherMouseDragged)
|
||||
mouseEvent(mouseDown)
|
||||
mouseEvent(rightMouseDown)
|
||||
mouseEvent(otherMouseDown)
|
||||
mouseEvent(mouseUp)
|
||||
mouseEvent(rightMouseUp)
|
||||
mouseEvent(otherMouseUp)
|
||||
|
||||
- (void)mouseEntered:(NSEvent *)e
|
||||
{
|
||||
uiArea *a = self->libui_a;
|
||||
|
||||
if (self->libui_enabled)
|
||||
(*(a->ah->MouseCrossed))(a->ah, a, 0);
|
||||
}
|
||||
|
||||
- (void)mouseExited:(NSEvent *)e
|
||||
{
|
||||
uiArea *a = self->libui_a;
|
||||
|
||||
if (self->libui_enabled)
|
||||
(*(a->ah->MouseCrossed))(a->ah, a, 1);
|
||||
}
|
||||
|
||||
// note: there is no equivalent to WM_CAPTURECHANGED on Mac OS X; there literally is no way to break a grab like that
|
||||
// even if I invoke the task switcher and switch processes, the mouse grab will still be held until I let go of all buttons
|
||||
// therefore, no DragBroken()
|
||||
|
||||
- (int)sendKeyEvent:(uiAreaKeyEvent *)ke
|
||||
{
|
||||
uiArea *a = self->libui_a;
|
||||
|
||||
return (*(a->ah->KeyEvent))(a->ah, a, ke);
|
||||
}
|
||||
|
||||
- (int)doKeyDownUp:(NSEvent *)e up:(int)up
|
||||
{
|
||||
uiAreaKeyEvent ke;
|
||||
|
||||
ke.Key = 0;
|
||||
ke.ExtKey = 0;
|
||||
ke.Modifier = 0;
|
||||
|
||||
ke.Modifiers = [self parseModifiers:e];
|
||||
|
||||
ke.Up = up;
|
||||
|
||||
if (!fromKeycode([e keyCode], &ke))
|
||||
return 0;
|
||||
return [self sendKeyEvent:&ke];
|
||||
}
|
||||
|
||||
- (int)doKeyDown:(NSEvent *)e
|
||||
{
|
||||
return [self doKeyDownUp:e up:0];
|
||||
}
|
||||
|
||||
- (int)doKeyUp:(NSEvent *)e
|
||||
{
|
||||
return [self doKeyDownUp:e up:1];
|
||||
}
|
||||
|
||||
- (int)doFlagsChanged:(NSEvent *)e
|
||||
{
|
||||
uiAreaKeyEvent ke;
|
||||
uiModifiers whichmod;
|
||||
|
||||
ke.Key = 0;
|
||||
ke.ExtKey = 0;
|
||||
|
||||
// Mac OS X sends this event on both key up and key down.
|
||||
// Fortunately -[e keyCode] IS valid here, so we can simply map from key code to Modifiers, get the value of [e modifierFlags], and check if the respective bit is set or not — that will give us the up/down state
|
||||
if (!keycodeModifier([e keyCode], &whichmod))
|
||||
return 0;
|
||||
ke.Modifier = whichmod;
|
||||
ke.Modifiers = [self parseModifiers:e];
|
||||
ke.Up = (ke.Modifiers & ke.Modifier) == 0;
|
||||
// and then drop the current modifier from Modifiers
|
||||
ke.Modifiers &= ~ke.Modifier;
|
||||
return [self sendKeyEvent:&ke];
|
||||
}
|
||||
|
||||
- (void)setFrameSize:(NSSize)size
|
||||
{
|
||||
uiArea *a = self->libui_a;
|
||||
|
||||
[super setFrameSize:size];
|
||||
if (!a->scrolling)
|
||||
// we must redraw everything on resize because Windows requires it
|
||||
[self setNeedsDisplay:YES];
|
||||
}
|
||||
|
||||
// TODO does this update the frame?
|
||||
- (void)setScrollingSize:(NSSize)s
|
||||
{
|
||||
self->libui_ss = s;
|
||||
[self invalidateIntrinsicContentSize];
|
||||
}
|
||||
|
||||
- (NSSize)intrinsicContentSize
|
||||
{
|
||||
if (!self->libui_a->scrolling)
|
||||
return [super intrinsicContentSize];
|
||||
return self->libui_ss;
|
||||
}
|
||||
|
||||
- (BOOL)becomeFirstResponder
|
||||
{
|
||||
return [self isEnabled];
|
||||
}
|
||||
|
||||
- (BOOL)isEnabled
|
||||
{
|
||||
return self->libui_enabled;
|
||||
}
|
||||
|
||||
- (void)setEnabled:(BOOL)e
|
||||
{
|
||||
self->libui_enabled = e;
|
||||
if (!self->libui_enabled && [self window] != nil)
|
||||
if ([[self window] firstResponder] == self)
|
||||
[[self window] makeFirstResponder:nil];
|
||||
}
|
||||
|
||||
@end
|
||||
|
||||
uiDarwinControlAllDefaultsExceptDestroy(uiArea, view)
|
||||
|
||||
static void uiAreaDestroy(uiControl *c)
|
||||
{
|
||||
uiArea *a = uiArea(c);
|
||||
|
||||
if (a->scrolling)
|
||||
scrollViewFreeData(a->sv, a->d);
|
||||
[a->area release];
|
||||
if (a->scrolling)
|
||||
[a->sv release];
|
||||
uiFreeControl(uiControl(a));
|
||||
}
|
||||
|
||||
// called by subclasses of -[NSApplication sendEvent:]
|
||||
// by default, NSApplication eats some key events
|
||||
// this prevents that from happening with uiArea
|
||||
// see http://stackoverflow.com/questions/24099063/how-do-i-detect-keyup-in-my-nsview-with-the-command-key-held and http://lists.apple.com/archives/cocoa-dev/2003/Oct/msg00442.html
|
||||
int sendAreaEvents(NSEvent *e)
|
||||
{
|
||||
NSEventType type;
|
||||
id focused;
|
||||
areaView *view;
|
||||
|
||||
type = [e type];
|
||||
if (type != NSKeyDown && type != NSKeyUp && type != NSFlagsChanged)
|
||||
return 0;
|
||||
focused = [[e window] firstResponder];
|
||||
if (focused == nil)
|
||||
return 0;
|
||||
if (![focused isKindOfClass:[areaView class]])
|
||||
return 0;
|
||||
view = (areaView *) focused;
|
||||
switch (type) {
|
||||
case NSKeyDown:
|
||||
return [view doKeyDown:e];
|
||||
case NSKeyUp:
|
||||
return [view doKeyUp:e];
|
||||
case NSFlagsChanged:
|
||||
return [view doFlagsChanged:e];
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
void uiAreaSetSize(uiArea *a, int width, int height)
|
||||
{
|
||||
if (!a->scrolling)
|
||||
userbug("You cannot call uiAreaSetSize() on a non-scrolling uiArea. (area: %p)", a);
|
||||
[a->area setScrollingSize:NSMakeSize(width, height)];
|
||||
}
|
||||
|
||||
void uiAreaQueueRedrawAll(uiArea *a)
|
||||
{
|
||||
[a->area setNeedsDisplay:YES];
|
||||
}
|
||||
|
||||
void uiAreaScrollTo(uiArea *a, double x, double y, double width, double height)
|
||||
{
|
||||
if (!a->scrolling)
|
||||
userbug("You cannot call uiAreaScrollTo() on a non-scrolling uiArea. (area: %p)", a);
|
||||
[a->area scrollRectToVisible:NSMakeRect(x, y, width, height)];
|
||||
// don't worry about the return value; it just says whether scrolling was needed
|
||||
}
|
||||
|
||||
void uiAreaBeginUserWindowMove(uiArea *a)
|
||||
{
|
||||
libuiNSWindow *w;
|
||||
|
||||
w = (libuiNSWindow *) [a->area window];
|
||||
if (w == nil)
|
||||
return; // TODO
|
||||
if (a->dragevent == nil)
|
||||
return; // TODO
|
||||
[w libui_doMove:a->dragevent];
|
||||
}
|
||||
|
||||
void uiAreaBeginUserWindowResize(uiArea *a, uiWindowResizeEdge edge)
|
||||
{
|
||||
libuiNSWindow *w;
|
||||
|
||||
w = (libuiNSWindow *) [a->area window];
|
||||
if (w == nil)
|
||||
return; // TODO
|
||||
if (a->dragevent == nil)
|
||||
return; // TODO
|
||||
[w libui_doResize:a->dragevent on:edge];
|
||||
}
|
||||
|
||||
uiArea *uiNewArea(uiAreaHandler *ah)
|
||||
{
|
||||
uiArea *a;
|
||||
|
||||
uiDarwinNewControl(uiArea, a);
|
||||
|
||||
a->ah = ah;
|
||||
a->scrolling = NO;
|
||||
|
||||
a->area = [[areaView alloc] initWithFrame:NSZeroRect area:a];
|
||||
|
||||
a->view = a->area;
|
||||
|
||||
return a;
|
||||
}
|
||||
|
||||
uiArea *uiNewScrollingArea(uiAreaHandler *ah, int width, int height)
|
||||
{
|
||||
uiArea *a;
|
||||
struct scrollViewCreateParams p;
|
||||
|
||||
uiDarwinNewControl(uiArea, a);
|
||||
|
||||
a->ah = ah;
|
||||
a->scrolling = YES;
|
||||
|
||||
a->area = [[areaView alloc] initWithFrame:NSMakeRect(0, 0, width, height)
|
||||
area:a];
|
||||
|
||||
memset(&p, 0, sizeof (struct scrollViewCreateParams));
|
||||
p.DocumentView = a->area;
|
||||
p.BackgroundColor = [NSColor controlColor];
|
||||
p.DrawsBackground = 1;
|
||||
p.Bordered = NO;
|
||||
p.HScroll = YES;
|
||||
p.VScroll = YES;
|
||||
a->sv = mkScrollView(&p, &(a->d));
|
||||
|
||||
a->view = a->sv;
|
||||
|
||||
return a;
|
||||
}
|
||||
Vendored
+159
@@ -0,0 +1,159 @@
|
||||
// 30 march 2014
|
||||
#import "uipriv_darwin.h"
|
||||
|
||||
/*
|
||||
Mac OS X uses its own set of hardware key codes that are different from PC keyboard scancodes, but are positional (like PC keyboard scancodes). These are defined in <HIToolbox/Events.h>, a Carbon header. As far as I can tell, there's no way to include this header without either using an absolute path or linking Carbon into the program, so the constant values are used here instead.
|
||||
|
||||
The Cocoa docs do guarantee that -[NSEvent keyCode] results in key codes that are the same as those returned by Carbon; that is, these codes.
|
||||
*/
|
||||
|
||||
// use uintptr_t to be safe
|
||||
static const struct {
|
||||
uintptr_t keycode;
|
||||
char equiv;
|
||||
} keycodeKeys[] = {
|
||||
{ 0x00, 'a' },
|
||||
{ 0x01, 's' },
|
||||
{ 0x02, 'd' },
|
||||
{ 0x03, 'f' },
|
||||
{ 0x04, 'h' },
|
||||
{ 0x05, 'g' },
|
||||
{ 0x06, 'z' },
|
||||
{ 0x07, 'x' },
|
||||
{ 0x08, 'c' },
|
||||
{ 0x09, 'v' },
|
||||
{ 0x0B, 'b' },
|
||||
{ 0x0C, 'q' },
|
||||
{ 0x0D, 'w' },
|
||||
{ 0x0E, 'e' },
|
||||
{ 0x0F, 'r' },
|
||||
{ 0x10, 'y' },
|
||||
{ 0x11, 't' },
|
||||
{ 0x12, '1' },
|
||||
{ 0x13, '2' },
|
||||
{ 0x14, '3' },
|
||||
{ 0x15, '4' },
|
||||
{ 0x16, '6' },
|
||||
{ 0x17, '5' },
|
||||
{ 0x18, '=' },
|
||||
{ 0x19, '9' },
|
||||
{ 0x1A, '7' },
|
||||
{ 0x1B, '-' },
|
||||
{ 0x1C, '8' },
|
||||
{ 0x1D, '0' },
|
||||
{ 0x1E, ']' },
|
||||
{ 0x1F, 'o' },
|
||||
{ 0x20, 'u' },
|
||||
{ 0x21, '[' },
|
||||
{ 0x22, 'i' },
|
||||
{ 0x23, 'p' },
|
||||
{ 0x25, 'l' },
|
||||
{ 0x26, 'j' },
|
||||
{ 0x27, '\'' },
|
||||
{ 0x28, 'k' },
|
||||
{ 0x29, ';' },
|
||||
{ 0x2A, '\\' },
|
||||
{ 0x2B, ',' },
|
||||
{ 0x2C, '/' },
|
||||
{ 0x2D, 'n' },
|
||||
{ 0x2E, 'm' },
|
||||
{ 0x2F, '.' },
|
||||
{ 0x32, '`' },
|
||||
{ 0x24, '\n' },
|
||||
{ 0x30, '\t' },
|
||||
{ 0x31, ' ' },
|
||||
{ 0x33, '\b' },
|
||||
{ 0xFFFF, 0 },
|
||||
};
|
||||
|
||||
static const struct {
|
||||
uintptr_t keycode;
|
||||
uiExtKey equiv;
|
||||
} keycodeExtKeys[] = {
|
||||
{ 0x41, uiExtKeyNDot },
|
||||
{ 0x43, uiExtKeyNMultiply },
|
||||
{ 0x45, uiExtKeyNAdd },
|
||||
{ 0x4B, uiExtKeyNDivide },
|
||||
{ 0x4C, uiExtKeyNEnter },
|
||||
{ 0x4E, uiExtKeyNSubtract },
|
||||
{ 0x52, uiExtKeyN0 },
|
||||
{ 0x53, uiExtKeyN1 },
|
||||
{ 0x54, uiExtKeyN2 },
|
||||
{ 0x55, uiExtKeyN3 },
|
||||
{ 0x56, uiExtKeyN4 },
|
||||
{ 0x57, uiExtKeyN5 },
|
||||
{ 0x58, uiExtKeyN6 },
|
||||
{ 0x59, uiExtKeyN7 },
|
||||
{ 0x5B, uiExtKeyN8 },
|
||||
{ 0x5C, uiExtKeyN9 },
|
||||
{ 0x35, uiExtKeyEscape },
|
||||
{ 0x60, uiExtKeyF5 },
|
||||
{ 0x61, uiExtKeyF6 },
|
||||
{ 0x62, uiExtKeyF7 },
|
||||
{ 0x63, uiExtKeyF3 },
|
||||
{ 0x64, uiExtKeyF8 },
|
||||
{ 0x65, uiExtKeyF9 },
|
||||
{ 0x67, uiExtKeyF11 },
|
||||
{ 0x6D, uiExtKeyF10 },
|
||||
{ 0x6F, uiExtKeyF12 },
|
||||
{ 0x72, uiExtKeyInsert }, // listed as the Help key but it's in the same position on an Apple keyboard as the Insert key on a Windows keyboard; thanks to SeanieB from irc.badnik.net and Psy in irc.freenode.net/#macdev for confirming they have the same code
|
||||
{ 0x73, uiExtKeyHome },
|
||||
{ 0x74, uiExtKeyPageUp },
|
||||
{ 0x75, uiExtKeyDelete },
|
||||
{ 0x76, uiExtKeyF4 },
|
||||
{ 0x77, uiExtKeyEnd },
|
||||
{ 0x78, uiExtKeyF2 },
|
||||
{ 0x79, uiExtKeyPageDown },
|
||||
{ 0x7A, uiExtKeyF1 },
|
||||
{ 0x7B, uiExtKeyLeft },
|
||||
{ 0x7C, uiExtKeyRight },
|
||||
{ 0x7D, uiExtKeyDown },
|
||||
{ 0x7E, uiExtKeyUp },
|
||||
{ 0xFFFF, 0 },
|
||||
};
|
||||
|
||||
static const struct {
|
||||
uintptr_t keycode;
|
||||
uiModifiers equiv;
|
||||
} keycodeModifiers[] = {
|
||||
{ 0x37, uiModifierSuper }, // left command
|
||||
{ 0x38, uiModifierShift }, // left shift
|
||||
{ 0x3A, uiModifierAlt }, // left option
|
||||
{ 0x3B, uiModifierCtrl }, // left control
|
||||
{ 0x3C, uiModifierShift }, // right shift
|
||||
{ 0x3D, uiModifierAlt }, // right alt
|
||||
{ 0x3E, uiModifierCtrl }, // right control
|
||||
// the following is not in Events.h for some reason
|
||||
// thanks to Nicole and jedivulcan from irc.badnik.net
|
||||
{ 0x36, uiModifierSuper }, // right command
|
||||
{ 0xFFFF, 0 },
|
||||
};
|
||||
|
||||
BOOL fromKeycode(unsigned short keycode, uiAreaKeyEvent *ke)
|
||||
{
|
||||
int i;
|
||||
|
||||
for (i = 0; keycodeKeys[i].keycode != 0xFFFF; i++)
|
||||
if (keycodeKeys[i].keycode == keycode) {
|
||||
ke->Key = keycodeKeys[i].equiv;
|
||||
return YES;
|
||||
}
|
||||
for (i = 0; keycodeExtKeys[i].keycode != 0xFFFF; i++)
|
||||
if (keycodeExtKeys[i].keycode == keycode) {
|
||||
ke->ExtKey = keycodeExtKeys[i].equiv;
|
||||
return YES;
|
||||
}
|
||||
return NO;
|
||||
}
|
||||
|
||||
BOOL keycodeModifier(unsigned short keycode, uiModifiers *mod)
|
||||
{
|
||||
int i;
|
||||
|
||||
for (i = 0; keycodeModifiers[i].keycode != 0xFFFF; i++)
|
||||
if (keycodeModifiers[i].keycode == keycode) {
|
||||
*mod = keycodeModifiers[i].equiv;
|
||||
return YES;
|
||||
}
|
||||
return NO;
|
||||
}
|
||||
Vendored
+161
@@ -0,0 +1,161 @@
|
||||
// 15 august 2015
|
||||
#import "uipriv_darwin.h"
|
||||
|
||||
NSLayoutConstraint *mkConstraint(id view1, NSLayoutAttribute attr1, NSLayoutRelation relation, id view2, NSLayoutAttribute attr2, CGFloat multiplier, CGFloat c, NSString *desc)
|
||||
{
|
||||
NSLayoutConstraint *constraint;
|
||||
|
||||
constraint = [NSLayoutConstraint constraintWithItem:view1
|
||||
attribute:attr1
|
||||
relatedBy:relation
|
||||
toItem:view2
|
||||
attribute:attr2
|
||||
multiplier:multiplier
|
||||
constant:c];
|
||||
// apparently only added in 10.9
|
||||
if ([constraint respondsToSelector:@selector(setIdentifier:)])
|
||||
[((id) constraint) setIdentifier:desc];
|
||||
return constraint;
|
||||
}
|
||||
|
||||
CGFloat uiDarwinMarginAmount(void *reserved)
|
||||
{
|
||||
return 20.0;
|
||||
}
|
||||
|
||||
CGFloat uiDarwinPaddingAmount(void *reserved)
|
||||
{
|
||||
return 8.0;
|
||||
}
|
||||
|
||||
// this is needed for NSSplitView to work properly; see http://stackoverflow.com/questions/34574478/how-can-i-set-the-position-of-a-nssplitview-nowadays-setpositionofdivideratind (stal in irc.freenode.net/#macdev came up with the exact combination)
|
||||
// turns out it also works on NSTabView and NSBox too, possibly others!
|
||||
// and for bonus points, it even seems to fix unsatisfiable-constraint-autoresizing-mask issues with NSTabView and NSBox too!!! this is nuts
|
||||
void jiggleViewLayout(NSView *view)
|
||||
{
|
||||
[view setNeedsLayout:YES];
|
||||
[view layoutSubtreeIfNeeded];
|
||||
}
|
||||
|
||||
static CGFloat margins(int margined)
|
||||
{
|
||||
if (!margined)
|
||||
return 0.0;
|
||||
return uiDarwinMarginAmount(NULL);
|
||||
}
|
||||
|
||||
void singleChildConstraintsEstablish(struct singleChildConstraints *c, NSView *contentView, NSView *childView, BOOL hugsTrailing, BOOL hugsBottom, int margined, NSString *desc)
|
||||
{
|
||||
CGFloat margin;
|
||||
|
||||
margin = margins(margined);
|
||||
|
||||
c->leadingConstraint = mkConstraint(contentView, NSLayoutAttributeLeading,
|
||||
NSLayoutRelationEqual,
|
||||
childView, NSLayoutAttributeLeading,
|
||||
1, -margin,
|
||||
[desc stringByAppendingString:@" leading constraint"]);
|
||||
[contentView addConstraint:c->leadingConstraint];
|
||||
[c->leadingConstraint retain];
|
||||
|
||||
c->topConstraint = mkConstraint(contentView, NSLayoutAttributeTop,
|
||||
NSLayoutRelationEqual,
|
||||
childView, NSLayoutAttributeTop,
|
||||
1, -margin,
|
||||
[desc stringByAppendingString:@" top constraint"]);
|
||||
[contentView addConstraint:c->topConstraint];
|
||||
[c->topConstraint retain];
|
||||
|
||||
c->trailingConstraintGreater = mkConstraint(contentView, NSLayoutAttributeTrailing,
|
||||
NSLayoutRelationGreaterThanOrEqual,
|
||||
childView, NSLayoutAttributeTrailing,
|
||||
1, margin,
|
||||
[desc stringByAppendingString:@" trailing >= constraint"]);
|
||||
if (hugsTrailing)
|
||||
[c->trailingConstraintGreater setPriority:NSLayoutPriorityDefaultLow];
|
||||
[contentView addConstraint:c->trailingConstraintGreater];
|
||||
[c->trailingConstraintGreater retain];
|
||||
|
||||
c->trailingConstraintEqual = mkConstraint(contentView, NSLayoutAttributeTrailing,
|
||||
NSLayoutRelationEqual,
|
||||
childView, NSLayoutAttributeTrailing,
|
||||
1, margin,
|
||||
[desc stringByAppendingString:@" trailing == constraint"]);
|
||||
if (!hugsTrailing)
|
||||
[c->trailingConstraintEqual setPriority:NSLayoutPriorityDefaultLow];
|
||||
[contentView addConstraint:c->trailingConstraintEqual];
|
||||
[c->trailingConstraintEqual retain];
|
||||
|
||||
c->bottomConstraintGreater = mkConstraint(contentView, NSLayoutAttributeBottom,
|
||||
NSLayoutRelationGreaterThanOrEqual,
|
||||
childView, NSLayoutAttributeBottom,
|
||||
1, margin,
|
||||
[desc stringByAppendingString:@" bottom >= constraint"]);
|
||||
if (hugsBottom)
|
||||
[c->bottomConstraintGreater setPriority:NSLayoutPriorityDefaultLow];
|
||||
[contentView addConstraint:c->bottomConstraintGreater];
|
||||
[c->bottomConstraintGreater retain];
|
||||
|
||||
c->bottomConstraintEqual = mkConstraint(contentView, NSLayoutAttributeBottom,
|
||||
NSLayoutRelationEqual,
|
||||
childView, NSLayoutAttributeBottom,
|
||||
1, margin,
|
||||
[desc stringByAppendingString:@" bottom == constraint"]);
|
||||
if (!hugsBottom)
|
||||
[c->bottomConstraintEqual setPriority:NSLayoutPriorityDefaultLow];
|
||||
[contentView addConstraint:c->bottomConstraintEqual];
|
||||
[c->bottomConstraintEqual retain];
|
||||
}
|
||||
|
||||
void singleChildConstraintsRemove(struct singleChildConstraints *c, NSView *cv)
|
||||
{
|
||||
if (c->leadingConstraint != nil) {
|
||||
[cv removeConstraint:c->leadingConstraint];
|
||||
[c->leadingConstraint release];
|
||||
c->leadingConstraint = nil;
|
||||
}
|
||||
if (c->topConstraint != nil) {
|
||||
[cv removeConstraint:c->topConstraint];
|
||||
[c->topConstraint release];
|
||||
c->topConstraint = nil;
|
||||
}
|
||||
if (c->trailingConstraintGreater != nil) {
|
||||
[cv removeConstraint:c->trailingConstraintGreater];
|
||||
[c->trailingConstraintGreater release];
|
||||
c->trailingConstraintGreater = nil;
|
||||
}
|
||||
if (c->trailingConstraintEqual != nil) {
|
||||
[cv removeConstraint:c->trailingConstraintEqual];
|
||||
[c->trailingConstraintEqual release];
|
||||
c->trailingConstraintEqual = nil;
|
||||
}
|
||||
if (c->bottomConstraintGreater != nil) {
|
||||
[cv removeConstraint:c->bottomConstraintGreater];
|
||||
[c->bottomConstraintGreater release];
|
||||
c->bottomConstraintGreater = nil;
|
||||
}
|
||||
if (c->bottomConstraintEqual != nil) {
|
||||
[cv removeConstraint:c->bottomConstraintEqual];
|
||||
[c->bottomConstraintEqual release];
|
||||
c->bottomConstraintEqual = nil;
|
||||
}
|
||||
}
|
||||
|
||||
void singleChildConstraintsSetMargined(struct singleChildConstraints *c, int margined)
|
||||
{
|
||||
CGFloat margin;
|
||||
|
||||
margin = margins(margined);
|
||||
if (c->leadingConstraint != nil)
|
||||
[c->leadingConstraint setConstant:-margin];
|
||||
if (c->topConstraint != nil)
|
||||
[c->topConstraint setConstant:-margin];
|
||||
if (c->trailingConstraintGreater != nil)
|
||||
[c->trailingConstraintGreater setConstant:margin];
|
||||
if (c->trailingConstraintEqual != nil)
|
||||
[c->trailingConstraintEqual setConstant:margin];
|
||||
if (c->bottomConstraintGreater != nil)
|
||||
[c->bottomConstraintGreater setConstant:margin];
|
||||
if (c->bottomConstraintEqual != nil)
|
||||
[c->bottomConstraintEqual setConstant:margin];
|
||||
}
|
||||
Vendored
+469
@@ -0,0 +1,469 @@
|
||||
// 15 august 2015
|
||||
#import "uipriv_darwin.h"
|
||||
|
||||
// TODO hiding all stretchy controls still hugs trailing edge
|
||||
|
||||
@interface boxChild : NSObject
|
||||
@property uiControl *c;
|
||||
@property BOOL stretchy;
|
||||
@property NSLayoutPriority oldPrimaryHuggingPri;
|
||||
@property NSLayoutPriority oldSecondaryHuggingPri;
|
||||
- (NSView *)view;
|
||||
@end
|
||||
|
||||
@interface boxView : NSView {
|
||||
uiBox *b;
|
||||
NSMutableArray *children;
|
||||
BOOL vertical;
|
||||
int padded;
|
||||
|
||||
NSLayoutConstraint *first;
|
||||
NSMutableArray *inBetweens;
|
||||
NSLayoutConstraint *last;
|
||||
NSMutableArray *otherConstraints;
|
||||
|
||||
NSLayoutAttribute primaryStart;
|
||||
NSLayoutAttribute primaryEnd;
|
||||
NSLayoutAttribute secondaryStart;
|
||||
NSLayoutAttribute secondaryEnd;
|
||||
NSLayoutAttribute primarySize;
|
||||
NSLayoutConstraintOrientation primaryOrientation;
|
||||
NSLayoutConstraintOrientation secondaryOrientation;
|
||||
}
|
||||
- (id)initWithVertical:(BOOL)vert b:(uiBox *)bb;
|
||||
- (void)onDestroy;
|
||||
- (void)removeOurConstraints;
|
||||
- (void)syncEnableStates:(int)enabled;
|
||||
- (CGFloat)paddingAmount;
|
||||
- (void)establishOurConstraints;
|
||||
- (void)append:(uiControl *)c stretchy:(int)stretchy;
|
||||
- (void)delete:(int)n;
|
||||
- (int)isPadded;
|
||||
- (void)setPadded:(int)p;
|
||||
- (BOOL)hugsTrailing;
|
||||
- (BOOL)hugsBottom;
|
||||
- (int)nStretchy;
|
||||
@end
|
||||
|
||||
struct uiBox {
|
||||
uiDarwinControl c;
|
||||
boxView *view;
|
||||
};
|
||||
|
||||
@implementation boxChild
|
||||
|
||||
- (NSView *)view
|
||||
{
|
||||
return (NSView *) uiControlHandle(self.c);
|
||||
}
|
||||
|
||||
@end
|
||||
|
||||
@implementation boxView
|
||||
|
||||
- (id)initWithVertical:(BOOL)vert b:(uiBox *)bb
|
||||
{
|
||||
self = [super initWithFrame:NSZeroRect];
|
||||
if (self != nil) {
|
||||
// the weird names vert and bb are to shut the compiler up about shadowing because implicit this/self is stupid
|
||||
self->b = bb;
|
||||
self->vertical = vert;
|
||||
self->padded = 0;
|
||||
self->children = [NSMutableArray new];
|
||||
|
||||
self->inBetweens = [NSMutableArray new];
|
||||
self->otherConstraints = [NSMutableArray new];
|
||||
|
||||
if (self->vertical) {
|
||||
self->primaryStart = NSLayoutAttributeTop;
|
||||
self->primaryEnd = NSLayoutAttributeBottom;
|
||||
self->secondaryStart = NSLayoutAttributeLeading;
|
||||
self->secondaryEnd = NSLayoutAttributeTrailing;
|
||||
self->primarySize = NSLayoutAttributeHeight;
|
||||
self->primaryOrientation = NSLayoutConstraintOrientationVertical;
|
||||
self->secondaryOrientation = NSLayoutConstraintOrientationHorizontal;
|
||||
} else {
|
||||
self->primaryStart = NSLayoutAttributeLeading;
|
||||
self->primaryEnd = NSLayoutAttributeTrailing;
|
||||
self->secondaryStart = NSLayoutAttributeTop;
|
||||
self->secondaryEnd = NSLayoutAttributeBottom;
|
||||
self->primarySize = NSLayoutAttributeWidth;
|
||||
self->primaryOrientation = NSLayoutConstraintOrientationHorizontal;
|
||||
self->secondaryOrientation = NSLayoutConstraintOrientationVertical;
|
||||
}
|
||||
}
|
||||
return self;
|
||||
}
|
||||
|
||||
- (void)onDestroy
|
||||
{
|
||||
boxChild *bc;
|
||||
|
||||
[self removeOurConstraints];
|
||||
[self->inBetweens release];
|
||||
[self->otherConstraints release];
|
||||
|
||||
for (bc in self->children) {
|
||||
uiControlSetParent(bc.c, NULL);
|
||||
uiDarwinControlSetSuperview(uiDarwinControl(bc.c), nil);
|
||||
uiControlDestroy(bc.c);
|
||||
}
|
||||
[self->children release];
|
||||
}
|
||||
|
||||
- (void)removeOurConstraints
|
||||
{
|
||||
if (self->first != nil) {
|
||||
[self removeConstraint:self->first];
|
||||
[self->first release];
|
||||
self->first = nil;
|
||||
}
|
||||
if ([self->inBetweens count] != 0) {
|
||||
[self removeConstraints:self->inBetweens];
|
||||
[self->inBetweens removeAllObjects];
|
||||
}
|
||||
if (self->last != nil) {
|
||||
[self removeConstraint:self->last];
|
||||
[self->last release];
|
||||
self->last = nil;
|
||||
}
|
||||
if ([self->otherConstraints count] != 0) {
|
||||
[self removeConstraints:self->otherConstraints];
|
||||
[self->otherConstraints removeAllObjects];
|
||||
}
|
||||
}
|
||||
|
||||
- (void)syncEnableStates:(int)enabled
|
||||
{
|
||||
boxChild *bc;
|
||||
|
||||
for (bc in self->children)
|
||||
uiDarwinControlSyncEnableState(uiDarwinControl(bc.c), enabled);
|
||||
}
|
||||
|
||||
- (CGFloat)paddingAmount
|
||||
{
|
||||
if (!self->padded)
|
||||
return 0.0;
|
||||
return uiDarwinPaddingAmount(NULL);
|
||||
}
|
||||
|
||||
- (void)establishOurConstraints
|
||||
{
|
||||
boxChild *bc;
|
||||
CGFloat padding;
|
||||
NSView *prev;
|
||||
NSLayoutConstraint *c;
|
||||
BOOL (*hugsSecondary)(uiDarwinControl *);
|
||||
|
||||
[self removeOurConstraints];
|
||||
if ([self->children count] == 0)
|
||||
return;
|
||||
padding = [self paddingAmount];
|
||||
|
||||
// first arrange in the primary direction
|
||||
prev = nil;
|
||||
for (bc in self->children) {
|
||||
if (!uiControlVisible(bc.c))
|
||||
continue;
|
||||
if (prev == nil) { // first view
|
||||
self->first = mkConstraint(self, self->primaryStart,
|
||||
NSLayoutRelationEqual,
|
||||
[bc view], self->primaryStart,
|
||||
1, 0,
|
||||
@"uiBox first primary constraint");
|
||||
[self addConstraint:self->first];
|
||||
[self->first retain];
|
||||
prev = [bc view];
|
||||
continue;
|
||||
}
|
||||
// not the first; link it
|
||||
c = mkConstraint(prev, self->primaryEnd,
|
||||
NSLayoutRelationEqual,
|
||||
[bc view], self->primaryStart,
|
||||
1, -padding,
|
||||
@"uiBox in-between primary constraint");
|
||||
[self addConstraint:c];
|
||||
[self->inBetweens addObject:c];
|
||||
prev = [bc view];
|
||||
}
|
||||
if (prev == nil) // no control visible; act as if no controls
|
||||
return;
|
||||
self->last = mkConstraint(prev, self->primaryEnd,
|
||||
NSLayoutRelationEqual,
|
||||
self, self->primaryEnd,
|
||||
1, 0,
|
||||
@"uiBox last primary constraint");
|
||||
[self addConstraint:self->last];
|
||||
[self->last retain];
|
||||
|
||||
// then arrange in the secondary direction
|
||||
hugsSecondary = uiDarwinControlHugsTrailingEdge;
|
||||
if (!self->vertical)
|
||||
hugsSecondary = uiDarwinControlHugsBottom;
|
||||
for (bc in self->children) {
|
||||
if (!uiControlVisible(bc.c))
|
||||
continue;
|
||||
c = mkConstraint(self, self->secondaryStart,
|
||||
NSLayoutRelationEqual,
|
||||
[bc view], self->secondaryStart,
|
||||
1, 0,
|
||||
@"uiBox secondary start constraint");
|
||||
[self addConstraint:c];
|
||||
[self->otherConstraints addObject:c];
|
||||
c = mkConstraint([bc view], self->secondaryEnd,
|
||||
NSLayoutRelationLessThanOrEqual,
|
||||
self, self->secondaryEnd,
|
||||
1, 0,
|
||||
@"uiBox secondary end <= constraint");
|
||||
if ((*hugsSecondary)(uiDarwinControl(bc.c)))
|
||||
[c setPriority:NSLayoutPriorityDefaultLow];
|
||||
[self addConstraint:c];
|
||||
[self->otherConstraints addObject:c];
|
||||
c = mkConstraint([bc view], self->secondaryEnd,
|
||||
NSLayoutRelationEqual,
|
||||
self, self->secondaryEnd,
|
||||
1, 0,
|
||||
@"uiBox secondary end == constraint");
|
||||
if (!(*hugsSecondary)(uiDarwinControl(bc.c)))
|
||||
[c setPriority:NSLayoutPriorityDefaultLow];
|
||||
[self addConstraint:c];
|
||||
[self->otherConstraints addObject:c];
|
||||
}
|
||||
|
||||
// and make all stretchy controls the same size
|
||||
if ([self nStretchy] == 0)
|
||||
return;
|
||||
prev = nil; // first stretchy view
|
||||
for (bc in self->children) {
|
||||
if (!uiControlVisible(bc.c))
|
||||
continue;
|
||||
if (!bc.stretchy)
|
||||
continue;
|
||||
if (prev == nil) {
|
||||
prev = [bc view];
|
||||
continue;
|
||||
}
|
||||
c = mkConstraint(prev, self->primarySize,
|
||||
NSLayoutRelationEqual,
|
||||
[bc view], self->primarySize,
|
||||
1, 0,
|
||||
@"uiBox stretchy size constraint");
|
||||
[self addConstraint:c];
|
||||
[self->otherConstraints addObject:c];
|
||||
}
|
||||
}
|
||||
|
||||
- (void)append:(uiControl *)c stretchy:(int)stretchy
|
||||
{
|
||||
boxChild *bc;
|
||||
NSLayoutPriority priority;
|
||||
int oldnStretchy;
|
||||
|
||||
bc = [boxChild new];
|
||||
bc.c = c;
|
||||
bc.stretchy = stretchy;
|
||||
bc.oldPrimaryHuggingPri = uiDarwinControlHuggingPriority(uiDarwinControl(bc.c), self->primaryOrientation);
|
||||
bc.oldSecondaryHuggingPri = uiDarwinControlHuggingPriority(uiDarwinControl(bc.c), self->secondaryOrientation);
|
||||
|
||||
uiControlSetParent(bc.c, uiControl(self->b));
|
||||
uiDarwinControlSetSuperview(uiDarwinControl(bc.c), self);
|
||||
uiDarwinControlSyncEnableState(uiDarwinControl(bc.c), uiControlEnabledToUser(uiControl(self->b)));
|
||||
|
||||
// if a control is stretchy, it should not hug in the primary direction
|
||||
// otherwise, it should *forcibly* hug
|
||||
if (bc.stretchy)
|
||||
priority = NSLayoutPriorityDefaultLow;
|
||||
else
|
||||
// LONGTERM will default high work?
|
||||
priority = NSLayoutPriorityRequired;
|
||||
uiDarwinControlSetHuggingPriority(uiDarwinControl(bc.c), priority, self->primaryOrientation);
|
||||
// make sure controls don't hug their secondary direction so they fill the width of the view
|
||||
uiDarwinControlSetHuggingPriority(uiDarwinControl(bc.c), NSLayoutPriorityDefaultLow, self->secondaryOrientation);
|
||||
|
||||
oldnStretchy = [self nStretchy];
|
||||
[self->children addObject:bc];
|
||||
|
||||
[self establishOurConstraints];
|
||||
if (bc.stretchy)
|
||||
if (oldnStretchy == 0)
|
||||
uiDarwinNotifyEdgeHuggingChanged(uiDarwinControl(self->b));
|
||||
|
||||
[bc release]; // we don't need the initial reference now
|
||||
}
|
||||
|
||||
- (void)delete:(int)n
|
||||
{
|
||||
boxChild *bc;
|
||||
int stretchy;
|
||||
|
||||
bc = (boxChild *) [self->children objectAtIndex:n];
|
||||
stretchy = bc.stretchy;
|
||||
|
||||
uiControlSetParent(bc.c, NULL);
|
||||
uiDarwinControlSetSuperview(uiDarwinControl(bc.c), nil);
|
||||
|
||||
uiDarwinControlSetHuggingPriority(uiDarwinControl(bc.c), bc.oldPrimaryHuggingPri, self->primaryOrientation);
|
||||
uiDarwinControlSetHuggingPriority(uiDarwinControl(bc.c), bc.oldSecondaryHuggingPri, self->secondaryOrientation);
|
||||
|
||||
[self->children removeObjectAtIndex:n];
|
||||
|
||||
[self establishOurConstraints];
|
||||
if (stretchy)
|
||||
if ([self nStretchy] == 0)
|
||||
uiDarwinNotifyEdgeHuggingChanged(uiDarwinControl(self->b));
|
||||
}
|
||||
|
||||
- (int)isPadded
|
||||
{
|
||||
return self->padded;
|
||||
}
|
||||
|
||||
- (void)setPadded:(int)p
|
||||
{
|
||||
CGFloat padding;
|
||||
NSLayoutConstraint *c;
|
||||
|
||||
self->padded = p;
|
||||
padding = [self paddingAmount];
|
||||
for (c in self->inBetweens)
|
||||
[c setConstant:-padding];
|
||||
}
|
||||
|
||||
- (BOOL)hugsTrailing
|
||||
{
|
||||
if (self->vertical) // always hug if vertical
|
||||
return YES;
|
||||
return [self nStretchy] != 0;
|
||||
}
|
||||
|
||||
- (BOOL)hugsBottom
|
||||
{
|
||||
if (!self->vertical) // always hug if horizontal
|
||||
return YES;
|
||||
return [self nStretchy] != 0;
|
||||
}
|
||||
|
||||
- (int)nStretchy
|
||||
{
|
||||
boxChild *bc;
|
||||
int n;
|
||||
|
||||
n = 0;
|
||||
for (bc in self->children) {
|
||||
if (!uiControlVisible(bc.c))
|
||||
continue;
|
||||
if (bc.stretchy)
|
||||
n++;
|
||||
}
|
||||
return n;
|
||||
}
|
||||
|
||||
@end
|
||||
|
||||
static void uiBoxDestroy(uiControl *c)
|
||||
{
|
||||
uiBox *b = uiBox(c);
|
||||
|
||||
[b->view onDestroy];
|
||||
[b->view release];
|
||||
uiFreeControl(uiControl(b));
|
||||
}
|
||||
|
||||
uiDarwinControlDefaultHandle(uiBox, view)
|
||||
uiDarwinControlDefaultParent(uiBox, view)
|
||||
uiDarwinControlDefaultSetParent(uiBox, view)
|
||||
uiDarwinControlDefaultToplevel(uiBox, view)
|
||||
uiDarwinControlDefaultVisible(uiBox, view)
|
||||
uiDarwinControlDefaultShow(uiBox, view)
|
||||
uiDarwinControlDefaultHide(uiBox, view)
|
||||
uiDarwinControlDefaultEnabled(uiBox, view)
|
||||
uiDarwinControlDefaultEnable(uiBox, view)
|
||||
uiDarwinControlDefaultDisable(uiBox, view)
|
||||
|
||||
static void uiBoxSyncEnableState(uiDarwinControl *c, int enabled)
|
||||
{
|
||||
uiBox *b = uiBox(c);
|
||||
|
||||
if (uiDarwinShouldStopSyncEnableState(uiDarwinControl(b), enabled))
|
||||
return;
|
||||
[b->view syncEnableStates:enabled];
|
||||
}
|
||||
|
||||
uiDarwinControlDefaultSetSuperview(uiBox, view)
|
||||
|
||||
static BOOL uiBoxHugsTrailingEdge(uiDarwinControl *c)
|
||||
{
|
||||
uiBox *b = uiBox(c);
|
||||
|
||||
return [b->view hugsTrailing];
|
||||
}
|
||||
|
||||
static BOOL uiBoxHugsBottom(uiDarwinControl *c)
|
||||
{
|
||||
uiBox *b = uiBox(c);
|
||||
|
||||
return [b->view hugsBottom];
|
||||
}
|
||||
|
||||
static void uiBoxChildEdgeHuggingChanged(uiDarwinControl *c)
|
||||
{
|
||||
uiBox *b = uiBox(c);
|
||||
|
||||
[b->view establishOurConstraints];
|
||||
}
|
||||
|
||||
uiDarwinControlDefaultHuggingPriority(uiBox, view)
|
||||
uiDarwinControlDefaultSetHuggingPriority(uiBox, view)
|
||||
|
||||
static void uiBoxChildVisibilityChanged(uiDarwinControl *c)
|
||||
{
|
||||
uiBox *b = uiBox(c);
|
||||
|
||||
[b->view establishOurConstraints];
|
||||
}
|
||||
|
||||
void uiBoxAppend(uiBox *b, uiControl *c, int stretchy)
|
||||
{
|
||||
// LONGTERM on other platforms
|
||||
// or at leat allow this and implicitly turn it into a spacer
|
||||
if (c == NULL)
|
||||
userbug("You cannot add NULL to a uiBox.");
|
||||
[b->view append:c stretchy:stretchy];
|
||||
}
|
||||
|
||||
void uiBoxDelete(uiBox *b, int n)
|
||||
{
|
||||
[b->view delete:n];
|
||||
}
|
||||
|
||||
int uiBoxPadded(uiBox *b)
|
||||
{
|
||||
return [b->view isPadded];
|
||||
}
|
||||
|
||||
void uiBoxSetPadded(uiBox *b, int padded)
|
||||
{
|
||||
[b->view setPadded:padded];
|
||||
}
|
||||
|
||||
static uiBox *finishNewBox(BOOL vertical)
|
||||
{
|
||||
uiBox *b;
|
||||
|
||||
uiDarwinNewControl(uiBox, b);
|
||||
|
||||
b->view = [[boxView alloc] initWithVertical:vertical b:b];
|
||||
|
||||
return b;
|
||||
}
|
||||
|
||||
uiBox *uiNewHorizontalBox(void)
|
||||
{
|
||||
return finishNewBox(NO);
|
||||
}
|
||||
|
||||
uiBox *uiNewVerticalBox(void)
|
||||
{
|
||||
return finishNewBox(YES);
|
||||
}
|
||||
Vendored
+113
@@ -0,0 +1,113 @@
|
||||
// 13 august 2015
|
||||
#import "uipriv_darwin.h"
|
||||
|
||||
struct uiButton {
|
||||
uiDarwinControl c;
|
||||
NSButton *button;
|
||||
void (*onClicked)(uiButton *, void *);
|
||||
void *onClickedData;
|
||||
};
|
||||
|
||||
@interface buttonDelegateClass : NSObject {
|
||||
struct mapTable *buttons;
|
||||
}
|
||||
- (IBAction)onClicked:(id)sender;
|
||||
- (void)registerButton:(uiButton *)b;
|
||||
- (void)unregisterButton:(uiButton *)b;
|
||||
@end
|
||||
|
||||
@implementation buttonDelegateClass
|
||||
|
||||
- (id)init
|
||||
{
|
||||
self = [super init];
|
||||
if (self)
|
||||
self->buttons = newMap();
|
||||
return self;
|
||||
}
|
||||
|
||||
- (void)dealloc
|
||||
{
|
||||
mapDestroy(self->buttons);
|
||||
[super dealloc];
|
||||
}
|
||||
|
||||
- (IBAction)onClicked:(id)sender
|
||||
{
|
||||
uiButton *b;
|
||||
|
||||
b = (uiButton *) mapGet(self->buttons, sender);
|
||||
(*(b->onClicked))(b, b->onClickedData);
|
||||
}
|
||||
|
||||
- (void)registerButton:(uiButton *)b
|
||||
{
|
||||
mapSet(self->buttons, b->button, b);
|
||||
[b->button setTarget:self];
|
||||
[b->button setAction:@selector(onClicked:)];
|
||||
}
|
||||
|
||||
- (void)unregisterButton:(uiButton *)b
|
||||
{
|
||||
[b->button setTarget:nil];
|
||||
mapDelete(self->buttons, b->button);
|
||||
}
|
||||
|
||||
@end
|
||||
|
||||
static buttonDelegateClass *buttonDelegate = nil;
|
||||
|
||||
uiDarwinControlAllDefaultsExceptDestroy(uiButton, button)
|
||||
|
||||
static void uiButtonDestroy(uiControl *c)
|
||||
{
|
||||
uiButton *b = uiButton(c);
|
||||
|
||||
[buttonDelegate unregisterButton:b];
|
||||
[b->button release];
|
||||
uiFreeControl(uiControl(b));
|
||||
}
|
||||
|
||||
char *uiButtonText(uiButton *b)
|
||||
{
|
||||
return uiDarwinNSStringToText([b->button title]);
|
||||
}
|
||||
|
||||
void uiButtonSetText(uiButton *b, const char *text)
|
||||
{
|
||||
[b->button setTitle:toNSString(text)];
|
||||
}
|
||||
|
||||
void uiButtonOnClicked(uiButton *b, void (*f)(uiButton *, void *), void *data)
|
||||
{
|
||||
b->onClicked = f;
|
||||
b->onClickedData = data;
|
||||
}
|
||||
|
||||
static void defaultOnClicked(uiButton *b, void *data)
|
||||
{
|
||||
// do nothing
|
||||
}
|
||||
|
||||
uiButton *uiNewButton(const char *text)
|
||||
{
|
||||
uiButton *b;
|
||||
|
||||
uiDarwinNewControl(uiButton, b);
|
||||
|
||||
b->button = [[NSButton alloc] initWithFrame:NSZeroRect];
|
||||
[b->button setTitle:toNSString(text)];
|
||||
[b->button setButtonType:NSMomentaryPushInButton];
|
||||
[b->button setBordered:YES];
|
||||
[b->button setBezelStyle:NSRoundedBezelStyle];
|
||||
uiDarwinSetControlFont(b->button, NSRegularControlSize);
|
||||
|
||||
if (buttonDelegate == nil) {
|
||||
buttonDelegate = [[buttonDelegateClass new] autorelease];
|
||||
[delegates addObject:buttonDelegate];
|
||||
}
|
||||
[buttonDelegate registerButton:b];
|
||||
uiButtonOnClicked(b, defaultOnClicked, NULL);
|
||||
|
||||
return b;
|
||||
}
|
||||
Vendored
+129
@@ -0,0 +1,129 @@
|
||||
// 14 august 2015
|
||||
#import "uipriv_darwin.h"
|
||||
|
||||
struct uiCheckbox {
|
||||
uiDarwinControl c;
|
||||
NSButton *button;
|
||||
void (*onToggled)(uiCheckbox *, void *);
|
||||
void *onToggledData;
|
||||
};
|
||||
|
||||
@interface checkboxDelegateClass : NSObject {
|
||||
struct mapTable *buttons;
|
||||
}
|
||||
- (IBAction)onToggled:(id)sender;
|
||||
- (void)registerCheckbox:(uiCheckbox *)c;
|
||||
- (void)unregisterCheckbox:(uiCheckbox *)c;
|
||||
@end
|
||||
|
||||
@implementation checkboxDelegateClass
|
||||
|
||||
- (id)init
|
||||
{
|
||||
self = [super init];
|
||||
if (self)
|
||||
self->buttons = newMap();
|
||||
return self;
|
||||
}
|
||||
|
||||
- (void)dealloc
|
||||
{
|
||||
mapDestroy(self->buttons);
|
||||
[super dealloc];
|
||||
}
|
||||
|
||||
- (IBAction)onToggled:(id)sender
|
||||
{
|
||||
uiCheckbox *c;
|
||||
|
||||
c = (uiCheckbox *) mapGet(self->buttons, sender);
|
||||
(*(c->onToggled))(c, c->onToggledData);
|
||||
}
|
||||
|
||||
- (void)registerCheckbox:(uiCheckbox *)c
|
||||
{
|
||||
mapSet(self->buttons, c->button, c);
|
||||
[c->button setTarget:self];
|
||||
[c->button setAction:@selector(onToggled:)];
|
||||
}
|
||||
|
||||
- (void)unregisterCheckbox:(uiCheckbox *)c
|
||||
{
|
||||
[c->button setTarget:nil];
|
||||
mapDelete(self->buttons, c->button);
|
||||
}
|
||||
|
||||
@end
|
||||
|
||||
static checkboxDelegateClass *checkboxDelegate = nil;
|
||||
|
||||
uiDarwinControlAllDefaultsExceptDestroy(uiCheckbox, button)
|
||||
|
||||
static void uiCheckboxDestroy(uiControl *cc)
|
||||
{
|
||||
uiCheckbox *c = uiCheckbox(cc);
|
||||
|
||||
[checkboxDelegate unregisterCheckbox:c];
|
||||
[c->button release];
|
||||
uiFreeControl(uiControl(c));
|
||||
}
|
||||
|
||||
char *uiCheckboxText(uiCheckbox *c)
|
||||
{
|
||||
return uiDarwinNSStringToText([c->button title]);
|
||||
}
|
||||
|
||||
void uiCheckboxSetText(uiCheckbox *c, const char *text)
|
||||
{
|
||||
[c->button setTitle:toNSString(text)];
|
||||
}
|
||||
|
||||
void uiCheckboxOnToggled(uiCheckbox *c, void (*f)(uiCheckbox *, void *), void *data)
|
||||
{
|
||||
c->onToggled = f;
|
||||
c->onToggledData = data;
|
||||
}
|
||||
|
||||
int uiCheckboxChecked(uiCheckbox *c)
|
||||
{
|
||||
return [c->button state] == NSOnState;
|
||||
}
|
||||
|
||||
void uiCheckboxSetChecked(uiCheckbox *c, int checked)
|
||||
{
|
||||
NSInteger state;
|
||||
|
||||
state = NSOnState;
|
||||
if (!checked)
|
||||
state = NSOffState;
|
||||
[c->button setState:state];
|
||||
}
|
||||
|
||||
static void defaultOnToggled(uiCheckbox *c, void *data)
|
||||
{
|
||||
// do nothing
|
||||
}
|
||||
|
||||
uiCheckbox *uiNewCheckbox(const char *text)
|
||||
{
|
||||
uiCheckbox *c;
|
||||
|
||||
uiDarwinNewControl(uiCheckbox, c);
|
||||
|
||||
c->button = [[NSButton alloc] initWithFrame:NSZeroRect];
|
||||
[c->button setTitle:toNSString(text)];
|
||||
[c->button setButtonType:NSSwitchButton];
|
||||
// doesn't seem to have an associated bezel style
|
||||
[c->button setBordered:NO];
|
||||
[c->button setTransparent:NO];
|
||||
uiDarwinSetControlFont(c->button, NSRegularControlSize);
|
||||
|
||||
if (checkboxDelegate == nil) {
|
||||
checkboxDelegate = [[checkboxDelegateClass new] autorelease];
|
||||
[delegates addObject:checkboxDelegate];
|
||||
}
|
||||
[checkboxDelegate registerCheckbox:c];
|
||||
uiCheckboxOnToggled(c, defaultOnToggled, NULL);
|
||||
|
||||
return c;
|
||||
}
|
||||
Vendored
+159
@@ -0,0 +1,159 @@
|
||||
// 15 may 2016
|
||||
#import "uipriv_darwin.h"
|
||||
|
||||
// TODO no intrinsic height?
|
||||
|
||||
@interface colorButton : NSColorWell {
|
||||
uiColorButton *libui_b;
|
||||
BOOL libui_changing;
|
||||
BOOL libui_setting;
|
||||
}
|
||||
- (id)initWithFrame:(NSRect)frame libuiColorButton:(uiColorButton *)b;
|
||||
- (void)deactivateOnClose:(NSNotification *)note;
|
||||
- (void)libuiColor:(double *)r g:(double *)g b:(double *)b a:(double *)a;
|
||||
- (void)libuiSetColor:(double)r g:(double)g b:(double)b a:(double)a;
|
||||
@end
|
||||
|
||||
// only one may be active at one time
|
||||
static colorButton *activeColorButton = nil;
|
||||
|
||||
struct uiColorButton {
|
||||
uiDarwinControl c;
|
||||
colorButton *button;
|
||||
void (*onChanged)(uiColorButton *, void *);
|
||||
void *onChangedData;
|
||||
};
|
||||
|
||||
@implementation colorButton
|
||||
|
||||
- (id)initWithFrame:(NSRect)frame libuiColorButton:(uiColorButton *)b
|
||||
{
|
||||
self = [super initWithFrame:frame];
|
||||
if (self) {
|
||||
// the default color is white; set it to black first (see -setColor: below for why we do it first)
|
||||
[self libuiSetColor:0.0 g:0.0 b:0.0 a:1.0];
|
||||
|
||||
self->libui_b = b;
|
||||
self->libui_changing = NO;
|
||||
}
|
||||
return self;
|
||||
}
|
||||
|
||||
- (void)activate:(BOOL)exclusive
|
||||
{
|
||||
if (activeColorButton != nil)
|
||||
activeColorButton->libui_changing = YES;
|
||||
[NSColorPanel setPickerMask:NSColorPanelAllModesMask];
|
||||
[[NSColorPanel sharedColorPanel] setShowsAlpha:YES];
|
||||
[super activate:YES];
|
||||
activeColorButton = self;
|
||||
// see stddialogs.m for details
|
||||
[[NSColorPanel sharedColorPanel] setWorksWhenModal:NO];
|
||||
[[NSNotificationCenter defaultCenter] addObserver:self
|
||||
selector:@selector(deactivateOnClose:)
|
||||
name:NSWindowWillCloseNotification
|
||||
object:[NSColorPanel sharedColorPanel]];
|
||||
}
|
||||
|
||||
- (void)deactivate
|
||||
{
|
||||
[super deactivate];
|
||||
activeColorButton = nil;
|
||||
if (!self->libui_changing)
|
||||
[[NSColorPanel sharedColorPanel] orderOut:nil];
|
||||
[[NSNotificationCenter defaultCenter] removeObserver:self
|
||||
name:NSWindowWillCloseNotification
|
||||
object:[NSColorPanel sharedColorPanel]];
|
||||
self->libui_changing = NO;
|
||||
}
|
||||
|
||||
- (void)deactivateOnClose:(NSNotification *)note
|
||||
{
|
||||
[self deactivate];
|
||||
}
|
||||
|
||||
- (void)setColor:(NSColor *)color
|
||||
{
|
||||
uiColorButton *b = self->libui_b;
|
||||
|
||||
[super setColor:color];
|
||||
// this is called by NSColorWell's init, so we have to guard
|
||||
// also don't signal during a programmatic change
|
||||
if (b != nil && !self->libui_setting)
|
||||
(*(b->onChanged))(b, b->onChangedData);
|
||||
}
|
||||
|
||||
- (void)libuiColor:(double *)r g:(double *)g b:(double *)b a:(double *)a
|
||||
{
|
||||
NSColor *rgba;
|
||||
CGFloat cr, cg, cb, ca;
|
||||
|
||||
// the given color may not be an RGBA color, which will cause the -getRed:green:blue:alpha: call to throw an exception
|
||||
rgba = [[self color] colorUsingColorSpace:[NSColorSpace sRGBColorSpace]];
|
||||
[rgba getRed:&cr green:&cg blue:&cb alpha:&ca];
|
||||
*r = cr;
|
||||
*g = cg;
|
||||
*b = cb;
|
||||
*a = ca;
|
||||
// rgba will be autoreleased since it isn't a new or init call
|
||||
}
|
||||
|
||||
- (void)libuiSetColor:(double)r g:(double)g b:(double)b a:(double)a
|
||||
{
|
||||
self->libui_setting = YES;
|
||||
[self setColor:[NSColor colorWithSRGBRed:r green:g blue:b alpha:a]];
|
||||
self->libui_setting = NO;
|
||||
}
|
||||
|
||||
// NSColorWell has no intrinsic size by default; give it the default Interface Builder size.
|
||||
- (NSSize)intrinsicContentSize
|
||||
{
|
||||
return NSMakeSize(44, 23);
|
||||
}
|
||||
|
||||
@end
|
||||
|
||||
uiDarwinControlAllDefaults(uiColorButton, button)
|
||||
|
||||
// we do not want color change events to be sent to any controls other than the color buttons
|
||||
// see main.m for more details
|
||||
BOOL colorButtonInhibitSendAction(SEL sel, id from, id to)
|
||||
{
|
||||
if (sel != @selector(changeColor:))
|
||||
return NO;
|
||||
return ![to isKindOfClass:[colorButton class]];
|
||||
}
|
||||
|
||||
static void defaultOnChanged(uiColorButton *b, void *data)
|
||||
{
|
||||
// do nothing
|
||||
}
|
||||
|
||||
void uiColorButtonColor(uiColorButton *b, double *r, double *g, double *bl, double *a)
|
||||
{
|
||||
[b->button libuiColor:r g:g b:bl a:a];
|
||||
}
|
||||
|
||||
void uiColorButtonSetColor(uiColorButton *b, double r, double g, double bl, double a)
|
||||
{
|
||||
[b->button libuiSetColor:r g:g b:bl a:a];
|
||||
}
|
||||
|
||||
void uiColorButtonOnChanged(uiColorButton *b, void (*f)(uiColorButton *, void *), void *data)
|
||||
{
|
||||
b->onChanged = f;
|
||||
b->onChangedData = data;
|
||||
}
|
||||
|
||||
uiColorButton *uiNewColorButton(void)
|
||||
{
|
||||
uiColorButton *b;
|
||||
|
||||
uiDarwinNewControl(uiColorButton, b);
|
||||
|
||||
b->button = [[colorButton alloc] initWithFrame:NSZeroRect libuiColorButton:b];
|
||||
|
||||
uiColorButtonOnChanged(b, defaultOnChanged, NULL);
|
||||
|
||||
return b;
|
||||
}
|
||||
Vendored
+145
@@ -0,0 +1,145 @@
|
||||
// 14 august 2015
|
||||
#import "uipriv_darwin.h"
|
||||
|
||||
// NSComboBoxes have no intrinsic width; we'll use the default Interface Builder width for them.
|
||||
// NSPopUpButton is fine.
|
||||
#define comboboxWidth 96
|
||||
|
||||
struct uiCombobox {
|
||||
uiDarwinControl c;
|
||||
NSPopUpButton *pb;
|
||||
NSArrayController *pbac;
|
||||
void (*onSelected)(uiCombobox *, void *);
|
||||
void *onSelectedData;
|
||||
};
|
||||
|
||||
@interface comboboxDelegateClass : NSObject {
|
||||
struct mapTable *comboboxes;
|
||||
}
|
||||
- (IBAction)onSelected:(id)sender;
|
||||
- (void)registerCombobox:(uiCombobox *)c;
|
||||
- (void)unregisterCombobox:(uiCombobox *)c;
|
||||
@end
|
||||
|
||||
@implementation comboboxDelegateClass
|
||||
|
||||
- (id)init
|
||||
{
|
||||
self = [super init];
|
||||
if (self)
|
||||
self->comboboxes = newMap();
|
||||
return self;
|
||||
}
|
||||
|
||||
- (void)dealloc
|
||||
{
|
||||
mapDestroy(self->comboboxes);
|
||||
[super dealloc];
|
||||
}
|
||||
|
||||
- (IBAction)onSelected:(id)sender
|
||||
{
|
||||
uiCombobox *c;
|
||||
|
||||
c = uiCombobox(mapGet(self->comboboxes, sender));
|
||||
(*(c->onSelected))(c, c->onSelectedData);
|
||||
}
|
||||
|
||||
- (void)registerCombobox:(uiCombobox *)c
|
||||
{
|
||||
mapSet(self->comboboxes, c->pb, c);
|
||||
[c->pb setTarget:self];
|
||||
[c->pb setAction:@selector(onSelected:)];
|
||||
}
|
||||
|
||||
- (void)unregisterCombobox:(uiCombobox *)c
|
||||
{
|
||||
[c->pb setTarget:nil];
|
||||
mapDelete(self->comboboxes, c->pb);
|
||||
}
|
||||
|
||||
@end
|
||||
|
||||
static comboboxDelegateClass *comboboxDelegate = nil;
|
||||
|
||||
uiDarwinControlAllDefaultsExceptDestroy(uiCombobox, pb)
|
||||
|
||||
static void uiComboboxDestroy(uiControl *cc)
|
||||
{
|
||||
uiCombobox *c = uiCombobox(cc);
|
||||
|
||||
[comboboxDelegate unregisterCombobox:c];
|
||||
[c->pb unbind:@"contentObjects"];
|
||||
[c->pb unbind:@"selectedIndex"];
|
||||
[c->pbac release];
|
||||
[c->pb release];
|
||||
uiFreeControl(uiControl(c));
|
||||
}
|
||||
|
||||
void uiComboboxAppend(uiCombobox *c, const char *text)
|
||||
{
|
||||
[c->pbac addObject:toNSString(text)];
|
||||
}
|
||||
|
||||
int uiComboboxSelected(uiCombobox *c)
|
||||
{
|
||||
return [c->pb indexOfSelectedItem];
|
||||
}
|
||||
|
||||
void uiComboboxSetSelected(uiCombobox *c, int n)
|
||||
{
|
||||
[c->pb selectItemAtIndex:n];
|
||||
}
|
||||
|
||||
void uiComboboxOnSelected(uiCombobox *c, void (*f)(uiCombobox *c, void *data), void *data)
|
||||
{
|
||||
c->onSelected = f;
|
||||
c->onSelectedData = data;
|
||||
}
|
||||
|
||||
static void defaultOnSelected(uiCombobox *c, void *data)
|
||||
{
|
||||
// do nothing
|
||||
}
|
||||
|
||||
uiCombobox *uiNewCombobox(void)
|
||||
{
|
||||
uiCombobox *c;
|
||||
NSPopUpButtonCell *pbcell;
|
||||
|
||||
uiDarwinNewControl(uiCombobox, c);
|
||||
|
||||
c->pb = [[NSPopUpButton alloc] initWithFrame:NSZeroRect pullsDown:NO];
|
||||
[c->pb setPreferredEdge:NSMinYEdge];
|
||||
pbcell = (NSPopUpButtonCell *) [c->pb cell];
|
||||
[pbcell setArrowPosition:NSPopUpArrowAtBottom];
|
||||
// the font defined by Interface Builder is Menu 13, which is lol
|
||||
// just use the regular control size for consistency
|
||||
uiDarwinSetControlFont(c->pb, NSRegularControlSize);
|
||||
|
||||
// NSPopUpButton doesn't work like a combobox
|
||||
// - it automatically selects the first item
|
||||
// - it doesn't support duplicates
|
||||
// but we can use a NSArrayController and Cocoa bindings to bypass these restrictions
|
||||
c->pbac = [NSArrayController new];
|
||||
[c->pbac setAvoidsEmptySelection:NO];
|
||||
[c->pbac setSelectsInsertedObjects:NO];
|
||||
[c->pbac setAutomaticallyRearrangesObjects:NO];
|
||||
[c->pb bind:@"contentValues"
|
||||
toObject:c->pbac
|
||||
withKeyPath:@"arrangedObjects"
|
||||
options:nil];
|
||||
[c->pb bind:@"selectedIndex"
|
||||
toObject:c->pbac
|
||||
withKeyPath:@"selectionIndex"
|
||||
options:nil];
|
||||
|
||||
if (comboboxDelegate == nil) {
|
||||
comboboxDelegate = [[comboboxDelegateClass new] autorelease];
|
||||
[delegates addObject:comboboxDelegate];
|
||||
}
|
||||
[comboboxDelegate registerCombobox:c];
|
||||
uiComboboxOnSelected(c, defaultOnSelected, NULL);
|
||||
|
||||
return c;
|
||||
}
|
||||
Vendored
+84
@@ -0,0 +1,84 @@
|
||||
// 16 august 2015
|
||||
#import "uipriv_darwin.h"
|
||||
|
||||
void uiDarwinControlSyncEnableState(uiDarwinControl *c, int state)
|
||||
{
|
||||
(*(c->SyncEnableState))(c, state);
|
||||
}
|
||||
|
||||
void uiDarwinControlSetSuperview(uiDarwinControl *c, NSView *superview)
|
||||
{
|
||||
(*(c->SetSuperview))(c, superview);
|
||||
}
|
||||
|
||||
BOOL uiDarwinControlHugsTrailingEdge(uiDarwinControl *c)
|
||||
{
|
||||
return (*(c->HugsTrailingEdge))(c);
|
||||
}
|
||||
|
||||
BOOL uiDarwinControlHugsBottom(uiDarwinControl *c)
|
||||
{
|
||||
return (*(c->HugsBottom))(c);
|
||||
}
|
||||
|
||||
void uiDarwinControlChildEdgeHuggingChanged(uiDarwinControl *c)
|
||||
{
|
||||
(*(c->ChildEdgeHuggingChanged))(c);
|
||||
}
|
||||
|
||||
NSLayoutPriority uiDarwinControlHuggingPriority(uiDarwinControl *c, NSLayoutConstraintOrientation orientation)
|
||||
{
|
||||
return (*(c->HuggingPriority))(c, orientation);
|
||||
}
|
||||
|
||||
void uiDarwinControlSetHuggingPriority(uiDarwinControl *c, NSLayoutPriority priority, NSLayoutConstraintOrientation orientation)
|
||||
{
|
||||
(*(c->SetHuggingPriority))(c, priority, orientation);
|
||||
}
|
||||
|
||||
void uiDarwinControlChildVisibilityChanged(uiDarwinControl *c)
|
||||
{
|
||||
(*(c->ChildVisibilityChanged))(c);
|
||||
}
|
||||
|
||||
void uiDarwinSetControlFont(NSControl *c, NSControlSize size)
|
||||
{
|
||||
[c setFont:[NSFont systemFontOfSize:[NSFont systemFontSizeForControlSize:size]]];
|
||||
}
|
||||
|
||||
#define uiDarwinControlSignature 0x44617277
|
||||
|
||||
uiDarwinControl *uiDarwinAllocControl(size_t n, uint32_t typesig, const char *typenamestr)
|
||||
{
|
||||
return uiDarwinControl(uiAllocControl(n, uiDarwinControlSignature, typesig, typenamestr));
|
||||
}
|
||||
|
||||
BOOL uiDarwinShouldStopSyncEnableState(uiDarwinControl *c, BOOL enabled)
|
||||
{
|
||||
int ce;
|
||||
|
||||
ce = uiControlEnabled(uiControl(c));
|
||||
// only stop if we're going from disabled back to enabled; don't stop under any other condition
|
||||
// (if we stop when going from enabled to disabled then enabled children of a disabled control won't get disabled at the OS level)
|
||||
if (!ce && enabled)
|
||||
return YES;
|
||||
return NO;
|
||||
}
|
||||
|
||||
void uiDarwinNotifyEdgeHuggingChanged(uiDarwinControl *c)
|
||||
{
|
||||
uiControl *parent;
|
||||
|
||||
parent = uiControlParent(uiControl(c));
|
||||
if (parent != NULL)
|
||||
uiDarwinControlChildEdgeHuggingChanged(uiDarwinControl(parent));
|
||||
}
|
||||
|
||||
void uiDarwinNotifyVisibilityChanged(uiDarwinControl *c)
|
||||
{
|
||||
uiControl *parent;
|
||||
|
||||
parent = uiControlParent(uiControl(c));
|
||||
if (parent != NULL)
|
||||
uiDarwinControlChildVisibilityChanged(uiDarwinControl(parent));
|
||||
}
|
||||
Vendored
+42
@@ -0,0 +1,42 @@
|
||||
// 14 august 2015
|
||||
#import "uipriv_darwin.h"
|
||||
|
||||
struct uiDateTimePicker {
|
||||
uiDarwinControl c;
|
||||
NSDatePicker *dp;
|
||||
};
|
||||
|
||||
uiDarwinControlAllDefaults(uiDateTimePicker, dp)
|
||||
|
||||
static uiDateTimePicker *finishNewDateTimePicker(NSDatePickerElementFlags elements)
|
||||
{
|
||||
uiDateTimePicker *d;
|
||||
|
||||
uiDarwinNewControl(uiDateTimePicker, d);
|
||||
|
||||
d->dp = [[NSDatePicker alloc] initWithFrame:NSZeroRect];
|
||||
[d->dp setBordered:NO];
|
||||
[d->dp setBezeled:YES];
|
||||
[d->dp setDrawsBackground:YES];
|
||||
[d->dp setDatePickerStyle:NSTextFieldAndStepperDatePickerStyle];
|
||||
[d->dp setDatePickerElements:elements];
|
||||
[d->dp setDatePickerMode:NSSingleDateMode];
|
||||
uiDarwinSetControlFont(d->dp, NSRegularControlSize);
|
||||
|
||||
return d;
|
||||
}
|
||||
|
||||
uiDateTimePicker *uiNewDateTimePicker(void)
|
||||
{
|
||||
return finishNewDateTimePicker(NSYearMonthDayDatePickerElementFlag | NSHourMinuteSecondDatePickerElementFlag);
|
||||
}
|
||||
|
||||
uiDateTimePicker *uiNewDatePicker(void)
|
||||
{
|
||||
return finishNewDateTimePicker(NSYearMonthDayDatePickerElementFlag);
|
||||
}
|
||||
|
||||
uiDateTimePicker *uiNewTimePicker(void)
|
||||
{
|
||||
return finishNewDateTimePicker(NSHourMinuteSecondDatePickerElementFlag);
|
||||
}
|
||||
Vendored
+19
@@ -0,0 +1,19 @@
|
||||
// 13 may 2016
|
||||
#import "uipriv_darwin.h"
|
||||
|
||||
// LONGTERM don't halt on release builds
|
||||
|
||||
void realbug(const char *file, const char *line, const char *func, const char *prefix, const char *format, va_list ap)
|
||||
{
|
||||
NSMutableString *str;
|
||||
NSString *formatted;
|
||||
|
||||
str = [NSMutableString new];
|
||||
[str appendString:[NSString stringWithFormat:@"[libui] %s:%s:%s() %s", file, line, func, prefix]];
|
||||
formatted = [[NSString alloc] initWithFormat:[NSString stringWithUTF8String:format] arguments:ap];
|
||||
[str appendString:formatted];
|
||||
[formatted release];
|
||||
NSLog(@"%@", str);
|
||||
[str release];
|
||||
__builtin_trap();
|
||||
}
|
||||
Vendored
+454
@@ -0,0 +1,454 @@
|
||||
// 6 september 2015
|
||||
#import "uipriv_darwin.h"
|
||||
|
||||
struct uiDrawPath {
|
||||
CGMutablePathRef path;
|
||||
uiDrawFillMode fillMode;
|
||||
BOOL ended;
|
||||
};
|
||||
|
||||
uiDrawPath *uiDrawNewPath(uiDrawFillMode mode)
|
||||
{
|
||||
uiDrawPath *p;
|
||||
|
||||
p = uiNew(uiDrawPath);
|
||||
p->path = CGPathCreateMutable();
|
||||
p->fillMode = mode;
|
||||
return p;
|
||||
}
|
||||
|
||||
void uiDrawFreePath(uiDrawPath *p)
|
||||
{
|
||||
CGPathRelease((CGPathRef) (p->path));
|
||||
uiFree(p);
|
||||
}
|
||||
|
||||
void uiDrawPathNewFigure(uiDrawPath *p, double x, double y)
|
||||
{
|
||||
if (p->ended)
|
||||
userbug("You cannot call uiDrawPathNewFigure() on a uiDrawPath that has already been ended. (path; %p)", p);
|
||||
CGPathMoveToPoint(p->path, NULL, x, y);
|
||||
}
|
||||
|
||||
void uiDrawPathNewFigureWithArc(uiDrawPath *p, double xCenter, double yCenter, double radius, double startAngle, double sweep, int negative)
|
||||
{
|
||||
double sinStart, cosStart;
|
||||
double startx, starty;
|
||||
|
||||
if (p->ended)
|
||||
userbug("You cannot call uiDrawPathNewFigureWithArc() on a uiDrawPath that has already been ended. (path; %p)", p);
|
||||
sinStart = sin(startAngle);
|
||||
cosStart = cos(startAngle);
|
||||
startx = xCenter + radius * cosStart;
|
||||
starty = yCenter + radius * sinStart;
|
||||
CGPathMoveToPoint(p->path, NULL, startx, starty);
|
||||
uiDrawPathArcTo(p, xCenter, yCenter, radius, startAngle, sweep, negative);
|
||||
}
|
||||
|
||||
void uiDrawPathLineTo(uiDrawPath *p, double x, double y)
|
||||
{
|
||||
// TODO refine this to require being in a path
|
||||
if (p->ended)
|
||||
implbug("attempt to add line to ended path in uiDrawPathLineTo()");
|
||||
CGPathAddLineToPoint(p->path, NULL, x, y);
|
||||
}
|
||||
|
||||
void uiDrawPathArcTo(uiDrawPath *p, double xCenter, double yCenter, double radius, double startAngle, double sweep, int negative)
|
||||
{
|
||||
bool cw;
|
||||
|
||||
// TODO likewise
|
||||
if (p->ended)
|
||||
implbug("attempt to add arc to ended path in uiDrawPathArcTo()");
|
||||
if (sweep > 2 * uiPi)
|
||||
sweep = 2 * uiPi;
|
||||
cw = false;
|
||||
if (negative)
|
||||
cw = true;
|
||||
CGPathAddArc(p->path, NULL,
|
||||
xCenter, yCenter,
|
||||
radius,
|
||||
startAngle, startAngle + sweep,
|
||||
cw);
|
||||
}
|
||||
|
||||
void uiDrawPathBezierTo(uiDrawPath *p, double c1x, double c1y, double c2x, double c2y, double endX, double endY)
|
||||
{
|
||||
// TODO likewise
|
||||
if (p->ended)
|
||||
implbug("attempt to add bezier to ended path in uiDrawPathBezierTo()");
|
||||
CGPathAddCurveToPoint(p->path, NULL,
|
||||
c1x, c1y,
|
||||
c2x, c2y,
|
||||
endX, endY);
|
||||
}
|
||||
|
||||
void uiDrawPathCloseFigure(uiDrawPath *p)
|
||||
{
|
||||
// TODO likewise
|
||||
if (p->ended)
|
||||
implbug("attempt to close figure of ended path in uiDrawPathCloseFigure()");
|
||||
CGPathCloseSubpath(p->path);
|
||||
}
|
||||
|
||||
void uiDrawPathAddRectangle(uiDrawPath *p, double x, double y, double width, double height)
|
||||
{
|
||||
if (p->ended)
|
||||
userbug("You cannot call uiDrawPathAddRectangle() on a uiDrawPath that has already been ended. (path; %p)", p);
|
||||
CGPathAddRect(p->path, NULL, CGRectMake(x, y, width, height));
|
||||
}
|
||||
|
||||
void uiDrawPathEnd(uiDrawPath *p)
|
||||
{
|
||||
p->ended = TRUE;
|
||||
}
|
||||
|
||||
struct uiDrawContext {
|
||||
CGContextRef c;
|
||||
CGFloat height; // needed for text; see below
|
||||
};
|
||||
|
||||
uiDrawContext *newContext(CGContextRef ctxt, CGFloat height)
|
||||
{
|
||||
uiDrawContext *c;
|
||||
|
||||
c = uiNew(uiDrawContext);
|
||||
c->c = ctxt;
|
||||
c->height = height;
|
||||
return c;
|
||||
}
|
||||
|
||||
void freeContext(uiDrawContext *c)
|
||||
{
|
||||
uiFree(c);
|
||||
}
|
||||
|
||||
// a stroke is identical to a fill of a stroked path
|
||||
// we need to do this in order to stroke with a gradient; see http://stackoverflow.com/a/25034854/3408572
|
||||
// doing this for other brushes works too
|
||||
void uiDrawStroke(uiDrawContext *c, uiDrawPath *path, uiDrawBrush *b, uiDrawStrokeParams *p)
|
||||
{
|
||||
CGLineCap cap;
|
||||
CGLineJoin join;
|
||||
CGPathRef dashPath;
|
||||
CGFloat *dashes;
|
||||
size_t i;
|
||||
uiDrawPath p2;
|
||||
|
||||
if (!path->ended)
|
||||
userbug("You cannot call uiDrawStroke() on a uiDrawPath that has not been ended. (path: %p)", path);
|
||||
|
||||
switch (p->Cap) {
|
||||
case uiDrawLineCapFlat:
|
||||
cap = kCGLineCapButt;
|
||||
break;
|
||||
case uiDrawLineCapRound:
|
||||
cap = kCGLineCapRound;
|
||||
break;
|
||||
case uiDrawLineCapSquare:
|
||||
cap = kCGLineCapSquare;
|
||||
break;
|
||||
}
|
||||
switch (p->Join) {
|
||||
case uiDrawLineJoinMiter:
|
||||
join = kCGLineJoinMiter;
|
||||
break;
|
||||
case uiDrawLineJoinRound:
|
||||
join = kCGLineJoinRound;
|
||||
break;
|
||||
case uiDrawLineJoinBevel:
|
||||
join = kCGLineJoinBevel;
|
||||
break;
|
||||
}
|
||||
|
||||
// create a temporary path identical to the previous one
|
||||
dashPath = (CGPathRef) path->path;
|
||||
if (p->NumDashes != 0) {
|
||||
dashes = (CGFloat *) uiAlloc(p->NumDashes * sizeof (CGFloat), "CGFloat[]");
|
||||
for (i = 0; i < p->NumDashes; i++)
|
||||
dashes[i] = p->Dashes[i];
|
||||
dashPath = CGPathCreateCopyByDashingPath(path->path,
|
||||
NULL,
|
||||
p->DashPhase,
|
||||
dashes,
|
||||
p->NumDashes);
|
||||
uiFree(dashes);
|
||||
}
|
||||
// the documentation is wrong: this produces a path suitable for calling CGPathCreateCopyByStrokingPath(), not for filling directly
|
||||
// the cast is safe; we never modify the CGPathRef and always cast it back to a CGPathRef anyway
|
||||
p2.path = (CGMutablePathRef) CGPathCreateCopyByStrokingPath(dashPath,
|
||||
NULL,
|
||||
p->Thickness,
|
||||
cap,
|
||||
join,
|
||||
p->MiterLimit);
|
||||
if (p->NumDashes != 0)
|
||||
CGPathRelease(dashPath);
|
||||
|
||||
// always draw stroke fills using the winding rule
|
||||
// otherwise intersecting figures won't draw correctly
|
||||
p2.fillMode = uiDrawFillModeWinding;
|
||||
p2.ended = path->ended;
|
||||
uiDrawFill(c, &p2, b);
|
||||
// and clean up
|
||||
CGPathRelease((CGPathRef) (p2.path));
|
||||
}
|
||||
|
||||
// for a solid fill, we can merely have Core Graphics fill directly
|
||||
static void fillSolid(CGContextRef ctxt, uiDrawPath *p, uiDrawBrush *b)
|
||||
{
|
||||
// TODO this uses DeviceRGB; switch to sRGB
|
||||
CGContextSetRGBFillColor(ctxt, b->R, b->G, b->B, b->A);
|
||||
switch (p->fillMode) {
|
||||
case uiDrawFillModeWinding:
|
||||
CGContextFillPath(ctxt);
|
||||
break;
|
||||
case uiDrawFillModeAlternate:
|
||||
CGContextEOFillPath(ctxt);
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
// for a gradient fill, we need to clip to the path and then draw the gradient
|
||||
// see http://stackoverflow.com/a/25034854/3408572
|
||||
static void fillGradient(CGContextRef ctxt, uiDrawPath *p, uiDrawBrush *b)
|
||||
{
|
||||
CGGradientRef gradient;
|
||||
CGColorSpaceRef colorspace;
|
||||
CGFloat *colors;
|
||||
CGFloat *locations;
|
||||
size_t i;
|
||||
|
||||
// gradients need a color space
|
||||
// for consistency with windows, use sRGB
|
||||
colorspace = CGColorSpaceCreateWithName(kCGColorSpaceSRGB);
|
||||
|
||||
// make the gradient
|
||||
colors = uiAlloc(b->NumStops * 4 * sizeof (CGFloat), "CGFloat[]");
|
||||
locations = uiAlloc(b->NumStops * sizeof (CGFloat), "CGFloat[]");
|
||||
for (i = 0; i < b->NumStops; i++) {
|
||||
colors[i * 4 + 0] = b->Stops[i].R;
|
||||
colors[i * 4 + 1] = b->Stops[i].G;
|
||||
colors[i * 4 + 2] = b->Stops[i].B;
|
||||
colors[i * 4 + 3] = b->Stops[i].A;
|
||||
locations[i] = b->Stops[i].Pos;
|
||||
}
|
||||
gradient = CGGradientCreateWithColorComponents(colorspace, colors, locations, b->NumStops);
|
||||
uiFree(locations);
|
||||
uiFree(colors);
|
||||
|
||||
// because we're mucking with clipping, we need to save the graphics state and restore it later
|
||||
CGContextSaveGState(ctxt);
|
||||
|
||||
// clip
|
||||
switch (p->fillMode) {
|
||||
case uiDrawFillModeWinding:
|
||||
CGContextClip(ctxt);
|
||||
break;
|
||||
case uiDrawFillModeAlternate:
|
||||
CGContextEOClip(ctxt);
|
||||
break;
|
||||
}
|
||||
|
||||
// draw the gradient
|
||||
switch (b->Type) {
|
||||
case uiDrawBrushTypeLinearGradient:
|
||||
CGContextDrawLinearGradient(ctxt,
|
||||
gradient,
|
||||
CGPointMake(b->X0, b->Y0),
|
||||
CGPointMake(b->X1, b->Y1),
|
||||
kCGGradientDrawsBeforeStartLocation | kCGGradientDrawsAfterEndLocation);
|
||||
break;
|
||||
case uiDrawBrushTypeRadialGradient:
|
||||
CGContextDrawRadialGradient(ctxt,
|
||||
gradient,
|
||||
CGPointMake(b->X0, b->Y0),
|
||||
// make the start circle radius 0 to make it a point
|
||||
0,
|
||||
CGPointMake(b->X1, b->Y1),
|
||||
b->OuterRadius,
|
||||
kCGGradientDrawsBeforeStartLocation | kCGGradientDrawsAfterEndLocation);
|
||||
break;
|
||||
}
|
||||
|
||||
// and clean up
|
||||
CGContextRestoreGState(ctxt);
|
||||
CGGradientRelease(gradient);
|
||||
CGColorSpaceRelease(colorspace);
|
||||
}
|
||||
|
||||
void uiDrawFill(uiDrawContext *c, uiDrawPath *path, uiDrawBrush *b)
|
||||
{
|
||||
if (!path->ended)
|
||||
userbug("You cannot call uiDrawStroke() on a uiDrawPath that has not been ended. (path: %p)", path);
|
||||
CGContextAddPath(c->c, (CGPathRef) (path->path));
|
||||
switch (b->Type) {
|
||||
case uiDrawBrushTypeSolid:
|
||||
fillSolid(c->c, path, b);
|
||||
return;
|
||||
case uiDrawBrushTypeLinearGradient:
|
||||
case uiDrawBrushTypeRadialGradient:
|
||||
fillGradient(c->c, path, b);
|
||||
return;
|
||||
// case uiDrawBrushTypeImage:
|
||||
// TODO
|
||||
return;
|
||||
}
|
||||
userbug("Unknown brush type %d passed to uiDrawFill().", b->Type);
|
||||
}
|
||||
|
||||
static void m2c(uiDrawMatrix *m, CGAffineTransform *c)
|
||||
{
|
||||
c->a = m->M11;
|
||||
c->b = m->M12;
|
||||
c->c = m->M21;
|
||||
c->d = m->M22;
|
||||
c->tx = m->M31;
|
||||
c->ty = m->M32;
|
||||
}
|
||||
|
||||
static void c2m(CGAffineTransform *c, uiDrawMatrix *m)
|
||||
{
|
||||
m->M11 = c->a;
|
||||
m->M12 = c->b;
|
||||
m->M21 = c->c;
|
||||
m->M22 = c->d;
|
||||
m->M31 = c->tx;
|
||||
m->M32 = c->ty;
|
||||
}
|
||||
|
||||
void uiDrawMatrixTranslate(uiDrawMatrix *m, double x, double y)
|
||||
{
|
||||
CGAffineTransform c;
|
||||
|
||||
m2c(m, &c);
|
||||
c = CGAffineTransformTranslate(c, x, y);
|
||||
c2m(&c, m);
|
||||
}
|
||||
|
||||
void uiDrawMatrixScale(uiDrawMatrix *m, double xCenter, double yCenter, double x, double y)
|
||||
{
|
||||
CGAffineTransform c;
|
||||
double xt, yt;
|
||||
|
||||
m2c(m, &c);
|
||||
xt = x;
|
||||
yt = y;
|
||||
scaleCenter(xCenter, yCenter, &xt, &yt);
|
||||
c = CGAffineTransformTranslate(c, xt, yt);
|
||||
c = CGAffineTransformScale(c, x, y);
|
||||
c = CGAffineTransformTranslate(c, -xt, -yt);
|
||||
c2m(&c, m);
|
||||
}
|
||||
|
||||
void uiDrawMatrixRotate(uiDrawMatrix *m, double x, double y, double amount)
|
||||
{
|
||||
CGAffineTransform c;
|
||||
|
||||
m2c(m, &c);
|
||||
c = CGAffineTransformTranslate(c, x, y);
|
||||
c = CGAffineTransformRotate(c, amount);
|
||||
c = CGAffineTransformTranslate(c, -x, -y);
|
||||
c2m(&c, m);
|
||||
}
|
||||
|
||||
void uiDrawMatrixSkew(uiDrawMatrix *m, double x, double y, double xamount, double yamount)
|
||||
{
|
||||
fallbackSkew(m, x, y, xamount, yamount);
|
||||
}
|
||||
|
||||
void uiDrawMatrixMultiply(uiDrawMatrix *dest, uiDrawMatrix *src)
|
||||
{
|
||||
CGAffineTransform c;
|
||||
CGAffineTransform d;
|
||||
|
||||
m2c(dest, &c);
|
||||
m2c(src, &d);
|
||||
c = CGAffineTransformConcat(c, d);
|
||||
c2m(&c, dest);
|
||||
}
|
||||
|
||||
// there is no test for invertibility; CGAffineTransformInvert() is merely documented as returning the matrix unchanged if it isn't invertible
|
||||
// therefore, special care must be taken to catch matrices who are their own inverses
|
||||
// TODO figure out which matrices these are and do so
|
||||
int uiDrawMatrixInvertible(uiDrawMatrix *m)
|
||||
{
|
||||
CGAffineTransform c, d;
|
||||
|
||||
m2c(m, &c);
|
||||
d = CGAffineTransformInvert(c);
|
||||
return CGAffineTransformEqualToTransform(c, d) == false;
|
||||
}
|
||||
|
||||
int uiDrawMatrixInvert(uiDrawMatrix *m)
|
||||
{
|
||||
CGAffineTransform c, d;
|
||||
|
||||
m2c(m, &c);
|
||||
d = CGAffineTransformInvert(c);
|
||||
if (CGAffineTransformEqualToTransform(c, d))
|
||||
return 0;
|
||||
c2m(&d, m);
|
||||
return 1;
|
||||
}
|
||||
|
||||
void uiDrawMatrixTransformPoint(uiDrawMatrix *m, double *x, double *y)
|
||||
{
|
||||
CGAffineTransform c;
|
||||
CGPoint p;
|
||||
|
||||
m2c(m, &c);
|
||||
p = CGPointApplyAffineTransform(CGPointMake(*x, *y), c);
|
||||
*x = p.x;
|
||||
*y = p.y;
|
||||
}
|
||||
|
||||
void uiDrawMatrixTransformSize(uiDrawMatrix *m, double *x, double *y)
|
||||
{
|
||||
CGAffineTransform c;
|
||||
CGSize s;
|
||||
|
||||
m2c(m, &c);
|
||||
s = CGSizeApplyAffineTransform(CGSizeMake(*x, *y), c);
|
||||
*x = s.width;
|
||||
*y = s.height;
|
||||
}
|
||||
|
||||
void uiDrawTransform(uiDrawContext *c, uiDrawMatrix *m)
|
||||
{
|
||||
CGAffineTransform cm;
|
||||
|
||||
m2c(m, &cm);
|
||||
CGContextConcatCTM(c->c, cm);
|
||||
}
|
||||
|
||||
void uiDrawClip(uiDrawContext *c, uiDrawPath *path)
|
||||
{
|
||||
if (!path->ended)
|
||||
userbug("You cannot call uiDrawCilp() on a uiDrawPath that has not been ended. (path: %p)", path);
|
||||
CGContextAddPath(c->c, (CGPathRef) (path->path));
|
||||
switch (path->fillMode) {
|
||||
case uiDrawFillModeWinding:
|
||||
CGContextClip(c->c);
|
||||
break;
|
||||
case uiDrawFillModeAlternate:
|
||||
CGContextEOClip(c->c);
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
// TODO figure out what besides transforms these save/restore on all platforms
|
||||
void uiDrawSave(uiDrawContext *c)
|
||||
{
|
||||
CGContextSaveGState(c->c);
|
||||
}
|
||||
|
||||
void uiDrawRestore(uiDrawContext *c)
|
||||
{
|
||||
CGContextRestoreGState(c->c);
|
||||
}
|
||||
|
||||
void uiDrawText(uiDrawContext *c, double x, double y, uiDrawTextLayout *layout)
|
||||
{
|
||||
doDrawText(c->c, c->height, x, y, layout);
|
||||
}
|
||||
Vendored
+655
@@ -0,0 +1,655 @@
|
||||
// 6 september 2015
|
||||
#import "uipriv_darwin.h"
|
||||
|
||||
// TODO
|
||||
#define complain(...) implbug(__VA_ARGS__)
|
||||
|
||||
// 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
|
||||
|
||||
// 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
|
||||
static const CGFloat ctWeights[] = {
|
||||
// yeah these two have their names swapped; blame Pango
|
||||
[uiDrawTextWeightThin] = ourNSFontWeightUltraLight,
|
||||
[uiDrawTextWeightUltraLight] = ourNSFontWeightThin,
|
||||
[uiDrawTextWeightLight] = ourNSFontWeightLight,
|
||||
// for this one let's go between Light and Regular
|
||||
// we're doing nearest so if there happens to be an exact value hopefully it's close enough
|
||||
[uiDrawTextWeightBook] = ourNSFontWeightLight + ((ourNSFontWeightRegular - ourNSFontWeightLight) / 2),
|
||||
[uiDrawTextWeightNormal] = ourNSFontWeightRegular,
|
||||
[uiDrawTextWeightMedium] = ourNSFontWeightMedium,
|
||||
[uiDrawTextWeightSemiBold] = ourNSFontWeightSemibold,
|
||||
[uiDrawTextWeightBold] = ourNSFontWeightBold,
|
||||
// for this one let's go between Bold and Heavy
|
||||
[uiDrawTextWeightUltraBold] = ourNSFontWeightBold + ((ourNSFontWeightHeavy - ourNSFontWeightBold) / 2),
|
||||
[uiDrawTextWeightHeavy] = ourNSFontWeightHeavy,
|
||||
[uiDrawTextWeightUltraHeavy] = ourNSFontWeightBlack,
|
||||
};
|
||||
|
||||
// Unfortunately there are still no named constants for these.
|
||||
// Let's just use normalized widths.
|
||||
// As far as I can tell (OS X only ships with condensed fonts, not expanded fonts; TODO), regardless of condensed or expanded, negative means condensed and positive means expanded.
|
||||
// TODO verify this is correct
|
||||
static const CGFloat ctStretches[] = {
|
||||
[uiDrawTextStretchUltraCondensed] = -1.0,
|
||||
[uiDrawTextStretchExtraCondensed] = -0.75,
|
||||
[uiDrawTextStretchCondensed] = -0.5,
|
||||
[uiDrawTextStretchSemiCondensed] = -0.25,
|
||||
[uiDrawTextStretchNormal] = 0.0,
|
||||
[uiDrawTextStretchSemiExpanded] = 0.25,
|
||||
[uiDrawTextStretchExpanded] = 0.5,
|
||||
[uiDrawTextStretchExtraExpanded] = 0.75,
|
||||
[uiDrawTextStretchUltraExpanded] = 1.0,
|
||||
};
|
||||
|
||||
struct closeness {
|
||||
CFIndex index;
|
||||
CGFloat weight;
|
||||
CGFloat italic;
|
||||
CGFloat stretch;
|
||||
CGFloat distance;
|
||||
};
|
||||
|
||||
// Stupidity: CTFont requires an **exact match for the entire traits dictionary**, otherwise it will **drop ALL the traits**.
|
||||
// We have to implement the closest match ourselves.
|
||||
// Also we have to do this before adding the small caps flags, because the matching descriptors won't have those.
|
||||
CTFontDescriptorRef matchTraits(CTFontDescriptorRef against, uiDrawTextWeight weight, uiDrawTextItalic italic, uiDrawTextStretch stretch)
|
||||
{
|
||||
CGFloat targetWeight;
|
||||
CGFloat italicCloseness, obliqueCloseness, normalCloseness;
|
||||
CGFloat targetStretch;
|
||||
CFArrayRef matching;
|
||||
CFIndex i, n;
|
||||
struct closeness *closeness;
|
||||
CTFontDescriptorRef current;
|
||||
CTFontDescriptorRef out;
|
||||
|
||||
targetWeight = ctWeights[weight];
|
||||
switch (italic) {
|
||||
case uiDrawTextItalicNormal:
|
||||
italicCloseness = 1;
|
||||
obliqueCloseness = 1;
|
||||
normalCloseness = 0;
|
||||
break;
|
||||
case uiDrawTextItalicOblique:
|
||||
italicCloseness = 0.5;
|
||||
obliqueCloseness = 0;
|
||||
normalCloseness = 1;
|
||||
break;
|
||||
case uiDrawTextItalicItalic:
|
||||
italicCloseness = 0;
|
||||
obliqueCloseness = 0.5;
|
||||
normalCloseness = 1;
|
||||
break;
|
||||
}
|
||||
targetStretch = ctStretches[stretch];
|
||||
|
||||
matching = CTFontDescriptorCreateMatchingFontDescriptors(against, NULL);
|
||||
if (matching == NULL)
|
||||
// no matches; give the original back and hope for the best
|
||||
return against;
|
||||
n = CFArrayGetCount(matching);
|
||||
if (n == 0) {
|
||||
// likewise
|
||||
CFRelease(matching);
|
||||
return against;
|
||||
}
|
||||
|
||||
closeness = (struct closeness *) uiAlloc(n * sizeof (struct closeness), "struct closeness[]");
|
||||
for (i = 0; i < n; i++) {
|
||||
CFDictionaryRef traits;
|
||||
CFNumberRef cfnum;
|
||||
CTFontSymbolicTraits symbolic;
|
||||
|
||||
closeness[i].index = i;
|
||||
|
||||
current = CFArrayGetValueAtIndex(matching, i);
|
||||
traits = CTFontDescriptorCopyAttribute(current, kCTFontTraitsAttribute);
|
||||
if (traits == NULL) {
|
||||
// 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;
|
||||
}
|
||||
|
||||
symbolic = 0; // assume no symbolic traits if none are listed
|
||||
cfnum = CFDictionaryGetValue(traits, kCTFontSymbolicTrait);
|
||||
if (cfnum != NULL) {
|
||||
SInt32 s;
|
||||
|
||||
if (CFNumberGetValue(cfnum, kCFNumberSInt32Type, &s) == false)
|
||||
complain("error getting symbolic traits in matchTraits()");
|
||||
symbolic = (CTFontSymbolicTraits) s;
|
||||
// Get rule; do not release cfnum
|
||||
}
|
||||
|
||||
// now try weight
|
||||
cfnum = CFDictionaryGetValue(traits, kCTFontWeightTrait);
|
||||
if (cfnum != NULL) {
|
||||
CGFloat val;
|
||||
|
||||
// LONGTERM instead of complaining for this and width and possibly also symbolic traits above, should we just fall through to the default?
|
||||
if (CFNumberGetValue(cfnum, kCFNumberCGFloatType, &val) == false)
|
||||
complain("error getting weight value in matchTraits()");
|
||||
closeness[i].weight = val - targetWeight;
|
||||
} else
|
||||
// okay there's no weight key; let's try the literal meaning of the symbolic constant
|
||||
// LONGTERM is the weight key guaranteed?
|
||||
if ((symbolic & kCTFontBoldTrait) != 0)
|
||||
closeness[i].weight = ourNSFontWeightBold - targetWeight;
|
||||
else
|
||||
closeness[i].weight = ourNSFontWeightRegular - targetWeight;
|
||||
|
||||
// italics is a bit harder because Core Text doesn't expose a concept of obliqueness
|
||||
// Pango 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
|
||||
if ((symbolic & kCTFontItalicTrait) != 0)
|
||||
closeness[i].italic = italicCloseness;
|
||||
else {
|
||||
CFStringRef styleName;
|
||||
BOOL isOblique;
|
||||
|
||||
isOblique = NO; // default value
|
||||
styleName = CTFontDescriptorCopyAttribute(current, kCTFontStyleNameAttribute);
|
||||
if (styleName != NULL) {
|
||||
CFRange range;
|
||||
|
||||
// note the use of the toll-free bridge for the string literal, since CFSTR() *can* return NULL
|
||||
range = CFStringFind(styleName, (CFStringRef) @"Oblique", kCFCompareBackwards);
|
||||
if (range.location != kCFNotFound)
|
||||
isOblique = YES;
|
||||
CFRelease(styleName);
|
||||
}
|
||||
if (isOblique)
|
||||
closeness[i].italic = obliqueCloseness;
|
||||
else
|
||||
closeness[i].italic = normalCloseness;
|
||||
}
|
||||
|
||||
// now try width
|
||||
// TODO this does not seem to be enough for Skia's extended variants; the width trait is 0 but the Expanded flag is on
|
||||
// TODO verify the rest of this matrix (what matrix?)
|
||||
cfnum = CFDictionaryGetValue(traits, kCTFontWidthTrait);
|
||||
if (cfnum != NULL) {
|
||||
CGFloat val;
|
||||
|
||||
if (CFNumberGetValue(cfnum, kCFNumberCGFloatType, &val) == false)
|
||||
complain("error getting width value in matchTraits()");
|
||||
closeness[i].stretch = val - targetStretch;
|
||||
} else
|
||||
// okay there's no width key; let's try the literal meaning of the symbolic constant
|
||||
// LONGTERM is the width key guaranteed?
|
||||
if ((symbolic & kCTFontExpandedTrait) != 0)
|
||||
closeness[i].stretch = 1.0 - targetStretch;
|
||||
else if ((symbolic & kCTFontCondensedTrait) != 0)
|
||||
closeness[i].stretch = -1.0 - targetStretch;
|
||||
else
|
||||
closeness[i].stretch = 0.0 - targetStretch;
|
||||
|
||||
CFRelease(traits);
|
||||
}
|
||||
|
||||
// now figure out the 3-space difference between the three and sort by that
|
||||
for (i = 0; i < n; i++) {
|
||||
CGFloat 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 = CFArrayGetValueAtIndex(matching, closeness[0].index);
|
||||
CFRetain(out); // get rule
|
||||
|
||||
// release everything
|
||||
uiFree(closeness);
|
||||
CFRelease(matching);
|
||||
// and release the original descriptor since we no longer need it
|
||||
CFRelease(against);
|
||||
|
||||
return out;
|
||||
}
|
||||
|
||||
// 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);
|
||||
}
|
||||
|
||||
struct uiDrawTextLayout {
|
||||
CFMutableAttributedStringRef mas;
|
||||
CFRange *charsToRanges;
|
||||
double width;
|
||||
};
|
||||
|
||||
uiDrawTextLayout *uiDrawNewTextLayout(const char *str, uiDrawTextFont *defaultFont, double width)
|
||||
{
|
||||
uiDrawTextLayout *layout;
|
||||
CFAttributedStringRef immutable;
|
||||
CFMutableDictionaryRef attr;
|
||||
CFStringRef backing;
|
||||
CFIndex i, j, n;
|
||||
|
||||
layout = uiNew(uiDrawTextLayout);
|
||||
|
||||
// TODO docs say we need to use a different set of key callbacks
|
||||
// TODO see if the font attribute key callbacks need to be the same
|
||||
attr = newAttrList();
|
||||
// this will retain defaultFont->f; no need to worry
|
||||
CFDictionaryAddValue(attr, kCTFontAttributeName, defaultFont->f);
|
||||
|
||||
immutable = CFAttributedStringCreate(NULL, (CFStringRef) [NSString stringWithUTF8String:str], attr);
|
||||
if (immutable == NULL)
|
||||
complain("error creating immutable attributed string in uiDrawNewTextLayout()");
|
||||
CFRelease(attr);
|
||||
|
||||
layout->mas = CFAttributedStringCreateMutableCopy(NULL, 0, immutable);
|
||||
if (layout->mas == NULL)
|
||||
complain("error creating attributed string in uiDrawNewTextLayout()");
|
||||
CFRelease(immutable);
|
||||
|
||||
uiDrawTextLayoutSetWidth(layout, width);
|
||||
|
||||
// unfortunately the CFRanges for attributes expect UTF-16 codepoints
|
||||
// we want graphemes
|
||||
// fortunately CFStringGetRangeOfComposedCharactersAtIndex() is here for us
|
||||
// https://developer.apple.com/library/mac/documentation/Cocoa/Conceptual/Strings/Articles/stringsClusters.html says that this does work on all multi-codepoint graphemes (despite the name), and that this is the preferred function for this particular job anyway
|
||||
backing = CFAttributedStringGetString(layout->mas);
|
||||
n = CFStringGetLength(backing);
|
||||
// allocate one extra, just to be safe
|
||||
layout->charsToRanges = (CFRange *) uiAlloc((n + 1) * sizeof (CFRange), "CFRange[]");
|
||||
i = 0;
|
||||
j = 0;
|
||||
while (i < n) {
|
||||
CFRange range;
|
||||
|
||||
range = CFStringGetRangeOfComposedCharactersAtIndex(backing, i);
|
||||
i = range.location + range.length;
|
||||
layout->charsToRanges[j] = range;
|
||||
j++;
|
||||
}
|
||||
// and set the last one
|
||||
layout->charsToRanges[j].location = i;
|
||||
layout->charsToRanges[j].length = 0;
|
||||
|
||||
return layout;
|
||||
}
|
||||
|
||||
void uiDrawFreeTextLayout(uiDrawTextLayout *layout)
|
||||
{
|
||||
uiFree(layout->charsToRanges);
|
||||
CFRelease(layout->mas);
|
||||
uiFree(layout);
|
||||
}
|
||||
|
||||
void uiDrawTextLayoutSetWidth(uiDrawTextLayout *layout, double width)
|
||||
{
|
||||
layout->width = width;
|
||||
}
|
||||
|
||||
struct framesetter {
|
||||
CTFramesetterRef fs;
|
||||
CFMutableDictionaryRef frameAttrib;
|
||||
CGSize extents;
|
||||
};
|
||||
|
||||
// TODO CTFrameProgression for RTL/LTR
|
||||
// TODO kCTParagraphStyleSpecifierMaximumLineSpacing, kCTParagraphStyleSpecifierMinimumLineSpacing, kCTParagraphStyleSpecifierLineSpacingAdjustment for line spacing
|
||||
static void mkFramesetter(uiDrawTextLayout *layout, struct framesetter *fs)
|
||||
{
|
||||
CFRange fitRange;
|
||||
CGFloat width;
|
||||
|
||||
fs->fs = CTFramesetterCreateWithAttributedString(layout->mas);
|
||||
if (fs->fs == NULL)
|
||||
complain("error creating CTFramesetter object in mkFramesetter()");
|
||||
|
||||
// TODO kCTFramePathWidthAttributeName?
|
||||
fs->frameAttrib = NULL;
|
||||
|
||||
width = layout->width;
|
||||
if (layout->width < 0)
|
||||
width = CGFLOAT_MAX;
|
||||
// TODO these seem to be floor()'d or truncated?
|
||||
fs->extents = CTFramesetterSuggestFrameSizeWithConstraints(fs->fs,
|
||||
CFRangeMake(0, 0),
|
||||
fs->frameAttrib,
|
||||
CGSizeMake(width, CGFLOAT_MAX),
|
||||
&fitRange); // not documented as accepting NULL
|
||||
}
|
||||
|
||||
static void freeFramesetter(struct framesetter *fs)
|
||||
{
|
||||
if (fs->frameAttrib != NULL)
|
||||
CFRelease(fs->frameAttrib);
|
||||
CFRelease(fs->fs);
|
||||
}
|
||||
|
||||
// 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);
|
||||
}
|
||||
|
||||
// Core Text doesn't draw onto a flipped view correctly; we have to do this
|
||||
// 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 the CTM?
|
||||
static void prepareContextForText(CGContextRef c, CGFloat cheight, double *y)
|
||||
{
|
||||
CGContextSaveGState(c);
|
||||
CGContextTranslateCTM(c, 0, cheight);
|
||||
CGContextScaleCTM(c, 1.0, -1.0);
|
||||
CGContextSetTextMatrix(c, CGAffineTransformIdentity);
|
||||
|
||||
// wait, that's not enough; we need to offset y values to account for our new flipping
|
||||
*y = cheight - *y;
|
||||
}
|
||||
|
||||
// TODO placement is incorrect for Helvetica
|
||||
void doDrawText(CGContextRef c, CGFloat cheight, double x, double y, uiDrawTextLayout *layout)
|
||||
{
|
||||
struct framesetter fs;
|
||||
CGRect rect;
|
||||
CGPathRef path;
|
||||
CTFrameRef frame;
|
||||
|
||||
prepareContextForText(c, cheight, &y);
|
||||
mkFramesetter(layout, &fs);
|
||||
|
||||
// oh, and since we're flipped, y is the bottom-left coordinate of the rectangle, not the top-left
|
||||
// since we are flipped, we subtract
|
||||
y -= fs.extents.height;
|
||||
|
||||
rect.origin = CGPointMake(x, y);
|
||||
rect.size = fs.extents;
|
||||
path = CGPathCreateWithRect(rect, NULL);
|
||||
|
||||
frame = CTFramesetterCreateFrame(fs.fs,
|
||||
CFRangeMake(0, 0),
|
||||
path,
|
||||
fs.frameAttrib);
|
||||
if (frame == NULL)
|
||||
complain("error creating CTFrame object in doDrawText()");
|
||||
CTFrameDraw(frame, c);
|
||||
CFRelease(frame);
|
||||
|
||||
CFRelease(path);
|
||||
|
||||
freeFramesetter(&fs);
|
||||
CGContextRestoreGState(c);
|
||||
}
|
||||
|
||||
// LONGTERM provide an equivalent to CTLineGetTypographicBounds() on uiDrawTextLayout?
|
||||
|
||||
// LONGTERM keep this for later features and documentation purposes
|
||||
#if 0
|
||||
w = CTLineGetTypographicBounds(line, &ascent, &descent, NULL);
|
||||
// though CTLineGetTypographicBounds() returns 0 on error, it also returns 0 on an empty string, so we can't reasonably check for error
|
||||
CFRelease(line);
|
||||
|
||||
// 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
|
||||
|
||||
static CFRange charsToRange(uiDrawTextLayout *layout, int startChar, int endChar)
|
||||
{
|
||||
CFRange start, end;
|
||||
CFRange out;
|
||||
|
||||
start = layout->charsToRanges[startChar];
|
||||
end = layout->charsToRanges[endChar];
|
||||
out.location = start.location;
|
||||
out.length = end.location - start.location;
|
||||
return out;
|
||||
}
|
||||
|
||||
#define rangeToCFRange() charsToRange(layout, startChar, endChar)
|
||||
|
||||
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?
|
||||
}
|
||||
Vendored
+185
@@ -0,0 +1,185 @@
|
||||
// 14 august 2015
|
||||
#import "uipriv_darwin.h"
|
||||
|
||||
// So why did I split uiCombobox into uiCombobox and uiEditableCombobox? Here's (90% of the; the other 10% is GTK+ events) answer:
|
||||
// When you type a value into a NSComboBox that just happens to be in the list, it will autoselect that item!
|
||||
// I can't seem to find a workaround.
|
||||
// Fortunately, there's other weird behaviors that made this split worth it.
|
||||
// And besides, selected items make little sense with editable comboboxes... you either separate or combine them with the text entry :V
|
||||
|
||||
// NSComboBoxes have no intrinsic width; we'll use the default Interface Builder width for them.
|
||||
#define comboboxWidth 96
|
||||
|
||||
@interface libui_intrinsicWidthNSComboBox : NSComboBox
|
||||
@end
|
||||
|
||||
@implementation libui_intrinsicWidthNSComboBox
|
||||
|
||||
- (NSSize)intrinsicContentSize
|
||||
{
|
||||
NSSize s;
|
||||
|
||||
s = [super intrinsicContentSize];
|
||||
s.width = comboboxWidth;
|
||||
return s;
|
||||
}
|
||||
|
||||
@end
|
||||
|
||||
struct uiEditableCombobox {
|
||||
uiDarwinControl c;
|
||||
NSComboBox *cb;
|
||||
void (*onChanged)(uiEditableCombobox *, void *);
|
||||
void *onChangedData;
|
||||
};
|
||||
|
||||
@interface editableComboboxDelegateClass : NSObject<NSComboBoxDelegate> {
|
||||
struct mapTable *comboboxes;
|
||||
}
|
||||
- (void)controlTextDidChange:(NSNotification *)note;
|
||||
- (void)comboBoxSelectionDidChange:(NSNotification *)note;
|
||||
- (void)registerCombobox:(uiEditableCombobox *)c;
|
||||
- (void)unregisterCombobox:(uiEditableCombobox *)c;
|
||||
@end
|
||||
|
||||
@implementation editableComboboxDelegateClass
|
||||
|
||||
- (id)init
|
||||
{
|
||||
self = [super init];
|
||||
if (self)
|
||||
self->comboboxes = newMap();
|
||||
return self;
|
||||
}
|
||||
|
||||
- (void)dealloc
|
||||
{
|
||||
mapDestroy(self->comboboxes);
|
||||
[super dealloc];
|
||||
}
|
||||
|
||||
- (void)controlTextDidChange:(NSNotification *)note
|
||||
{
|
||||
uiEditableCombobox *c;
|
||||
|
||||
c = uiEditableCombobox(mapGet(self->comboboxes, [note object]));
|
||||
(*(c->onChanged))(c, c->onChangedData);
|
||||
}
|
||||
|
||||
// the above doesn't handle when an item is selected; this will
|
||||
- (void)comboBoxSelectionDidChange:(NSNotification *)note
|
||||
{
|
||||
// except this is sent BEFORE the entry is changed, and that doesn't send the above, so
|
||||
// this is via http://stackoverflow.com/a/21059819/3408572 - it avoids the need to manage selected items
|
||||
// this still isn't perfect — I get residual changes to the same value while navigating the list — but it's good enough
|
||||
[self performSelector:@selector(controlTextDidChange:)
|
||||
withObject:note
|
||||
afterDelay:0];
|
||||
}
|
||||
|
||||
- (void)registerCombobox:(uiEditableCombobox *)c
|
||||
{
|
||||
mapSet(self->comboboxes, c->cb, c);
|
||||
[c->cb setDelegate:self];
|
||||
}
|
||||
|
||||
- (void)unregisterCombobox:(uiEditableCombobox *)c
|
||||
{
|
||||
[c->cb setDelegate:nil];
|
||||
mapDelete(self->comboboxes, c->cb);
|
||||
}
|
||||
|
||||
@end
|
||||
|
||||
static editableComboboxDelegateClass *comboboxDelegate = nil;
|
||||
|
||||
uiDarwinControlAllDefaultsExceptDestroy(uiEditableCombobox, cb)
|
||||
|
||||
static void uiEditableComboboxDestroy(uiControl *cc)
|
||||
{
|
||||
uiEditableCombobox *c = uiEditableCombobox(cc);
|
||||
|
||||
[comboboxDelegate unregisterCombobox:c];
|
||||
[c->cb release];
|
||||
uiFreeControl(uiControl(c));
|
||||
}
|
||||
|
||||
void uiEditableComboboxAppend(uiEditableCombobox *c, const char *text)
|
||||
{
|
||||
[c->cb addItemWithObjectValue:toNSString(text)];
|
||||
}
|
||||
|
||||
char *uiEditableComboboxText(uiEditableCombobox *c)
|
||||
{
|
||||
return uiDarwinNSStringToText([c->cb stringValue]);
|
||||
}
|
||||
|
||||
void uiEditableComboboxSetText(uiEditableCombobox *c, const char *text)
|
||||
{
|
||||
NSString *t;
|
||||
|
||||
t = toNSString(text);
|
||||
[c->cb setStringValue:t];
|
||||
// yes, let's imitate the behavior that caused uiEditableCombobox to be separate in the first place!
|
||||
// just to avoid confusion when users see an option in the list in the text field but not selected in the list
|
||||
[c->cb selectItemWithObjectValue:t];
|
||||
}
|
||||
|
||||
#if 0
|
||||
// LONGTERM
|
||||
void uiEditableComboboxSetSelected(uiEditableCombobox *c, int n)
|
||||
{
|
||||
if (c->editable) {
|
||||
// see https://developer.apple.com/library/mac/documentation/Cocoa/Conceptual/ComboBox/Tasks/SettingComboBoxValue.html#//apple_ref/doc/uid/20000256
|
||||
id delegate;
|
||||
|
||||
// this triggers the delegate; turn it off for now
|
||||
delegate = [c->cb delegate];
|
||||
[c->cb setDelegate:nil];
|
||||
|
||||
// this seems to work fine for -1 too
|
||||
[c->cb selectItemAtIndex:n];
|
||||
if (n == -1)
|
||||
[c->cb setObjectValue:@""];
|
||||
else
|
||||
[c->cb setObjectValue:[c->cb objectValueOfSelectedItem]];
|
||||
|
||||
[c->cb setDelegate:delegate];
|
||||
return;
|
||||
}
|
||||
[c->pb selectItemAtIndex:n];
|
||||
}
|
||||
#endif
|
||||
|
||||
void uiEditableComboboxOnChanged(uiEditableCombobox *c, void (*f)(uiEditableCombobox *c, void *data), void *data)
|
||||
{
|
||||
c->onChanged = f;
|
||||
c->onChangedData = data;
|
||||
}
|
||||
|
||||
static void defaultOnChanged(uiEditableCombobox *c, void *data)
|
||||
{
|
||||
// do nothing
|
||||
}
|
||||
|
||||
uiEditableCombobox *uiNewEditableCombobox(void)
|
||||
{
|
||||
uiEditableCombobox *c;
|
||||
|
||||
uiDarwinNewControl(uiEditableCombobox, c);
|
||||
|
||||
c->cb = [[libui_intrinsicWidthNSComboBox alloc] initWithFrame:NSZeroRect];
|
||||
[c->cb setUsesDataSource:NO];
|
||||
[c->cb setButtonBordered:YES];
|
||||
[c->cb setCompletes:NO];
|
||||
uiDarwinSetControlFont(c->cb, NSRegularControlSize);
|
||||
|
||||
if (comboboxDelegate == nil) {
|
||||
comboboxDelegate = [[editableComboboxDelegateClass new] autorelease];
|
||||
[delegates addObject:comboboxDelegate];
|
||||
}
|
||||
[comboboxDelegate registerCombobox:c];
|
||||
uiEditableComboboxOnChanged(c, defaultOnChanged, NULL);
|
||||
|
||||
return c;
|
||||
}
|
||||
Vendored
+251
@@ -0,0 +1,251 @@
|
||||
// 14 august 2015
|
||||
#import "uipriv_darwin.h"
|
||||
|
||||
// Text fields for entering text have no intrinsic width; we'll use the default Interface Builder width for them.
|
||||
#define textfieldWidth 96
|
||||
|
||||
@interface libui_intrinsicWidthNSTextField : NSTextField
|
||||
@end
|
||||
|
||||
@implementation libui_intrinsicWidthNSTextField
|
||||
|
||||
- (NSSize)intrinsicContentSize
|
||||
{
|
||||
NSSize s;
|
||||
|
||||
s = [super intrinsicContentSize];
|
||||
s.width = textfieldWidth;
|
||||
return s;
|
||||
}
|
||||
|
||||
@end
|
||||
|
||||
// TODO does this have one on its own?
|
||||
@interface libui_intrinsicWidthNSSecureTextField : NSSecureTextField
|
||||
@end
|
||||
|
||||
@implementation libui_intrinsicWidthNSSecureTextField
|
||||
|
||||
- (NSSize)intrinsicContentSize
|
||||
{
|
||||
NSSize s;
|
||||
|
||||
s = [super intrinsicContentSize];
|
||||
s.width = textfieldWidth;
|
||||
return s;
|
||||
}
|
||||
|
||||
@end
|
||||
|
||||
// TODO does this have one on its own?
|
||||
@interface libui_intrinsicWidthNSSearchField : NSSearchField
|
||||
@end
|
||||
|
||||
@implementation libui_intrinsicWidthNSSearchField
|
||||
|
||||
- (NSSize)intrinsicContentSize
|
||||
{
|
||||
NSSize s;
|
||||
|
||||
s = [super intrinsicContentSize];
|
||||
s.width = textfieldWidth;
|
||||
return s;
|
||||
}
|
||||
|
||||
@end
|
||||
|
||||
struct uiEntry {
|
||||
uiDarwinControl c;
|
||||
NSTextField *textfield;
|
||||
void (*onChanged)(uiEntry *, void *);
|
||||
void *onChangedData;
|
||||
};
|
||||
|
||||
static BOOL isSearchField(NSTextField *tf)
|
||||
{
|
||||
return [tf isKindOfClass:[NSSearchField class]];
|
||||
}
|
||||
|
||||
@interface entryDelegateClass : NSObject<NSTextFieldDelegate> {
|
||||
struct mapTable *entries;
|
||||
}
|
||||
- (void)controlTextDidChange:(NSNotification *)note;
|
||||
- (IBAction)onSearch:(id)sender;
|
||||
- (void)registerEntry:(uiEntry *)e;
|
||||
- (void)unregisterEntry:(uiEntry *)e;
|
||||
@end
|
||||
|
||||
@implementation entryDelegateClass
|
||||
|
||||
- (id)init
|
||||
{
|
||||
self = [super init];
|
||||
if (self)
|
||||
self->entries = newMap();
|
||||
return self;
|
||||
}
|
||||
|
||||
- (void)dealloc
|
||||
{
|
||||
mapDestroy(self->entries);
|
||||
[super dealloc];
|
||||
}
|
||||
|
||||
- (void)controlTextDidChange:(NSNotification *)note
|
||||
{
|
||||
[self onSearch:[note object]];
|
||||
}
|
||||
|
||||
- (IBAction)onSearch:(id)sender
|
||||
{
|
||||
uiEntry *e;
|
||||
|
||||
e = (uiEntry *) mapGet(self->entries, sender);
|
||||
(*(e->onChanged))(e, e->onChangedData);
|
||||
}
|
||||
|
||||
- (void)registerEntry:(uiEntry *)e
|
||||
{
|
||||
mapSet(self->entries, e->textfield, e);
|
||||
if (isSearchField(e->textfield)) {
|
||||
[e->textfield setTarget:self];
|
||||
[e->textfield setAction:@selector(onSearch:)];
|
||||
} else
|
||||
[e->textfield setDelegate:self];
|
||||
}
|
||||
|
||||
- (void)unregisterEntry:(uiEntry *)e
|
||||
{
|
||||
if (isSearchField(e->textfield))
|
||||
[e->textfield setTarget:nil];
|
||||
else
|
||||
[e->textfield setDelegate:nil];
|
||||
mapDelete(self->entries, e->textfield);
|
||||
}
|
||||
|
||||
@end
|
||||
|
||||
static entryDelegateClass *entryDelegate = nil;
|
||||
|
||||
uiDarwinControlAllDefaultsExceptDestroy(uiEntry, textfield)
|
||||
|
||||
static void uiEntryDestroy(uiControl *c)
|
||||
{
|
||||
uiEntry *e = uiEntry(c);
|
||||
|
||||
[entryDelegate unregisterEntry:e];
|
||||
[e->textfield release];
|
||||
uiFreeControl(uiControl(e));
|
||||
}
|
||||
|
||||
char *uiEntryText(uiEntry *e)
|
||||
{
|
||||
return uiDarwinNSStringToText([e->textfield stringValue]);
|
||||
}
|
||||
|
||||
void uiEntrySetText(uiEntry *e, const char *text)
|
||||
{
|
||||
[e->textfield setStringValue:toNSString(text)];
|
||||
// don't queue the control for resize; entry sizes are independent of their contents
|
||||
}
|
||||
|
||||
void uiEntryOnChanged(uiEntry *e, void (*f)(uiEntry *, void *), void *data)
|
||||
{
|
||||
e->onChanged = f;
|
||||
e->onChangedData = data;
|
||||
}
|
||||
|
||||
int uiEntryReadOnly(uiEntry *e)
|
||||
{
|
||||
return [e->textfield isEditable] == NO;
|
||||
}
|
||||
|
||||
void uiEntrySetReadOnly(uiEntry *e, int readonly)
|
||||
{
|
||||
BOOL editable;
|
||||
|
||||
editable = YES;
|
||||
if (readonly)
|
||||
editable = NO;
|
||||
[e->textfield setEditable:editable];
|
||||
}
|
||||
|
||||
static void defaultOnChanged(uiEntry *e, void *data)
|
||||
{
|
||||
// do nothing
|
||||
}
|
||||
|
||||
// these are based on interface builder defaults; my comments in the old code weren't very good so I don't really know what talked about what, sorry :/
|
||||
void finishNewTextField(NSTextField *t, BOOL isEntry)
|
||||
{
|
||||
uiDarwinSetControlFont(t, NSRegularControlSize);
|
||||
|
||||
// THE ORDER OF THESE CALLS IS IMPORTANT; CHANGE IT AND THE BORDERS WILL DISAPPEAR
|
||||
[t setBordered:NO];
|
||||
[t setBezelStyle:NSTextFieldSquareBezel];
|
||||
[t setBezeled:isEntry];
|
||||
|
||||
// we don't need to worry about substitutions/autocorrect here; see window_darwin.m for details
|
||||
|
||||
[[t cell] setLineBreakMode:NSLineBreakByClipping];
|
||||
[[t cell] setScrollable:YES];
|
||||
}
|
||||
|
||||
static NSTextField *realNewEditableTextField(Class class)
|
||||
{
|
||||
NSTextField *tf;
|
||||
|
||||
tf = [[class alloc] initWithFrame:NSZeroRect];
|
||||
[tf setSelectable:YES]; // otherwise the setting is masked by the editable default of YES
|
||||
finishNewTextField(tf, YES);
|
||||
return tf;
|
||||
}
|
||||
|
||||
NSTextField *newEditableTextField(void)
|
||||
{
|
||||
return realNewEditableTextField([libui_intrinsicWidthNSTextField class]);
|
||||
}
|
||||
|
||||
static uiEntry *finishNewEntry(Class class)
|
||||
{
|
||||
uiEntry *e;
|
||||
|
||||
uiDarwinNewControl(uiEntry, e);
|
||||
|
||||
e->textfield = realNewEditableTextField(class);
|
||||
|
||||
if (entryDelegate == nil) {
|
||||
entryDelegate = [[entryDelegateClass new] autorelease];
|
||||
[delegates addObject:entryDelegate];
|
||||
}
|
||||
[entryDelegate registerEntry:e];
|
||||
uiEntryOnChanged(e, defaultOnChanged, NULL);
|
||||
|
||||
return e;
|
||||
}
|
||||
|
||||
uiEntry *uiNewEntry(void)
|
||||
{
|
||||
return finishNewEntry([libui_intrinsicWidthNSTextField class]);
|
||||
}
|
||||
|
||||
uiEntry *uiNewPasswordEntry(void)
|
||||
{
|
||||
return finishNewEntry([libui_intrinsicWidthNSSecureTextField class]);
|
||||
}
|
||||
|
||||
uiEntry *uiNewSearchEntry(void)
|
||||
{
|
||||
uiEntry *e;
|
||||
NSSearchField *s;
|
||||
|
||||
e = finishNewEntry([libui_intrinsicWidthNSSearchField class]);
|
||||
s = (NSSearchField *) (e->textfield);
|
||||
// TODO these are only on 10.10
|
||||
// [s setSendsSearchStringImmediately:NO];
|
||||
// [s setSendsWholeSearchString:NO];
|
||||
[s setBordered:NO];
|
||||
[s setBezelStyle:NSTextFieldRoundedBezel];
|
||||
[s setBezeled:YES];
|
||||
return e;
|
||||
}
|
||||
Vendored
+218
@@ -0,0 +1,218 @@
|
||||
// 14 april 2016
|
||||
#import "uipriv_darwin.h"
|
||||
|
||||
@interface fontButton : NSButton {
|
||||
uiFontButton *libui_b;
|
||||
NSFont *libui_font;
|
||||
}
|
||||
- (id)initWithFrame:(NSRect)frame libuiFontButton:(uiFontButton *)b;
|
||||
- (void)updateFontButtonLabel;
|
||||
- (IBAction)fontButtonClicked:(id)sender;
|
||||
- (void)activateFontButton;
|
||||
- (void)deactivateFontButton:(BOOL)activatingAnother;
|
||||
- (void)deactivateOnClose:(NSNotification *)note;
|
||||
- (uiDrawTextFont *)libuiFont;
|
||||
@end
|
||||
|
||||
// only one may be active at one time
|
||||
static fontButton *activeFontButton = nil;
|
||||
|
||||
struct uiFontButton {
|
||||
uiDarwinControl c;
|
||||
fontButton *button;
|
||||
void (*onChanged)(uiFontButton *, void *);
|
||||
void *onChangedData;
|
||||
};
|
||||
|
||||
@implementation fontButton
|
||||
|
||||
- (id)initWithFrame:(NSRect)frame libuiFontButton:(uiFontButton *)b
|
||||
{
|
||||
self = [super initWithFrame:frame];
|
||||
if (self) {
|
||||
self->libui_b = b;
|
||||
|
||||
// imitate a NSColorWell in appearance
|
||||
[self setButtonType:NSPushOnPushOffButton];
|
||||
[self setBordered:YES];
|
||||
[self setBezelStyle:NSShadowlessSquareBezelStyle];
|
||||
|
||||
// default font values according to the CTFontDescriptor reference
|
||||
// this is autoreleased (thanks swillits in irc.freenode.net/#macdev)
|
||||
self->libui_font = [[NSFont fontWithName:@"Helvetica" size:12.0] retain];
|
||||
[self updateFontButtonLabel];
|
||||
|
||||
// for when clicked
|
||||
[self setTarget:self];
|
||||
[self setAction:@selector(fontButtonClicked:)];
|
||||
}
|
||||
return self;
|
||||
}
|
||||
|
||||
- (void)dealloc
|
||||
{
|
||||
// clean up notifications
|
||||
if (activeFontButton == self)
|
||||
[self deactivateFontButton:NO];
|
||||
[self->libui_font release];
|
||||
[super dealloc];
|
||||
}
|
||||
|
||||
- (void)updateFontButtonLabel
|
||||
{
|
||||
NSString *title;
|
||||
|
||||
title = [NSString stringWithFormat:@"%@ %g",
|
||||
[self->libui_font displayName],
|
||||
[self->libui_font pointSize]];
|
||||
[self setTitle:title];
|
||||
}
|
||||
|
||||
- (IBAction)fontButtonClicked:(id)sender
|
||||
{
|
||||
if ([self state] == NSOnState)
|
||||
[self activateFontButton];
|
||||
else
|
||||
[self deactivateFontButton:NO];
|
||||
}
|
||||
|
||||
- (void)activateFontButton
|
||||
{
|
||||
NSFontManager *sfm;
|
||||
|
||||
sfm = [NSFontManager sharedFontManager];
|
||||
if (activeFontButton != nil)
|
||||
[activeFontButton deactivateFontButton:YES];
|
||||
[sfm setTarget:self];
|
||||
[sfm setSelectedFont:self->libui_font isMultiple:NO];
|
||||
[sfm orderFrontFontPanel:self];
|
||||
activeFontButton = self;
|
||||
[[NSNotificationCenter defaultCenter] addObserver:self
|
||||
selector:@selector(deactivateOnClose:)
|
||||
name:NSWindowWillCloseNotification
|
||||
object:[NSFontPanel sharedFontPanel]];
|
||||
[self setState:NSOnState];
|
||||
}
|
||||
|
||||
- (void)deactivateFontButton:(BOOL)activatingAnother
|
||||
{
|
||||
NSFontManager *sfm;
|
||||
|
||||
sfm = [NSFontManager sharedFontManager];
|
||||
[sfm setTarget:nil];
|
||||
if (!activatingAnother)
|
||||
[[NSFontPanel sharedFontPanel] orderOut:self];
|
||||
activeFontButton = nil;
|
||||
[[NSNotificationCenter defaultCenter] removeObserver:self
|
||||
name:NSWindowWillCloseNotification
|
||||
object:[NSFontPanel sharedFontPanel]];
|
||||
[self setState:NSOffState];
|
||||
}
|
||||
|
||||
- (void)deactivateOnClose:(NSNotification *)note
|
||||
{
|
||||
[self deactivateFontButton:NO];
|
||||
}
|
||||
|
||||
- (void)changeFont:(id)sender
|
||||
{
|
||||
NSFontManager *fm;
|
||||
NSFont *old;
|
||||
uiFontButton *b = self->libui_b;
|
||||
|
||||
fm = (NSFontManager *) sender;
|
||||
old = self->libui_font;
|
||||
self->libui_font = [sender convertFont:self->libui_font];
|
||||
// do this even if it returns the same; we don't own anything that isn't from a new or alloc/init
|
||||
[self->libui_font retain];
|
||||
// do this second just in case
|
||||
[old release];
|
||||
[self updateFontButtonLabel];
|
||||
(*(b->onChanged))(b, b->onChangedData);
|
||||
}
|
||||
|
||||
- (NSUInteger)validModesForFontPanel:(NSFontPanel *)panel
|
||||
{
|
||||
return NSFontPanelFaceModeMask |
|
||||
NSFontPanelSizeModeMask |
|
||||
NSFontPanelCollectionModeMask;
|
||||
}
|
||||
|
||||
- (uiDrawTextFont *)libuiFont
|
||||
{
|
||||
return mkTextFontFromNSFont(self->libui_font);
|
||||
}
|
||||
|
||||
@end
|
||||
|
||||
uiDarwinControlAllDefaults(uiFontButton, button)
|
||||
|
||||
// we do not want font change events to be sent to any controls other than the font buttons
|
||||
// see main.m for more details
|
||||
BOOL fontButtonInhibitSendAction(SEL sel, id from, id to)
|
||||
{
|
||||
if (sel != @selector(changeFont:))
|
||||
return NO;
|
||||
return ![to isKindOfClass:[fontButton class]];
|
||||
}
|
||||
|
||||
// we do not want NSFontPanelValidation messages to be sent to any controls other than the font buttons when a font button is active
|
||||
// see main.m for more details
|
||||
BOOL fontButtonOverrideTargetForAction(SEL sel, id from, id to, id *override)
|
||||
{
|
||||
if (activeFontButton == nil)
|
||||
return NO;
|
||||
if (sel != @selector(validModesForFontPanel:))
|
||||
return NO;
|
||||
*override = activeFontButton;
|
||||
return YES;
|
||||
}
|
||||
|
||||
// we also don't want the panel to be usable when there's a dialog running; see stddialogs.m for more details on that
|
||||
// unfortunately the panel seems to ignore -setWorksWhenModal: so we'll have to do things ourselves
|
||||
@interface nonModalFontPanel : NSFontPanel
|
||||
@end
|
||||
|
||||
@implementation nonModalFontPanel
|
||||
|
||||
- (BOOL)worksWhenModal
|
||||
{
|
||||
return NO;
|
||||
}
|
||||
|
||||
@end
|
||||
|
||||
void setupFontPanel(void)
|
||||
{
|
||||
[NSFontManager setFontPanelFactory:[nonModalFontPanel class]];
|
||||
}
|
||||
|
||||
static void defaultOnChanged(uiFontButton *b, void *data)
|
||||
{
|
||||
// do nothing
|
||||
}
|
||||
|
||||
uiDrawTextFont *uiFontButtonFont(uiFontButton *b)
|
||||
{
|
||||
return [b->button libuiFont];
|
||||
}
|
||||
|
||||
void uiFontButtonOnChanged(uiFontButton *b, void (*f)(uiFontButton *, void *), void *data)
|
||||
{
|
||||
b->onChanged = f;
|
||||
b->onChangedData = data;
|
||||
}
|
||||
|
||||
uiFontButton *uiNewFontButton(void)
|
||||
{
|
||||
uiFontButton *b;
|
||||
|
||||
uiDarwinNewControl(uiFontButton, b);
|
||||
|
||||
b->button = [[fontButton alloc] initWithFrame:NSZeroRect libuiFontButton:b];
|
||||
uiDarwinSetControlFont(b->button, NSRegularControlSize);
|
||||
|
||||
uiFontButtonOnChanged(b, defaultOnChanged, NULL);
|
||||
|
||||
return b;
|
||||
}
|
||||
Vendored
+561
@@ -0,0 +1,561 @@
|
||||
// 7 june 2016
|
||||
#import "uipriv_darwin.h"
|
||||
|
||||
// TODO in the test program, sometimes one of the radio buttons can disappear (try when spaced)
|
||||
|
||||
@interface formChild : NSView
|
||||
@property uiControl *c;
|
||||
@property (strong) NSTextField *label;
|
||||
@property BOOL stretchy;
|
||||
@property NSLayoutPriority oldHorzHuggingPri;
|
||||
@property NSLayoutPriority oldVertHuggingPri;
|
||||
@property (strong) NSLayoutConstraint *baseline;
|
||||
@property (strong) NSLayoutConstraint *leading;
|
||||
@property (strong) NSLayoutConstraint *top;
|
||||
@property (strong) NSLayoutConstraint *trailing;
|
||||
@property (strong) NSLayoutConstraint *bottom;
|
||||
- (id)initWithLabel:(NSTextField *)l;
|
||||
- (void)onDestroy;
|
||||
- (NSView *)view;
|
||||
@end
|
||||
|
||||
@interface formView : NSView {
|
||||
uiForm *f;
|
||||
NSMutableArray *children;
|
||||
int padded;
|
||||
|
||||
NSLayoutConstraint *first;
|
||||
NSMutableArray *inBetweens;
|
||||
NSLayoutConstraint *last;
|
||||
NSMutableArray *widths;
|
||||
NSMutableArray *leadings;
|
||||
NSMutableArray *middles;
|
||||
NSMutableArray *trailings;
|
||||
}
|
||||
- (id)initWithF:(uiForm *)ff;
|
||||
- (void)onDestroy;
|
||||
- (void)removeOurConstraints;
|
||||
- (void)syncEnableStates:(int)enabled;
|
||||
- (CGFloat)paddingAmount;
|
||||
- (void)establishOurConstraints;
|
||||
- (void)append:(NSString *)label c:(uiControl *)c stretchy:(int)stretchy;
|
||||
- (void)delete:(int)n;
|
||||
- (int)isPadded;
|
||||
- (void)setPadded:(int)p;
|
||||
- (BOOL)hugsTrailing;
|
||||
- (BOOL)hugsBottom;
|
||||
- (int)nStretchy;
|
||||
@end
|
||||
|
||||
struct uiForm {
|
||||
uiDarwinControl c;
|
||||
formView *view;
|
||||
};
|
||||
|
||||
@implementation formChild
|
||||
|
||||
- (id)initWithLabel:(NSTextField *)l
|
||||
{
|
||||
self = [super initWithFrame:NSZeroRect];
|
||||
if (self) {
|
||||
self.label = l;
|
||||
[self.label setTranslatesAutoresizingMaskIntoConstraints:NO];
|
||||
[self.label setContentHuggingPriority:NSLayoutPriorityRequired forOrientation:NSLayoutConstraintOrientationHorizontal];
|
||||
[self.label setContentHuggingPriority:NSLayoutPriorityRequired forOrientation:NSLayoutConstraintOrientationVertical];
|
||||
[self.label setContentCompressionResistancePriority:NSLayoutPriorityRequired forOrientation:NSLayoutConstraintOrientationHorizontal];
|
||||
[self.label setContentCompressionResistancePriority:NSLayoutPriorityRequired forOrientation:NSLayoutConstraintOrientationVertical];
|
||||
[self addSubview:self.label];
|
||||
|
||||
self.leading = mkConstraint(self.label, NSLayoutAttributeLeading,
|
||||
NSLayoutRelationGreaterThanOrEqual,
|
||||
self, NSLayoutAttributeLeading,
|
||||
1, 0,
|
||||
@"uiForm label leading");
|
||||
[self addConstraint:self.leading];
|
||||
self.top = mkConstraint(self.label, NSLayoutAttributeTop,
|
||||
NSLayoutRelationEqual,
|
||||
self, NSLayoutAttributeTop,
|
||||
1, 0,
|
||||
@"uiForm label top");
|
||||
[self addConstraint:self.top];
|
||||
self.trailing = mkConstraint(self.label, NSLayoutAttributeTrailing,
|
||||
NSLayoutRelationEqual,
|
||||
self, NSLayoutAttributeTrailing,
|
||||
1, 0,
|
||||
@"uiForm label trailing");
|
||||
[self addConstraint:self.trailing];
|
||||
self.bottom = mkConstraint(self.label, NSLayoutAttributeBottom,
|
||||
NSLayoutRelationEqual,
|
||||
self, NSLayoutAttributeBottom,
|
||||
1, 0,
|
||||
@"uiForm label bottom");
|
||||
[self addConstraint:self.bottom];
|
||||
}
|
||||
return self;
|
||||
}
|
||||
|
||||
- (void)onDestroy
|
||||
{
|
||||
[self removeConstraint:self.trailing];
|
||||
self.trailing = nil;
|
||||
[self removeConstraint:self.top];
|
||||
self.top = nil;
|
||||
[self removeConstraint:self.bottom];
|
||||
self.bottom = nil;
|
||||
|
||||
[self.label removeFromSuperview];
|
||||
self.label = nil;
|
||||
}
|
||||
|
||||
- (NSView *)view
|
||||
{
|
||||
return (NSView *) uiControlHandle(self.c);
|
||||
}
|
||||
|
||||
@end
|
||||
|
||||
@implementation formView
|
||||
|
||||
- (id)initWithF:(uiForm *)ff
|
||||
{
|
||||
self = [super initWithFrame:NSZeroRect];
|
||||
if (self != nil) {
|
||||
self->f = ff;
|
||||
self->padded = 0;
|
||||
self->children = [NSMutableArray new];
|
||||
|
||||
self->inBetweens = [NSMutableArray new];
|
||||
self->widths = [NSMutableArray new];
|
||||
self->leadings = [NSMutableArray new];
|
||||
self->middles = [NSMutableArray new];
|
||||
self->trailings = [NSMutableArray new];
|
||||
}
|
||||
return self;
|
||||
}
|
||||
|
||||
- (void)onDestroy
|
||||
{
|
||||
formChild *fc;
|
||||
|
||||
[self removeOurConstraints];
|
||||
[self->inBetweens release];
|
||||
[self->widths release];
|
||||
[self->leadings release];
|
||||
[self->middles release];
|
||||
[self->trailings release];
|
||||
|
||||
for (fc in self->children) {
|
||||
[self removeConstraint:fc.baseline];
|
||||
fc.baseline = nil;
|
||||
uiControlSetParent(fc.c, NULL);
|
||||
uiDarwinControlSetSuperview(uiDarwinControl(fc.c), nil);
|
||||
uiControlDestroy(fc.c);
|
||||
[fc onDestroy];
|
||||
[fc removeFromSuperview];
|
||||
}
|
||||
[self->children release];
|
||||
}
|
||||
|
||||
- (void)removeOurConstraints
|
||||
{
|
||||
if (self->first != nil) {
|
||||
[self removeConstraint:self->first];
|
||||
[self->first release];
|
||||
self->first = nil;
|
||||
}
|
||||
if ([self->inBetweens count] != 0) {
|
||||
[self removeConstraints:self->inBetweens];
|
||||
[self->inBetweens removeAllObjects];
|
||||
}
|
||||
if (self->last != nil) {
|
||||
[self removeConstraint:self->last];
|
||||
[self->last release];
|
||||
self->last = nil;
|
||||
}
|
||||
if ([self->widths count] != 0) {
|
||||
[self removeConstraints:self->widths];
|
||||
[self->widths removeAllObjects];
|
||||
}
|
||||
if ([self->leadings count] != 0) {
|
||||
[self removeConstraints:self->leadings];
|
||||
[self->leadings removeAllObjects];
|
||||
}
|
||||
if ([self->middles count] != 0) {
|
||||
[self removeConstraints:self->middles];
|
||||
[self->middles removeAllObjects];
|
||||
}
|
||||
if ([self->trailings count] != 0) {
|
||||
[self removeConstraints:self->trailings];
|
||||
[self->trailings removeAllObjects];
|
||||
}
|
||||
}
|
||||
|
||||
- (void)syncEnableStates:(int)enabled
|
||||
{
|
||||
formChild *fc;
|
||||
|
||||
for (fc in self->children)
|
||||
uiDarwinControlSyncEnableState(uiDarwinControl(fc.c), enabled);
|
||||
}
|
||||
|
||||
- (CGFloat)paddingAmount
|
||||
{
|
||||
if (!self->padded)
|
||||
return 0.0;
|
||||
return uiDarwinPaddingAmount(NULL);
|
||||
}
|
||||
|
||||
- (void)establishOurConstraints
|
||||
{
|
||||
formChild *fc;
|
||||
CGFloat padding;
|
||||
NSView *prev, *prevlabel;
|
||||
NSLayoutConstraint *c;
|
||||
|
||||
[self removeOurConstraints];
|
||||
if ([self->children count] == 0)
|
||||
return;
|
||||
padding = [self paddingAmount];
|
||||
|
||||
// first arrange the children vertically and make them the same width
|
||||
prev = nil;
|
||||
for (fc in self->children) {
|
||||
[fc setHidden:!uiControlVisible(fc.c)];
|
||||
if (!uiControlVisible(fc.c))
|
||||
continue;
|
||||
if (prev == nil) { // first view
|
||||
self->first = mkConstraint(self, NSLayoutAttributeTop,
|
||||
NSLayoutRelationEqual,
|
||||
[fc view], NSLayoutAttributeTop,
|
||||
1, 0,
|
||||
@"uiForm first vertical constraint");
|
||||
[self addConstraint:self->first];
|
||||
[self->first retain];
|
||||
prev = [fc view];
|
||||
prevlabel = fc;
|
||||
continue;
|
||||
}
|
||||
// not the first; link it
|
||||
c = mkConstraint(prev, NSLayoutAttributeBottom,
|
||||
NSLayoutRelationEqual,
|
||||
[fc view], NSLayoutAttributeTop,
|
||||
1, -padding,
|
||||
@"uiForm in-between vertical constraint");
|
||||
[self addConstraint:c];
|
||||
[self->inBetweens addObject:c];
|
||||
// and make the same width
|
||||
c = mkConstraint(prev, NSLayoutAttributeWidth,
|
||||
NSLayoutRelationEqual,
|
||||
[fc view], NSLayoutAttributeWidth,
|
||||
1, 0,
|
||||
@"uiForm control width constraint");
|
||||
[self addConstraint:c];
|
||||
[self->widths addObject:c];
|
||||
c = mkConstraint(prevlabel, NSLayoutAttributeWidth,
|
||||
NSLayoutRelationEqual,
|
||||
fc, NSLayoutAttributeWidth,
|
||||
1, 0,
|
||||
@"uiForm label lwidth constraint");
|
||||
[self addConstraint:c];
|
||||
[self->widths addObject:c];
|
||||
prev = [fc view];
|
||||
prevlabel = fc;
|
||||
}
|
||||
if (prev == nil) // all hidden; act as if nothing there
|
||||
return;
|
||||
self->last = mkConstraint(prev, NSLayoutAttributeBottom,
|
||||
NSLayoutRelationEqual,
|
||||
self, NSLayoutAttributeBottom,
|
||||
1, 0,
|
||||
@"uiForm last vertical constraint");
|
||||
[self addConstraint:self->last];
|
||||
[self->last retain];
|
||||
|
||||
// now arrange the controls horizontally
|
||||
for (fc in self->children) {
|
||||
if (!uiControlVisible(fc.c))
|
||||
continue;
|
||||
c = mkConstraint(self, NSLayoutAttributeLeading,
|
||||
NSLayoutRelationEqual,
|
||||
fc, NSLayoutAttributeLeading,
|
||||
1, 0,
|
||||
@"uiForm leading constraint");
|
||||
[self addConstraint:c];
|
||||
[self->leadings addObject:c];
|
||||
// coerce the control to be as wide as possible
|
||||
// see http://stackoverflow.com/questions/37710892/in-auto-layout-i-set-up-labels-that-shouldnt-grow-horizontally-and-controls-th
|
||||
c = mkConstraint(self, NSLayoutAttributeLeading,
|
||||
NSLayoutRelationEqual,
|
||||
[fc view], NSLayoutAttributeLeading,
|
||||
1, 0,
|
||||
@"uiForm leading constraint");
|
||||
[c setPriority:NSLayoutPriorityDefaultHigh];
|
||||
[self addConstraint:c];
|
||||
[self->leadings addObject:c];
|
||||
c = mkConstraint(fc, NSLayoutAttributeTrailing,
|
||||
NSLayoutRelationEqual,
|
||||
[fc view], NSLayoutAttributeLeading,
|
||||
1, -padding,
|
||||
@"uiForm middle constraint");
|
||||
[self addConstraint:c];
|
||||
[self->middles addObject:c];
|
||||
c = mkConstraint([fc view], NSLayoutAttributeTrailing,
|
||||
NSLayoutRelationEqual,
|
||||
self, NSLayoutAttributeTrailing,
|
||||
1, 0,
|
||||
@"uiForm trailing constraint");
|
||||
[self addConstraint:c];
|
||||
[self->trailings addObject:c];
|
||||
// TODO
|
||||
c = mkConstraint(fc, NSLayoutAttributeBottom,
|
||||
NSLayoutRelationLessThanOrEqual,
|
||||
self, NSLayoutAttributeBottom,
|
||||
1, 0,
|
||||
@"TODO");
|
||||
[self addConstraint:c];
|
||||
[self->trailings addObject:c];
|
||||
}
|
||||
|
||||
// and make all stretchy controls have the same height
|
||||
prev = nil;
|
||||
for (fc in self->children) {
|
||||
if (!uiControlVisible(fc.c))
|
||||
continue;
|
||||
if (!fc.stretchy)
|
||||
continue;
|
||||
if (prev == nil) {
|
||||
prev = [fc view];
|
||||
continue;
|
||||
}
|
||||
c = mkConstraint([fc view], NSLayoutAttributeHeight,
|
||||
NSLayoutRelationEqual,
|
||||
prev, NSLayoutAttributeHeight,
|
||||
1, 0,
|
||||
@"uiForm stretchy constraint");
|
||||
[self addConstraint:c];
|
||||
// TODO make a dedicated array for this
|
||||
[self->leadings addObject:c];
|
||||
}
|
||||
|
||||
// we don't arrange the labels vertically; that's done when we add the control since those constraints don't need to change (they just need to be at their baseline)
|
||||
}
|
||||
|
||||
- (void)append:(NSString *)label c:(uiControl *)c stretchy:(int)stretchy
|
||||
{
|
||||
formChild *fc;
|
||||
NSLayoutPriority priority;
|
||||
NSLayoutAttribute attribute;
|
||||
int oldnStretchy;
|
||||
|
||||
fc = [[formChild alloc] initWithLabel:newLabel(label)];
|
||||
fc.c = c;
|
||||
fc.stretchy = stretchy;
|
||||
fc.oldHorzHuggingPri = uiDarwinControlHuggingPriority(uiDarwinControl(fc.c), NSLayoutConstraintOrientationHorizontal);
|
||||
fc.oldVertHuggingPri = uiDarwinControlHuggingPriority(uiDarwinControl(fc.c), NSLayoutConstraintOrientationVertical);
|
||||
[fc setTranslatesAutoresizingMaskIntoConstraints:NO];
|
||||
[self addSubview:fc];
|
||||
|
||||
uiControlSetParent(fc.c, uiControl(self->f));
|
||||
uiDarwinControlSetSuperview(uiDarwinControl(fc.c), self);
|
||||
uiDarwinControlSyncEnableState(uiDarwinControl(fc.c), uiControlEnabledToUser(uiControl(self->f)));
|
||||
|
||||
// if a control is stretchy, it should not hug vertically
|
||||
// otherwise, it should *forcibly* hug
|
||||
if (fc.stretchy)
|
||||
priority = NSLayoutPriorityDefaultLow;
|
||||
else
|
||||
// LONGTERM will default high work?
|
||||
priority = NSLayoutPriorityRequired;
|
||||
uiDarwinControlSetHuggingPriority(uiDarwinControl(fc.c), priority, NSLayoutConstraintOrientationVertical);
|
||||
// make sure controls don't hug their horizontal direction so they fill the width of the view
|
||||
uiDarwinControlSetHuggingPriority(uiDarwinControl(fc.c), NSLayoutPriorityDefaultLow, NSLayoutConstraintOrientationHorizontal);
|
||||
|
||||
// and constrain the baselines to position the label vertically
|
||||
// if the view is a scroll view, align tops, not baselines
|
||||
// this is what Interface Builder does
|
||||
attribute = NSLayoutAttributeBaseline;
|
||||
if ([[fc view] isKindOfClass:[NSScrollView class]])
|
||||
attribute = NSLayoutAttributeTop;
|
||||
fc.baseline = mkConstraint(fc.label, attribute,
|
||||
NSLayoutRelationEqual,
|
||||
[fc view], attribute,
|
||||
1, 0,
|
||||
@"uiForm baseline constraint");
|
||||
[self addConstraint:fc.baseline];
|
||||
|
||||
oldnStretchy = [self nStretchy];
|
||||
[self->children addObject:fc];
|
||||
|
||||
[self establishOurConstraints];
|
||||
if (fc.stretchy)
|
||||
if (oldnStretchy == 0)
|
||||
uiDarwinNotifyEdgeHuggingChanged(uiDarwinControl(self->f));
|
||||
|
||||
[fc release]; // we don't need the initial reference now
|
||||
}
|
||||
|
||||
- (void)delete:(int)n
|
||||
{
|
||||
formChild *fc;
|
||||
int stretchy;
|
||||
|
||||
fc = (formChild *) [self->children objectAtIndex:n];
|
||||
stretchy = fc.stretchy;
|
||||
|
||||
uiControlSetParent(fc.c, NULL);
|
||||
uiDarwinControlSetSuperview(uiDarwinControl(fc.c), nil);
|
||||
|
||||
uiDarwinControlSetHuggingPriority(uiDarwinControl(fc.c), fc.oldHorzHuggingPri, NSLayoutConstraintOrientationHorizontal);
|
||||
uiDarwinControlSetHuggingPriority(uiDarwinControl(fc.c), fc.oldVertHuggingPri, NSLayoutConstraintOrientationVertical);
|
||||
|
||||
[fc onDestroy];
|
||||
[self->children removeObjectAtIndex:n];
|
||||
|
||||
[self establishOurConstraints];
|
||||
if (stretchy)
|
||||
if ([self nStretchy] == 0)
|
||||
uiDarwinNotifyEdgeHuggingChanged(uiDarwinControl(self->f));
|
||||
}
|
||||
|
||||
- (int)isPadded
|
||||
{
|
||||
return self->padded;
|
||||
}
|
||||
|
||||
- (void)setPadded:(int)p
|
||||
{
|
||||
CGFloat padding;
|
||||
NSLayoutConstraint *c;
|
||||
|
||||
self->padded = p;
|
||||
padding = [self paddingAmount];
|
||||
for (c in self->inBetweens)
|
||||
[c setConstant:-padding];
|
||||
for (c in self->middles)
|
||||
[c setConstant:-padding];
|
||||
}
|
||||
|
||||
- (BOOL)hugsTrailing
|
||||
{
|
||||
return YES; // always hug trailing
|
||||
}
|
||||
|
||||
- (BOOL)hugsBottom
|
||||
{
|
||||
// only hug if we have stretchy
|
||||
return [self nStretchy] != 0;
|
||||
}
|
||||
|
||||
- (int)nStretchy
|
||||
{
|
||||
formChild *fc;
|
||||
int n;
|
||||
|
||||
n = 0;
|
||||
for (fc in self->children) {
|
||||
if (!uiControlVisible(fc.c))
|
||||
continue;
|
||||
if (fc.stretchy)
|
||||
n++;
|
||||
}
|
||||
return n;
|
||||
}
|
||||
|
||||
@end
|
||||
|
||||
static void uiFormDestroy(uiControl *c)
|
||||
{
|
||||
uiForm *f = uiForm(c);
|
||||
|
||||
[f->view onDestroy];
|
||||
[f->view release];
|
||||
uiFreeControl(uiControl(f));
|
||||
}
|
||||
|
||||
uiDarwinControlDefaultHandle(uiForm, view)
|
||||
uiDarwinControlDefaultParent(uiForm, view)
|
||||
uiDarwinControlDefaultSetParent(uiForm, view)
|
||||
uiDarwinControlDefaultToplevel(uiForm, view)
|
||||
uiDarwinControlDefaultVisible(uiForm, view)
|
||||
uiDarwinControlDefaultShow(uiForm, view)
|
||||
uiDarwinControlDefaultHide(uiForm, view)
|
||||
uiDarwinControlDefaultEnabled(uiForm, view)
|
||||
uiDarwinControlDefaultEnable(uiForm, view)
|
||||
uiDarwinControlDefaultDisable(uiForm, view)
|
||||
|
||||
static void uiFormSyncEnableState(uiDarwinControl *c, int enabled)
|
||||
{
|
||||
uiForm *f = uiForm(c);
|
||||
|
||||
if (uiDarwinShouldStopSyncEnableState(uiDarwinControl(f), enabled))
|
||||
return;
|
||||
[f->view syncEnableStates:enabled];
|
||||
}
|
||||
|
||||
uiDarwinControlDefaultSetSuperview(uiForm, view)
|
||||
|
||||
static BOOL uiFormHugsTrailingEdge(uiDarwinControl *c)
|
||||
{
|
||||
uiForm *f = uiForm(c);
|
||||
|
||||
return [f->view hugsTrailing];
|
||||
}
|
||||
|
||||
static BOOL uiFormHugsBottom(uiDarwinControl *c)
|
||||
{
|
||||
uiForm *f = uiForm(c);
|
||||
|
||||
return [f->view hugsBottom];
|
||||
}
|
||||
|
||||
static void uiFormChildEdgeHuggingChanged(uiDarwinControl *c)
|
||||
{
|
||||
uiForm *f = uiForm(c);
|
||||
|
||||
[f->view establishOurConstraints];
|
||||
}
|
||||
|
||||
uiDarwinControlDefaultHuggingPriority(uiForm, view)
|
||||
uiDarwinControlDefaultSetHuggingPriority(uiForm, view)
|
||||
|
||||
static void uiFormChildVisibilityChanged(uiDarwinControl *c)
|
||||
{
|
||||
uiForm *f = uiForm(c);
|
||||
|
||||
[f->view establishOurConstraints];
|
||||
}
|
||||
|
||||
void uiFormAppend(uiForm *f, const char *label, uiControl *c, int stretchy)
|
||||
{
|
||||
// LONGTERM on other platforms
|
||||
// or at leat allow this and implicitly turn it into a spacer
|
||||
if (c == NULL)
|
||||
userbug("You cannot add NULL to a uiForm.");
|
||||
[f->view append:toNSString(label) c:c stretchy:stretchy];
|
||||
}
|
||||
|
||||
void uiFormDelete(uiForm *f, int n)
|
||||
{
|
||||
[f->view delete:n];
|
||||
}
|
||||
|
||||
int uiFormPadded(uiForm *f)
|
||||
{
|
||||
return [f->view isPadded];
|
||||
}
|
||||
|
||||
void uiFormSetPadded(uiForm *f, int padded)
|
||||
{
|
||||
[f->view setPadded:padded];
|
||||
}
|
||||
|
||||
uiForm *uiNewForm(void)
|
||||
{
|
||||
uiForm *f;
|
||||
|
||||
uiDarwinNewControl(uiForm, f);
|
||||
|
||||
f->view = [[formView alloc] initWithF:f];
|
||||
|
||||
return f;
|
||||
}
|
||||
Vendored
+800
@@ -0,0 +1,800 @@
|
||||
// 11 june 2016
|
||||
#import "uipriv_darwin.h"
|
||||
|
||||
// TODO the assorted test doesn't work right at all
|
||||
|
||||
@interface gridChild : NSView
|
||||
@property uiControl *c;
|
||||
@property int left;
|
||||
@property int top;
|
||||
@property int xspan;
|
||||
@property int yspan;
|
||||
@property int hexpand;
|
||||
@property uiAlign halign;
|
||||
@property int vexpand;
|
||||
@property uiAlign valign;
|
||||
|
||||
@property (strong) NSLayoutConstraint *leadingc;
|
||||
@property (strong) NSLayoutConstraint *topc;
|
||||
@property (strong) NSLayoutConstraint *trailingc;
|
||||
@property (strong) NSLayoutConstraint *bottomc;
|
||||
@property (strong) NSLayoutConstraint *xcenterc;
|
||||
@property (strong) NSLayoutConstraint *ycenterc;
|
||||
|
||||
@property NSLayoutPriority oldHorzHuggingPri;
|
||||
@property NSLayoutPriority oldVertHuggingPri;
|
||||
- (void)setC:(uiControl *)c grid:(uiGrid *)g;
|
||||
- (void)onDestroy;
|
||||
- (NSView *)view;
|
||||
@end
|
||||
|
||||
@interface gridView : NSView {
|
||||
uiGrid *g;
|
||||
NSMutableArray *children;
|
||||
int padded;
|
||||
|
||||
NSMutableArray *edges;
|
||||
NSMutableArray *inBetweens;
|
||||
|
||||
NSMutableArray *emptyCellViews;
|
||||
}
|
||||
- (id)initWithG:(uiGrid *)gg;
|
||||
- (void)onDestroy;
|
||||
- (void)removeOurConstraints;
|
||||
- (void)syncEnableStates:(int)enabled;
|
||||
- (CGFloat)paddingAmount;
|
||||
- (void)establishOurConstraints;
|
||||
- (void)append:(gridChild *)gc;
|
||||
- (void)insert:(gridChild *)gc after:(uiControl *)c at:(uiAt)at;
|
||||
- (int)isPadded;
|
||||
- (void)setPadded:(int)p;
|
||||
- (BOOL)hugsTrailing;
|
||||
- (BOOL)hugsBottom;
|
||||
- (int)nhexpand;
|
||||
- (int)nvexpand;
|
||||
@end
|
||||
|
||||
struct uiGrid {
|
||||
uiDarwinControl c;
|
||||
gridView *view;
|
||||
};
|
||||
|
||||
@implementation gridChild
|
||||
|
||||
- (void)setC:(uiControl *)c grid:(uiGrid *)g
|
||||
{
|
||||
self.c = c;
|
||||
self.oldHorzHuggingPri = uiDarwinControlHuggingPriority(uiDarwinControl(self.c), NSLayoutConstraintOrientationHorizontal);
|
||||
self.oldVertHuggingPri = uiDarwinControlHuggingPriority(uiDarwinControl(self.c), NSLayoutConstraintOrientationVertical);
|
||||
|
||||
uiControlSetParent(self.c, uiControl(g));
|
||||
uiDarwinControlSetSuperview(uiDarwinControl(self.c), self);
|
||||
uiDarwinControlSyncEnableState(uiDarwinControl(self.c), uiControlEnabledToUser(uiControl(g)));
|
||||
|
||||
if (self.halign == uiAlignStart || self.halign == uiAlignFill) {
|
||||
self.leadingc = mkConstraint(self, NSLayoutAttributeLeading,
|
||||
NSLayoutRelationEqual,
|
||||
[self view], NSLayoutAttributeLeading,
|
||||
1, 0,
|
||||
@"uiGrid child horizontal alignment start constraint");
|
||||
[self addConstraint:self.leadingc];
|
||||
}
|
||||
if (self.halign == uiAlignCenter) {
|
||||
self.xcenterc = mkConstraint(self, NSLayoutAttributeCenterX,
|
||||
NSLayoutRelationEqual,
|
||||
[self view], NSLayoutAttributeCenterX,
|
||||
1, 0,
|
||||
@"uiGrid child horizontal alignment center constraint");
|
||||
[self addConstraint:self.xcenterc];
|
||||
}
|
||||
if (self.halign == uiAlignEnd || self.halign == uiAlignFill) {
|
||||
self.trailingc = mkConstraint(self, NSLayoutAttributeTrailing,
|
||||
NSLayoutRelationEqual,
|
||||
[self view], NSLayoutAttributeTrailing,
|
||||
1, 0,
|
||||
@"uiGrid child horizontal alignment end constraint");
|
||||
[self addConstraint:self.trailingc];
|
||||
}
|
||||
|
||||
if (self.valign == uiAlignStart || self.valign == uiAlignFill) {
|
||||
self.topc = mkConstraint(self, NSLayoutAttributeTop,
|
||||
NSLayoutRelationEqual,
|
||||
[self view], NSLayoutAttributeTop,
|
||||
1, 0,
|
||||
@"uiGrid child vertical alignment start constraint");
|
||||
[self addConstraint:self.topc];
|
||||
}
|
||||
if (self.valign == uiAlignCenter) {
|
||||
self.ycenterc = mkConstraint(self, NSLayoutAttributeCenterY,
|
||||
NSLayoutRelationEqual,
|
||||
[self view], NSLayoutAttributeCenterY,
|
||||
1, 0,
|
||||
@"uiGrid child vertical alignment center constraint");
|
||||
[self addConstraint:self.ycenterc];
|
||||
}
|
||||
if (self.valign == uiAlignEnd || self.valign == uiAlignFill) {
|
||||
self.bottomc = mkConstraint(self, NSLayoutAttributeBottom,
|
||||
NSLayoutRelationEqual,
|
||||
[self view], NSLayoutAttributeBottom,
|
||||
1, 0,
|
||||
@"uiGrid child vertical alignment end constraint");
|
||||
[self addConstraint:self.bottomc];
|
||||
}
|
||||
}
|
||||
|
||||
- (void)onDestroy
|
||||
{
|
||||
if (self.leadingc != nil) {
|
||||
[self removeConstraint:self.leadingc];
|
||||
self.leadingc = nil;
|
||||
}
|
||||
if (self.topc != nil) {
|
||||
[self removeConstraint:self.topc];
|
||||
self.topc = nil;
|
||||
}
|
||||
if (self.trailingc != nil) {
|
||||
[self removeConstraint:self.trailingc];
|
||||
self.trailingc = nil;
|
||||
}
|
||||
if (self.bottomc != nil) {
|
||||
[self removeConstraint:self.bottomc];
|
||||
self.bottomc = nil;
|
||||
}
|
||||
if (self.xcenterc != nil) {
|
||||
[self removeConstraint:self.xcenterc];
|
||||
self.xcenterc = nil;
|
||||
}
|
||||
if (self.ycenterc != nil) {
|
||||
[self removeConstraint:self.ycenterc];
|
||||
self.ycenterc = nil;
|
||||
}
|
||||
|
||||
uiControlSetParent(self.c, NULL);
|
||||
uiDarwinControlSetSuperview(uiDarwinControl(self.c), nil);
|
||||
uiDarwinControlSetHuggingPriority(uiDarwinControl(self.c), self.oldHorzHuggingPri, NSLayoutConstraintOrientationHorizontal);
|
||||
uiDarwinControlSetHuggingPriority(uiDarwinControl(self.c), self.oldVertHuggingPri, NSLayoutConstraintOrientationVertical);
|
||||
}
|
||||
|
||||
- (NSView *)view
|
||||
{
|
||||
return (NSView *) uiControlHandle(self.c);
|
||||
}
|
||||
|
||||
@end
|
||||
|
||||
@implementation gridView
|
||||
|
||||
- (id)initWithG:(uiGrid *)gg
|
||||
{
|
||||
self = [super initWithFrame:NSZeroRect];
|
||||
if (self != nil) {
|
||||
self->g = gg;
|
||||
self->padded = 0;
|
||||
self->children = [NSMutableArray new];
|
||||
|
||||
self->edges = [NSMutableArray new];
|
||||
self->inBetweens = [NSMutableArray new];
|
||||
|
||||
self->emptyCellViews = [NSMutableArray new];
|
||||
}
|
||||
return self;
|
||||
}
|
||||
|
||||
- (void)onDestroy
|
||||
{
|
||||
gridChild *gc;
|
||||
|
||||
[self removeOurConstraints];
|
||||
[self->edges release];
|
||||
[self->inBetweens release];
|
||||
|
||||
[self->emptyCellViews release];
|
||||
|
||||
for (gc in self->children) {
|
||||
[gc onDestroy];
|
||||
uiControlDestroy(gc.c);
|
||||
[gc removeFromSuperview];
|
||||
}
|
||||
[self->children release];
|
||||
}
|
||||
|
||||
- (void)removeOurConstraints
|
||||
{
|
||||
NSView *v;
|
||||
|
||||
if ([self->edges count] != 0) {
|
||||
[self removeConstraints:self->edges];
|
||||
[self->edges removeAllObjects];
|
||||
}
|
||||
if ([self->inBetweens count] != 0) {
|
||||
[self removeConstraints:self->inBetweens];
|
||||
[self->inBetweens removeAllObjects];
|
||||
}
|
||||
|
||||
for (v in self->emptyCellViews)
|
||||
[v removeFromSuperview];
|
||||
[self->emptyCellViews removeAllObjects];
|
||||
}
|
||||
|
||||
- (void)syncEnableStates:(int)enabled
|
||||
{
|
||||
gridChild *gc;
|
||||
|
||||
for (gc in self->children)
|
||||
uiDarwinControlSyncEnableState(uiDarwinControl(gc.c), enabled);
|
||||
}
|
||||
|
||||
- (CGFloat)paddingAmount
|
||||
{
|
||||
if (!self->padded)
|
||||
return 0.0;
|
||||
return uiDarwinPaddingAmount(NULL);
|
||||
}
|
||||
|
||||
// LONGTERM stop early if all controls are hidden
|
||||
- (void)establishOurConstraints
|
||||
{
|
||||
gridChild *gc;
|
||||
CGFloat padding;
|
||||
int xmin, ymin;
|
||||
int xmax, ymax;
|
||||
int xcount, ycount;
|
||||
BOOL first;
|
||||
int **gg;
|
||||
NSView ***gv;
|
||||
BOOL **gspan;
|
||||
int x, y;
|
||||
int i;
|
||||
NSLayoutConstraint *c;
|
||||
int firstx, firsty;
|
||||
BOOL *hexpand, *vexpand;
|
||||
BOOL doit;
|
||||
BOOL onlyEmptyAndSpanning;
|
||||
|
||||
[self removeOurConstraints];
|
||||
if ([self->children count] == 0)
|
||||
return;
|
||||
padding = [self paddingAmount];
|
||||
|
||||
// first, figure out the minimum and maximum row and column numbers
|
||||
// ignore hidden controls
|
||||
first = YES;
|
||||
for (gc in self->children) {
|
||||
// this bit is important: it ensures row ymin and column xmin have at least one cell to draw, so the onlyEmptyAndSpanning logic below will never run on those rows
|
||||
if (!uiControlVisible(gc.c))
|
||||
continue;
|
||||
if (first) {
|
||||
xmin = gc.left;
|
||||
ymin = gc.top;
|
||||
xmax = gc.left + gc.xspan;
|
||||
ymax = gc.top + gc.yspan;
|
||||
first = NO;
|
||||
continue;
|
||||
}
|
||||
if (xmin > gc.left)
|
||||
xmin = gc.left;
|
||||
if (ymin > gc.top)
|
||||
ymin = gc.top;
|
||||
if (xmax < (gc.left + gc.xspan))
|
||||
xmax = gc.left + gc.xspan;
|
||||
if (ymax < (gc.top + gc.yspan))
|
||||
ymax = gc.top + gc.yspan;
|
||||
}
|
||||
if (first != NO) // the entire grid is hidden; do nothing
|
||||
return;
|
||||
xcount = xmax - xmin;
|
||||
ycount = ymax - ymin;
|
||||
|
||||
// now build a topological map of the grid gg[y][x]
|
||||
// also figure out which cells contain spanned views so they can be ignored later
|
||||
// treat hidden controls by keeping the indices -1
|
||||
gg = (int **) uiAlloc(ycount * sizeof (int *), "int[][]");
|
||||
gspan = (BOOL **) uiAlloc(ycount * sizeof (BOOL *), "BOOL[][]");
|
||||
for (y = 0; y < ycount; y++) {
|
||||
gg[y] = (int *) uiAlloc(xcount * sizeof (int), "int[]");
|
||||
gspan[y] = (BOOL *) uiAlloc(xcount * sizeof (BOOL), "BOOL[]");
|
||||
for (x = 0; x < xcount; x++)
|
||||
gg[y][x] = -1; // empty
|
||||
}
|
||||
for (i = 0; i < [self->children count]; i++) {
|
||||
gc = (gridChild *) [self->children objectAtIndex:i];
|
||||
if (!uiControlVisible(gc.c))
|
||||
continue;
|
||||
for (y = gc.top; y < gc.top + gc.yspan; y++)
|
||||
for (x = gc.left; x < gc.left + gc.xspan; x++) {
|
||||
gg[y - ymin][x - xmin] = i;
|
||||
if (x != gc.left || y != gc.top)
|
||||
gspan[y - ymin][x - xmin] = YES;
|
||||
}
|
||||
}
|
||||
|
||||
// if a row or column only contains emptys and spanning cells of a opposite-direction spannings, remove it by duplicating the previous row or column
|
||||
for (y = 0; y < ycount; y++) {
|
||||
onlyEmptyAndSpanning = YES;
|
||||
for (x = 0; x < xcount; x++)
|
||||
if (gg[y][x] != -1) {
|
||||
gc = (gridChild *) [self->children objectAtIndex:gg[y][x]];
|
||||
if (gc.yspan == 1 || gc.top - ymin == y) {
|
||||
onlyEmptyAndSpanning = NO;
|
||||
break;
|
||||
}
|
||||
}
|
||||
if (onlyEmptyAndSpanning)
|
||||
for (x = 0; x < xcount; x++) {
|
||||
gg[y][x] = gg[y - 1][x];
|
||||
gspan[y][x] = YES;
|
||||
}
|
||||
}
|
||||
for (x = 0; x < xcount; x++) {
|
||||
onlyEmptyAndSpanning = YES;
|
||||
for (y = 0; y < ycount; y++)
|
||||
if (gg[y][x] != -1) {
|
||||
gc = (gridChild *) [self->children objectAtIndex:gg[y][x]];
|
||||
if (gc.xspan == 1 || gc.left - xmin == x) {
|
||||
onlyEmptyAndSpanning = NO;
|
||||
break;
|
||||
}
|
||||
}
|
||||
if (onlyEmptyAndSpanning)
|
||||
for (y = 0; y < ycount; y++) {
|
||||
gg[y][x] = gg[y][x - 1];
|
||||
gspan[y][x] = YES;
|
||||
}
|
||||
}
|
||||
|
||||
// now build a topological map of the grid's views gv[y][x]
|
||||
// for any empty cell, create a dummy view
|
||||
gv = (NSView ***) uiAlloc(ycount * sizeof (NSView **), "NSView *[][]");
|
||||
for (y = 0; y < ycount; y++) {
|
||||
gv[y] = (NSView **) uiAlloc(xcount * sizeof (NSView *), "NSView *[]");
|
||||
for (x = 0; x < xcount; x++)
|
||||
if (gg[y][x] == -1) {
|
||||
gv[y][x] = [[NSView alloc] initWithFrame:NSZeroRect];
|
||||
[gv[y][x] setTranslatesAutoresizingMaskIntoConstraints:NO];
|
||||
[self addSubview:gv[y][x]];
|
||||
[self->emptyCellViews addObject:gv[y][x]];
|
||||
} else {
|
||||
gc = (gridChild *) [self->children objectAtIndex:gg[y][x]];
|
||||
gv[y][x] = gc;
|
||||
}
|
||||
}
|
||||
|
||||
// now figure out which rows and columns really expand
|
||||
hexpand = (BOOL *) uiAlloc(xcount * sizeof (BOOL), "BOOL[]");
|
||||
vexpand = (BOOL *) uiAlloc(ycount * sizeof (BOOL), "BOOL[]");
|
||||
// first, which don't span
|
||||
for (gc in self->children) {
|
||||
if (!uiControlVisible(gc.c))
|
||||
continue;
|
||||
if (gc.hexpand && gc.xspan == 1)
|
||||
hexpand[gc.left - xmin] = YES;
|
||||
if (gc.vexpand && gc.yspan == 1)
|
||||
vexpand[gc.top - ymin] = YES;
|
||||
}
|
||||
// second, which do span
|
||||
// the way we handle this is simple: if none of the spanned rows/columns expand, make all rows/columns expand
|
||||
for (gc in self->children) {
|
||||
if (!uiControlVisible(gc.c))
|
||||
continue;
|
||||
if (gc.hexpand && gc.xspan != 1) {
|
||||
doit = YES;
|
||||
for (x = gc.left; x < gc.left + gc.xspan; x++)
|
||||
if (hexpand[x - xmin]) {
|
||||
doit = NO;
|
||||
break;
|
||||
}
|
||||
if (doit)
|
||||
for (x = gc.left; x < gc.left + gc.xspan; x++)
|
||||
hexpand[x - xmin] = YES;
|
||||
}
|
||||
if (gc.vexpand && gc.yspan != 1) {
|
||||
doit = YES;
|
||||
for (y = gc.top; y < gc.top + gc.yspan; y++)
|
||||
if (vexpand[y - ymin]) {
|
||||
doit = NO;
|
||||
break;
|
||||
}
|
||||
if (doit)
|
||||
for (y = gc.top; y < gc.top + gc.yspan; y++)
|
||||
vexpand[y - ymin] = YES;
|
||||
}
|
||||
}
|
||||
|
||||
// now establish all the edge constraints
|
||||
// leading and trailing edges
|
||||
for (y = 0; y < ycount; y++) {
|
||||
c = mkConstraint(self, NSLayoutAttributeLeading,
|
||||
NSLayoutRelationEqual,
|
||||
gv[y][0], NSLayoutAttributeLeading,
|
||||
1, 0,
|
||||
@"uiGrid leading edge constraint");
|
||||
[self addConstraint:c];
|
||||
[self->edges addObject:c];
|
||||
c = mkConstraint(self, NSLayoutAttributeTrailing,
|
||||
NSLayoutRelationEqual,
|
||||
gv[y][xcount - 1], NSLayoutAttributeTrailing,
|
||||
1, 0,
|
||||
@"uiGrid trailing edge constraint");
|
||||
[self addConstraint:c];
|
||||
[self->edges addObject:c];
|
||||
}
|
||||
// top and bottom edges
|
||||
for (x = 0; x < xcount; x++) {
|
||||
c = mkConstraint(self, NSLayoutAttributeTop,
|
||||
NSLayoutRelationEqual,
|
||||
gv[0][x], NSLayoutAttributeTop,
|
||||
1, 0,
|
||||
@"uiGrid top edge constraint");
|
||||
[self addConstraint:c];
|
||||
[self->edges addObject:c];
|
||||
c = mkConstraint(self, NSLayoutAttributeBottom,
|
||||
NSLayoutRelationEqual,
|
||||
gv[ycount - 1][x], NSLayoutAttributeBottom,
|
||||
1, 0,
|
||||
@"uiGrid bottom edge constraint");
|
||||
[self addConstraint:c];
|
||||
[self->edges addObject:c];
|
||||
}
|
||||
|
||||
// now align leading and top edges
|
||||
// do NOT align spanning cells!
|
||||
for (x = 0; x < xcount; x++) {
|
||||
for (y = 0; y < ycount; y++)
|
||||
if (!gspan[y][x])
|
||||
break;
|
||||
firsty = y;
|
||||
for (y++; y < ycount; y++) {
|
||||
if (gspan[y][x])
|
||||
continue;
|
||||
c = mkConstraint(gv[firsty][x], NSLayoutAttributeLeading,
|
||||
NSLayoutRelationEqual,
|
||||
gv[y][x], NSLayoutAttributeLeading,
|
||||
1, 0,
|
||||
@"uiGrid column leading constraint");
|
||||
[self addConstraint:c];
|
||||
[self->edges addObject:c];
|
||||
}
|
||||
}
|
||||
for (y = 0; y < ycount; y++) {
|
||||
for (x = 0; x < xcount; x++)
|
||||
if (!gspan[y][x])
|
||||
break;
|
||||
firstx = x;
|
||||
for (x++; x < xcount; x++) {
|
||||
if (gspan[y][x])
|
||||
continue;
|
||||
c = mkConstraint(gv[y][firstx], NSLayoutAttributeTop,
|
||||
NSLayoutRelationEqual,
|
||||
gv[y][x], NSLayoutAttributeTop,
|
||||
1, 0,
|
||||
@"uiGrid row top constraint");
|
||||
[self addConstraint:c];
|
||||
[self->edges addObject:c];
|
||||
}
|
||||
}
|
||||
|
||||
// now string adjacent views together
|
||||
for (y = 0; y < ycount; y++)
|
||||
for (x = 1; x < xcount; x++)
|
||||
if (gv[y][x - 1] != gv[y][x]) {
|
||||
c = mkConstraint(gv[y][x - 1], NSLayoutAttributeTrailing,
|
||||
NSLayoutRelationEqual,
|
||||
gv[y][x], NSLayoutAttributeLeading,
|
||||
1, -padding,
|
||||
@"uiGrid internal horizontal constraint");
|
||||
[self addConstraint:c];
|
||||
[self->inBetweens addObject:c];
|
||||
}
|
||||
for (x = 0; x < xcount; x++)
|
||||
for (y = 1; y < ycount; y++)
|
||||
if (gv[y - 1][x] != gv[y][x]) {
|
||||
c = mkConstraint(gv[y - 1][x], NSLayoutAttributeBottom,
|
||||
NSLayoutRelationEqual,
|
||||
gv[y][x], NSLayoutAttributeTop,
|
||||
1, -padding,
|
||||
@"uiGrid internal vertical constraint");
|
||||
[self addConstraint:c];
|
||||
[self->inBetweens addObject:c];
|
||||
}
|
||||
|
||||
// now set priorities for all widgets that expand or not
|
||||
// if a cell is in an expanding row, OR If it spans, then it must be willing to stretch
|
||||
// otherwise, it tries not to
|
||||
// note we don't use NSLayoutPriorityRequired as that will cause things to squish when they shouldn't
|
||||
for (gc in self->children) {
|
||||
NSLayoutPriority priority;
|
||||
|
||||
if (!uiControlVisible(gc.c))
|
||||
continue;
|
||||
if (hexpand[gc.left - xmin] || gc.xspan != 1)
|
||||
priority = NSLayoutPriorityDefaultLow;
|
||||
else
|
||||
priority = NSLayoutPriorityDefaultHigh;
|
||||
uiDarwinControlSetHuggingPriority(uiDarwinControl(gc.c), priority, NSLayoutConstraintOrientationHorizontal);
|
||||
// same for vertical direction
|
||||
if (vexpand[gc.top - ymin] || gc.yspan != 1)
|
||||
priority = NSLayoutPriorityDefaultLow;
|
||||
else
|
||||
priority = NSLayoutPriorityDefaultHigh;
|
||||
uiDarwinControlSetHuggingPriority(uiDarwinControl(gc.c), priority, NSLayoutConstraintOrientationVertical);
|
||||
}
|
||||
|
||||
// TODO make all expanding rows/columns the same height/width
|
||||
|
||||
// and finally clean up
|
||||
uiFree(hexpand);
|
||||
uiFree(vexpand);
|
||||
for (y = 0; y < ycount; y++) {
|
||||
uiFree(gg[y]);
|
||||
uiFree(gv[y]);
|
||||
uiFree(gspan[y]);
|
||||
}
|
||||
uiFree(gg);
|
||||
uiFree(gv);
|
||||
uiFree(gspan);
|
||||
}
|
||||
|
||||
- (void)append:(gridChild *)gc
|
||||
{
|
||||
BOOL update;
|
||||
int oldnh, oldnv;
|
||||
|
||||
[gc setTranslatesAutoresizingMaskIntoConstraints:NO];
|
||||
[self addSubview:gc];
|
||||
|
||||
// no need to set priority here; that's done in establishOurConstraints
|
||||
|
||||
oldnh = [self nhexpand];
|
||||
oldnv = [self nvexpand];
|
||||
[self->children addObject:gc];
|
||||
|
||||
[self establishOurConstraints];
|
||||
update = NO;
|
||||
if (gc.hexpand)
|
||||
if (oldnh == 0)
|
||||
update = YES;
|
||||
if (gc.vexpand)
|
||||
if (oldnv == 0)
|
||||
update = YES;
|
||||
if (update)
|
||||
uiDarwinNotifyEdgeHuggingChanged(uiDarwinControl(self->g));
|
||||
|
||||
[gc release]; // we don't need the initial reference now
|
||||
}
|
||||
|
||||
- (void)insert:(gridChild *)gc after:(uiControl *)c at:(uiAt)at
|
||||
{
|
||||
gridChild *other;
|
||||
BOOL found;
|
||||
|
||||
found = NO;
|
||||
for (other in self->children)
|
||||
if (other.c == c) {
|
||||
found = YES;
|
||||
break;
|
||||
}
|
||||
if (!found)
|
||||
userbug("Existing control %p is not in grid %p; you cannot add other controls next to it", c, self->g);
|
||||
|
||||
switch (at) {
|
||||
case uiAtLeading:
|
||||
gc.left = other.left - gc.xspan;
|
||||
gc.top = other.top;
|
||||
break;
|
||||
case uiAtTop:
|
||||
gc.left = other.left;
|
||||
gc.top = other.top - gc.yspan;
|
||||
break;
|
||||
case uiAtTrailing:
|
||||
gc.left = other.left + other.xspan;
|
||||
gc.top = other.top;
|
||||
break;
|
||||
case uiAtBottom:
|
||||
gc.left = other.left;
|
||||
gc.top = other.top + other.yspan;
|
||||
break;
|
||||
// TODO add error checks to ALL enums
|
||||
}
|
||||
|
||||
[self append:gc];
|
||||
}
|
||||
|
||||
- (int)isPadded
|
||||
{
|
||||
return self->padded;
|
||||
}
|
||||
|
||||
- (void)setPadded:(int)p
|
||||
{
|
||||
CGFloat padding;
|
||||
NSLayoutConstraint *c;
|
||||
|
||||
#if 0 /* TODO */
|
||||
dispatch_after(
|
||||
dispatch_time(DISPATCH_TIME_NOW, 3 * NSEC_PER_SEC),
|
||||
dispatch_get_main_queue(),
|
||||
^{ [[self window] visualizeConstraints:[self constraints]]; }
|
||||
);
|
||||
#endif
|
||||
self->padded = p;
|
||||
padding = [self paddingAmount];
|
||||
for (c in self->inBetweens)
|
||||
switch ([c firstAttribute]) {
|
||||
case NSLayoutAttributeLeading:
|
||||
case NSLayoutAttributeTop:
|
||||
[c setConstant:padding];
|
||||
break;
|
||||
case NSLayoutAttributeTrailing:
|
||||
case NSLayoutAttributeBottom:
|
||||
[c setConstant:-padding];
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
- (BOOL)hugsTrailing
|
||||
{
|
||||
// only hug if we have horizontally expanding
|
||||
return [self nhexpand] != 0;
|
||||
}
|
||||
|
||||
- (BOOL)hugsBottom
|
||||
{
|
||||
// only hug if we have vertically expanding
|
||||
return [self nvexpand] != 0;
|
||||
}
|
||||
|
||||
- (int)nhexpand
|
||||
{
|
||||
gridChild *gc;
|
||||
int n;
|
||||
|
||||
n = 0;
|
||||
for (gc in self->children) {
|
||||
if (!uiControlVisible(gc.c))
|
||||
continue;
|
||||
if (gc.hexpand)
|
||||
n++;
|
||||
}
|
||||
return n;
|
||||
}
|
||||
|
||||
- (int)nvexpand
|
||||
{
|
||||
gridChild *gc;
|
||||
int n;
|
||||
|
||||
n = 0;
|
||||
for (gc in self->children) {
|
||||
if (!uiControlVisible(gc.c))
|
||||
continue;
|
||||
if (gc.vexpand)
|
||||
n++;
|
||||
}
|
||||
return n;
|
||||
}
|
||||
|
||||
@end
|
||||
|
||||
static void uiGridDestroy(uiControl *c)
|
||||
{
|
||||
uiGrid *g = uiGrid(c);
|
||||
|
||||
[g->view onDestroy];
|
||||
[g->view release];
|
||||
uiFreeControl(uiControl(g));
|
||||
}
|
||||
|
||||
uiDarwinControlDefaultHandle(uiGrid, view)
|
||||
uiDarwinControlDefaultParent(uiGrid, view)
|
||||
uiDarwinControlDefaultSetParent(uiGrid, view)
|
||||
uiDarwinControlDefaultToplevel(uiGrid, view)
|
||||
uiDarwinControlDefaultVisible(uiGrid, view)
|
||||
uiDarwinControlDefaultShow(uiGrid, view)
|
||||
uiDarwinControlDefaultHide(uiGrid, view)
|
||||
uiDarwinControlDefaultEnabled(uiGrid, view)
|
||||
uiDarwinControlDefaultEnable(uiGrid, view)
|
||||
uiDarwinControlDefaultDisable(uiGrid, view)
|
||||
|
||||
static void uiGridSyncEnableState(uiDarwinControl *c, int enabled)
|
||||
{
|
||||
uiGrid *g = uiGrid(c);
|
||||
|
||||
if (uiDarwinShouldStopSyncEnableState(uiDarwinControl(g), enabled))
|
||||
return;
|
||||
[g->view syncEnableStates:enabled];
|
||||
}
|
||||
|
||||
uiDarwinControlDefaultSetSuperview(uiGrid, view)
|
||||
|
||||
static BOOL uiGridHugsTrailingEdge(uiDarwinControl *c)
|
||||
{
|
||||
uiGrid *g = uiGrid(c);
|
||||
|
||||
return [g->view hugsTrailing];
|
||||
}
|
||||
|
||||
static BOOL uiGridHugsBottom(uiDarwinControl *c)
|
||||
{
|
||||
uiGrid *g = uiGrid(c);
|
||||
|
||||
return [g->view hugsBottom];
|
||||
}
|
||||
|
||||
static void uiGridChildEdgeHuggingChanged(uiDarwinControl *c)
|
||||
{
|
||||
uiGrid *g = uiGrid(c);
|
||||
|
||||
[g->view establishOurConstraints];
|
||||
}
|
||||
|
||||
uiDarwinControlDefaultHuggingPriority(uiGrid, view)
|
||||
uiDarwinControlDefaultSetHuggingPriority(uiGrid, view)
|
||||
|
||||
static void uiGridChildVisibilityChanged(uiDarwinControl *c)
|
||||
{
|
||||
uiGrid *g = uiGrid(c);
|
||||
|
||||
[g->view establishOurConstraints];
|
||||
}
|
||||
|
||||
static gridChild *toChild(uiControl *c, int xspan, int yspan, int hexpand, uiAlign halign, int vexpand, uiAlign valign, uiGrid *g)
|
||||
{
|
||||
gridChild *gc;
|
||||
|
||||
if (xspan < 0)
|
||||
userbug("You cannot have a negative xspan in a uiGrid cell.");
|
||||
if (yspan < 0)
|
||||
userbug("You cannot have a negative yspan in a uiGrid cell.");
|
||||
gc = [gridChild new];
|
||||
gc.xspan = xspan;
|
||||
gc.yspan = yspan;
|
||||
gc.hexpand = hexpand;
|
||||
gc.halign = halign;
|
||||
gc.vexpand = vexpand;
|
||||
gc.valign = valign;
|
||||
[gc setC:c grid:g];
|
||||
return gc;
|
||||
}
|
||||
|
||||
void uiGridAppend(uiGrid *g, uiControl *c, int left, int top, int xspan, int yspan, int hexpand, uiAlign halign, int vexpand, uiAlign valign)
|
||||
{
|
||||
gridChild *gc;
|
||||
|
||||
// LONGTERM on other platforms
|
||||
// or at leat allow this and implicitly turn it into a spacer
|
||||
if (c == NULL)
|
||||
userbug("You cannot add NULL to a uiGrid.");
|
||||
gc = toChild(c, xspan, yspan, hexpand, halign, vexpand, valign, g);
|
||||
gc.left = left;
|
||||
gc.top = top;
|
||||
[g->view append:gc];
|
||||
}
|
||||
|
||||
void uiGridInsertAt(uiGrid *g, uiControl *c, uiControl *existing, uiAt at, int xspan, int yspan, int hexpand, uiAlign halign, int vexpand, uiAlign valign)
|
||||
{
|
||||
gridChild *gc;
|
||||
|
||||
gc = toChild(c, xspan, yspan, hexpand, halign, vexpand, valign, g);
|
||||
[g->view insert:gc after:existing at:at];
|
||||
}
|
||||
|
||||
int uiGridPadded(uiGrid *g)
|
||||
{
|
||||
return [g->view isPadded];
|
||||
}
|
||||
|
||||
void uiGridSetPadded(uiGrid *g, int padded)
|
||||
{
|
||||
[g->view setPadded:padded];
|
||||
}
|
||||
|
||||
uiGrid *uiNewGrid(void)
|
||||
{
|
||||
uiGrid *g;
|
||||
|
||||
uiDarwinNewControl(uiGrid, g);
|
||||
|
||||
g->view = [[gridView alloc] initWithG:g];
|
||||
|
||||
return g;
|
||||
}
|
||||
Vendored
+194
@@ -0,0 +1,194 @@
|
||||
// 14 august 2015
|
||||
#import "uipriv_darwin.h"
|
||||
|
||||
struct uiGroup {
|
||||
uiDarwinControl c;
|
||||
NSBox *box;
|
||||
uiControl *child;
|
||||
NSLayoutPriority oldHorzHuggingPri;
|
||||
NSLayoutPriority oldVertHuggingPri;
|
||||
int margined;
|
||||
struct singleChildConstraints constraints;
|
||||
NSLayoutPriority horzHuggingPri;
|
||||
NSLayoutPriority vertHuggingPri;
|
||||
};
|
||||
|
||||
static void removeConstraints(uiGroup *g)
|
||||
{
|
||||
// set to contentView instead of to the box itself, otherwise we get clipping underneath the label
|
||||
singleChildConstraintsRemove(&(g->constraints), [g->box contentView]);
|
||||
}
|
||||
|
||||
static void uiGroupDestroy(uiControl *c)
|
||||
{
|
||||
uiGroup *g = uiGroup(c);
|
||||
|
||||
removeConstraints(g);
|
||||
if (g->child != NULL) {
|
||||
uiControlSetParent(g->child, NULL);
|
||||
uiDarwinControlSetSuperview(uiDarwinControl(g->child), nil);
|
||||
uiControlDestroy(g->child);
|
||||
}
|
||||
[g->box release];
|
||||
uiFreeControl(uiControl(g));
|
||||
}
|
||||
|
||||
uiDarwinControlDefaultHandle(uiGroup, box)
|
||||
uiDarwinControlDefaultParent(uiGroup, box)
|
||||
uiDarwinControlDefaultSetParent(uiGroup, box)
|
||||
uiDarwinControlDefaultToplevel(uiGroup, box)
|
||||
uiDarwinControlDefaultVisible(uiGroup, box)
|
||||
uiDarwinControlDefaultShow(uiGroup, box)
|
||||
uiDarwinControlDefaultHide(uiGroup, box)
|
||||
uiDarwinControlDefaultEnabled(uiGroup, box)
|
||||
uiDarwinControlDefaultEnable(uiGroup, box)
|
||||
uiDarwinControlDefaultDisable(uiGroup, box)
|
||||
|
||||
static void uiGroupSyncEnableState(uiDarwinControl *c, int enabled)
|
||||
{
|
||||
uiGroup *g = uiGroup(c);
|
||||
|
||||
if (uiDarwinShouldStopSyncEnableState(uiDarwinControl(g), enabled))
|
||||
return;
|
||||
if (g->child != NULL)
|
||||
uiDarwinControlSyncEnableState(uiDarwinControl(g->child), enabled);
|
||||
}
|
||||
|
||||
uiDarwinControlDefaultSetSuperview(uiGroup, box)
|
||||
|
||||
static void groupRelayout(uiGroup *g)
|
||||
{
|
||||
NSView *childView;
|
||||
|
||||
removeConstraints(g);
|
||||
if (g->child == NULL)
|
||||
return;
|
||||
childView = (NSView *) uiControlHandle(g->child);
|
||||
singleChildConstraintsEstablish(&(g->constraints),
|
||||
[g->box contentView], childView,
|
||||
uiDarwinControlHugsTrailingEdge(uiDarwinControl(g->child)),
|
||||
uiDarwinControlHugsBottom(uiDarwinControl(g->child)),
|
||||
g->margined,
|
||||
@"uiGroup");
|
||||
// needed for some very rare drawing errors...
|
||||
jiggleViewLayout(g->box);
|
||||
}
|
||||
|
||||
// TODO rename these since I'm starting to get confused by what they mean by hugging
|
||||
BOOL uiGroupHugsTrailingEdge(uiDarwinControl *c)
|
||||
{
|
||||
uiGroup *g = uiGroup(c);
|
||||
|
||||
// TODO make a function?
|
||||
return g->horzHuggingPri < NSLayoutPriorityWindowSizeStayPut;
|
||||
}
|
||||
|
||||
BOOL uiGroupHugsBottom(uiDarwinControl *c)
|
||||
{
|
||||
uiGroup *g = uiGroup(c);
|
||||
|
||||
return g->vertHuggingPri < NSLayoutPriorityWindowSizeStayPut;
|
||||
}
|
||||
|
||||
static void uiGroupChildEdgeHuggingChanged(uiDarwinControl *c)
|
||||
{
|
||||
uiGroup *g = uiGroup(c);
|
||||
|
||||
groupRelayout(g);
|
||||
}
|
||||
|
||||
static NSLayoutPriority uiGroupHuggingPriority(uiDarwinControl *c, NSLayoutConstraintOrientation orientation)
|
||||
{
|
||||
uiGroup *g = uiGroup(c);
|
||||
|
||||
if (orientation == NSLayoutConstraintOrientationHorizontal)
|
||||
return g->horzHuggingPri;
|
||||
return g->vertHuggingPri;
|
||||
}
|
||||
|
||||
static void uiGroupSetHuggingPriority(uiDarwinControl *c, NSLayoutPriority priority, NSLayoutConstraintOrientation orientation)
|
||||
{
|
||||
uiGroup *g = uiGroup(c);
|
||||
|
||||
if (orientation == NSLayoutConstraintOrientationHorizontal)
|
||||
g->horzHuggingPri = priority;
|
||||
else
|
||||
g->vertHuggingPri = priority;
|
||||
uiDarwinNotifyEdgeHuggingChanged(uiDarwinControl(g));
|
||||
}
|
||||
|
||||
static void uiGroupChildVisibilityChanged(uiDarwinControl *c)
|
||||
{
|
||||
uiGroup *g = uiGroup(c);
|
||||
|
||||
groupRelayout(g);
|
||||
}
|
||||
|
||||
char *uiGroupTitle(uiGroup *g)
|
||||
{
|
||||
return uiDarwinNSStringToText([g->box title]);
|
||||
}
|
||||
|
||||
void uiGroupSetTitle(uiGroup *g, const char *title)
|
||||
{
|
||||
[g->box setTitle:toNSString(title)];
|
||||
}
|
||||
|
||||
void uiGroupSetChild(uiGroup *g, uiControl *child)
|
||||
{
|
||||
NSView *childView;
|
||||
|
||||
if (g->child != NULL) {
|
||||
removeConstraints(g);
|
||||
uiDarwinControlSetHuggingPriority(uiDarwinControl(g->child), g->oldHorzHuggingPri, NSLayoutConstraintOrientationHorizontal);
|
||||
uiDarwinControlSetHuggingPriority(uiDarwinControl(g->child), g->oldVertHuggingPri, NSLayoutConstraintOrientationVertical);
|
||||
uiControlSetParent(g->child, NULL);
|
||||
uiDarwinControlSetSuperview(uiDarwinControl(g->child), nil);
|
||||
}
|
||||
g->child = child;
|
||||
if (g->child != NULL) {
|
||||
childView = (NSView *) uiControlHandle(g->child);
|
||||
uiControlSetParent(g->child, uiControl(g));
|
||||
uiDarwinControlSetSuperview(uiDarwinControl(g->child), [g->box contentView]);
|
||||
uiDarwinControlSyncEnableState(uiDarwinControl(g->child), uiControlEnabledToUser(uiControl(g)));
|
||||
// don't hug, just in case we're a stretchy group
|
||||
g->oldHorzHuggingPri = uiDarwinControlHuggingPriority(uiDarwinControl(g->child), NSLayoutConstraintOrientationHorizontal);
|
||||
g->oldVertHuggingPri = uiDarwinControlHuggingPriority(uiDarwinControl(g->child), NSLayoutConstraintOrientationVertical);
|
||||
uiDarwinControlSetHuggingPriority(uiDarwinControl(g->child), NSLayoutPriorityDefaultLow, NSLayoutConstraintOrientationHorizontal);
|
||||
uiDarwinControlSetHuggingPriority(uiDarwinControl(g->child), NSLayoutPriorityDefaultLow, NSLayoutConstraintOrientationVertical);
|
||||
}
|
||||
groupRelayout(g);
|
||||
}
|
||||
|
||||
int uiGroupMargined(uiGroup *g)
|
||||
{
|
||||
return g->margined;
|
||||
}
|
||||
|
||||
void uiGroupSetMargined(uiGroup *g, int margined)
|
||||
{
|
||||
g->margined = margined;
|
||||
singleChildConstraintsSetMargined(&(g->constraints), g->margined);
|
||||
}
|
||||
|
||||
uiGroup *uiNewGroup(const char *title)
|
||||
{
|
||||
uiGroup *g;
|
||||
|
||||
uiDarwinNewControl(uiGroup, g);
|
||||
|
||||
g->box = [[NSBox alloc] initWithFrame:NSZeroRect];
|
||||
[g->box setTitle:toNSString(title)];
|
||||
[g->box setBoxType:NSBoxPrimary];
|
||||
[g->box setBorderType:NSLineBorder];
|
||||
[g->box setTransparent:NO];
|
||||
[g->box setTitlePosition:NSAtTop];
|
||||
// we can't use uiDarwinSetControlFont() because the selector is different
|
||||
[g->box setTitleFont:[NSFont systemFontOfSize:[NSFont systemFontSizeForControlSize:NSSmallControlSize]]];
|
||||
|
||||
// default to low hugging to not hug edges
|
||||
g->horzHuggingPri = NSLayoutPriorityDefaultLow;
|
||||
g->vertHuggingPri = NSLayoutPriorityDefaultLow;
|
||||
|
||||
return g;
|
||||
}
|
||||
Vendored
+82
@@ -0,0 +1,82 @@
|
||||
// 25 june 2016
|
||||
#import "uipriv_darwin.h"
|
||||
|
||||
struct uiImage {
|
||||
NSImage *i;
|
||||
NSSize size;
|
||||
NSMutableArray *swizzled;
|
||||
};
|
||||
|
||||
uiImage *uiNewImage(double width, double height)
|
||||
{
|
||||
uiImage *i;
|
||||
|
||||
i = uiNew(uiImage);
|
||||
i->size = NSMakeSize(width, height);
|
||||
i->i = [[NSImage alloc] initWithSize:i->size];
|
||||
i->swizzled = [NSMutableArray new];
|
||||
return i;
|
||||
}
|
||||
|
||||
void uiFreeImage(uiImage *i)
|
||||
{
|
||||
NSValue *v;
|
||||
|
||||
[i->i release];
|
||||
// to be safe, do this after releasing the image
|
||||
for (v in i->swizzled)
|
||||
uiFree([v pointerValue]);
|
||||
[i->swizzled release];
|
||||
uiFree(i);
|
||||
}
|
||||
|
||||
void uiImageAppend(uiImage *i, void *pixels, int pixelWidth, int pixelHeight, int pixelStride)
|
||||
{
|
||||
NSBitmapImageRep *repCalibrated, *repsRGB;
|
||||
uint8_t *swizzled, *bp, *sp;
|
||||
int x, y;
|
||||
unsigned char *pix[1];
|
||||
|
||||
// OS X demands that R and B are in the opposite order from what we expect
|
||||
// we must swizzle :(
|
||||
// LONGTERM test on a big-endian system
|
||||
swizzled = (uint8_t *) uiAlloc((pixelStride * pixelHeight * 4) * sizeof (uint8_t), "uint8_t[]");
|
||||
bp = (uint8_t *) pixels;
|
||||
sp = swizzled;
|
||||
for (y = 0; y < pixelHeight * pixelStride; y += pixelStride)
|
||||
for (x = 0; x < pixelStride; x++) {
|
||||
sp[0] = bp[2];
|
||||
sp[1] = bp[1];
|
||||
sp[2] = bp[0];
|
||||
sp[3] = bp[3];
|
||||
sp += 4;
|
||||
bp += 4;
|
||||
}
|
||||
|
||||
pix[0] = (unsigned char *) swizzled;
|
||||
repCalibrated = [[NSBitmapImageRep alloc] initWithBitmapDataPlanes:pix
|
||||
pixelsWide:pixelWidth
|
||||
pixelsHigh:pixelHeight
|
||||
bitsPerSample:8
|
||||
samplesPerPixel:4
|
||||
hasAlpha:YES
|
||||
isPlanar:NO
|
||||
colorSpaceName:NSCalibratedRGBColorSpace
|
||||
bitmapFormat:0
|
||||
bytesPerRow:pixelStride
|
||||
bitsPerPixel:32];
|
||||
repsRGB = [repCalibrated bitmapImageRepByRetaggingWithColorSpace:[NSColorSpace sRGBColorSpace]];
|
||||
[repCalibrated release];
|
||||
|
||||
[i->i addRepresentation:repsRGB];
|
||||
[repsRGB setSize:i->size];
|
||||
[repsRGB release];
|
||||
|
||||
// we need to keep swizzled alive for NSBitmapImageRep
|
||||
[i->swizzled addObject:[NSValue valueWithPointer:swizzled]];
|
||||
}
|
||||
|
||||
NSImage *imageImage(uiImage *i)
|
||||
{
|
||||
return i->i;
|
||||
}
|
||||
Vendored
+43
@@ -0,0 +1,43 @@
|
||||
// 14 august 2015
|
||||
#import "uipriv_darwin.h"
|
||||
|
||||
struct uiLabel {
|
||||
uiDarwinControl c;
|
||||
NSTextField *textfield;
|
||||
};
|
||||
|
||||
uiDarwinControlAllDefaults(uiLabel, textfield)
|
||||
|
||||
char *uiLabelText(uiLabel *l)
|
||||
{
|
||||
return uiDarwinNSStringToText([l->textfield stringValue]);
|
||||
}
|
||||
|
||||
void uiLabelSetText(uiLabel *l, const char *text)
|
||||
{
|
||||
[l->textfield setStringValue:toNSString(text)];
|
||||
}
|
||||
|
||||
NSTextField *newLabel(NSString *str)
|
||||
{
|
||||
NSTextField *tf;
|
||||
|
||||
tf = [[NSTextField alloc] initWithFrame:NSZeroRect];
|
||||
[tf setStringValue:str];
|
||||
[tf setEditable:NO];
|
||||
[tf setSelectable:NO];
|
||||
[tf setDrawsBackground:NO];
|
||||
finishNewTextField(tf, NO);
|
||||
return tf;
|
||||
}
|
||||
|
||||
uiLabel *uiNewLabel(const char *text)
|
||||
{
|
||||
uiLabel *l;
|
||||
|
||||
uiDarwinNewControl(uiLabel, l);
|
||||
|
||||
l->textfield = newLabel(toNSString(text));
|
||||
|
||||
return l;
|
||||
}
|
||||
Vendored
+239
@@ -0,0 +1,239 @@
|
||||
// 6 april 2015
|
||||
#import "uipriv_darwin.h"
|
||||
|
||||
static BOOL canQuit = NO;
|
||||
static NSAutoreleasePool *globalPool;
|
||||
static applicationClass *app;
|
||||
static appDelegate *delegate;
|
||||
|
||||
static BOOL (^isRunning)(void);
|
||||
static BOOL stepsIsRunning;
|
||||
|
||||
@implementation applicationClass
|
||||
|
||||
- (void)sendEvent:(NSEvent *)e
|
||||
{
|
||||
if (sendAreaEvents(e) != 0)
|
||||
return;
|
||||
[super sendEvent:e];
|
||||
}
|
||||
|
||||
// NSColorPanel always sends changeColor: to the first responder regardless of whether there's a target set on it
|
||||
// we can override it here (see colorbutton.m)
|
||||
// thanks to mikeash in irc.freenode.net/#macdev for informing me this is how the first responder chain is initiated
|
||||
// it turns out NSFontManager also sends changeFont: through this; let's inhibit that here too (see fontbutton.m)
|
||||
- (BOOL)sendAction:(SEL)sel to:(id)to from:(id)from
|
||||
{
|
||||
if (colorButtonInhibitSendAction(sel, from, to))
|
||||
return NO;
|
||||
if (fontButtonInhibitSendAction(sel, from, to))
|
||||
return NO;
|
||||
return [super sendAction:sel to:to from:from];
|
||||
}
|
||||
|
||||
// likewise, NSFontManager also sends NSFontPanelValidation messages to the first responder, however it does NOT use sendAction:from:to:!
|
||||
// instead, it uses this one (thanks swillits in irc.freenode.net/#macdev)
|
||||
// we also need to override it (see fontbutton.m)
|
||||
- (id)targetForAction:(SEL)sel to:(id)to from:(id)from
|
||||
{
|
||||
id override;
|
||||
|
||||
if (fontButtonOverrideTargetForAction(sel, from, to, &override))
|
||||
return override;
|
||||
return [super targetForAction:sel to:to from:from];
|
||||
}
|
||||
|
||||
// hey look! we're overriding terminate:!
|
||||
// we're going to make sure we can go back to main() whether Cocoa likes it or not!
|
||||
// and just how are we going to do that, hm?
|
||||
// (note: this is called after applicationShouldTerminate:)
|
||||
- (void)terminate:(id)sender
|
||||
{
|
||||
// yes that's right folks: DO ABSOLUTELY NOTHING.
|
||||
// the magic is [NSApp run] will just... stop.
|
||||
|
||||
// well let's not do nothing; let's actually quit our graceful way
|
||||
NSEvent *e;
|
||||
|
||||
if (!canQuit)
|
||||
implbug("call to [NSApp terminate:] when not ready to terminate; definitely contact andlabs");
|
||||
|
||||
[realNSApp() stop:realNSApp()];
|
||||
// stop: won't register until another event has passed; let's synthesize one
|
||||
e = [NSEvent otherEventWithType:NSApplicationDefined
|
||||
location:NSZeroPoint
|
||||
modifierFlags:0
|
||||
timestamp:[[NSProcessInfo processInfo] systemUptime]
|
||||
windowNumber:0
|
||||
context:[NSGraphicsContext currentContext]
|
||||
subtype:0
|
||||
data1:0
|
||||
data2:0];
|
||||
[realNSApp() postEvent:e atStart:NO]; // let pending events take priority (this is what PostQuitMessage() on Windows does so we have to do it here too for parity; thanks to mikeash in irc.freenode.net/#macdev for confirming that this parameter should indeed be NO)
|
||||
|
||||
// and in case uiMainSteps() was called
|
||||
stepsIsRunning = NO;
|
||||
}
|
||||
|
||||
@end
|
||||
|
||||
@implementation appDelegate
|
||||
|
||||
- (void)dealloc
|
||||
{
|
||||
// Apple docs: "Don't Use Accessor Methods in Initializer Methods and dealloc"
|
||||
[_menuManager release];
|
||||
[super dealloc];
|
||||
}
|
||||
|
||||
- (NSApplicationTerminateReply)applicationShouldTerminate:(NSApplication *)app
|
||||
{
|
||||
// for debugging
|
||||
NSLog(@"in applicationShouldTerminate:");
|
||||
if (shouldQuit()) {
|
||||
canQuit = YES;
|
||||
// this will call terminate:, which is the same as uiQuit()
|
||||
return NSTerminateNow;
|
||||
}
|
||||
return NSTerminateCancel;
|
||||
}
|
||||
|
||||
- (BOOL)applicationShouldTerminateAfterLastWindowClosed:(NSApplication *)app
|
||||
{
|
||||
return NO;
|
||||
}
|
||||
|
||||
@end
|
||||
|
||||
uiInitOptions options;
|
||||
|
||||
const char *uiInit(uiInitOptions *o)
|
||||
{
|
||||
@autoreleasepool {
|
||||
options = *o;
|
||||
app = [[applicationClass sharedApplication] retain];
|
||||
// don't check for a NO return; something (launch services?) causes running from application bundles to always return NO when asking to change activation policy, even if the change is to the same activation policy!
|
||||
// see https://github.com/andlabs/ui/issues/6
|
||||
[realNSApp() setActivationPolicy:NSApplicationActivationPolicyRegular];
|
||||
delegate = [appDelegate new];
|
||||
[realNSApp() setDelegate:delegate];
|
||||
|
||||
initAlloc();
|
||||
|
||||
// always do this so we always have an application menu
|
||||
appDelegate().menuManager = [[menuManager new] autorelease];
|
||||
[realNSApp() setMainMenu:[appDelegate().menuManager makeMenubar]];
|
||||
|
||||
setupFontPanel();
|
||||
}
|
||||
|
||||
globalPool = [[NSAutoreleasePool alloc] init];
|
||||
|
||||
return NULL;
|
||||
}
|
||||
|
||||
void uiUninit(void)
|
||||
{
|
||||
if (!globalPool) {
|
||||
userbug("You must call uiInit() first!");
|
||||
}
|
||||
[globalPool release];
|
||||
|
||||
@autoreleasepool {
|
||||
[delegate release];
|
||||
[realNSApp() setDelegate:nil];
|
||||
[app release];
|
||||
uninitAlloc();
|
||||
}
|
||||
}
|
||||
|
||||
void uiFreeInitError(const char *err)
|
||||
{
|
||||
}
|
||||
|
||||
void uiMain(void)
|
||||
{
|
||||
isRunning = ^{
|
||||
return [realNSApp() isRunning];
|
||||
};
|
||||
[realNSApp() run];
|
||||
}
|
||||
|
||||
void uiMainSteps(void)
|
||||
{
|
||||
// SDL does this and it seems to be necessary for the menubar to work (see #182)
|
||||
[realNSApp() finishLaunching];
|
||||
isRunning = ^{
|
||||
return stepsIsRunning;
|
||||
};
|
||||
stepsIsRunning = YES;
|
||||
}
|
||||
|
||||
int uiMainStep(int wait)
|
||||
{
|
||||
struct nextEventArgs nea;
|
||||
|
||||
nea.mask = NSAnyEventMask;
|
||||
|
||||
// ProPuke did this in his original PR requesting this
|
||||
// I'm not sure if this will work, but I assume it will...
|
||||
nea.duration = [NSDate distantPast];
|
||||
if (wait) // but this is normal so it will work
|
||||
nea.duration = [NSDate distantFuture];
|
||||
|
||||
nea.mode = NSDefaultRunLoopMode;
|
||||
nea.dequeue = YES;
|
||||
|
||||
return mainStep(&nea, ^(NSEvent *e) {
|
||||
return NO;
|
||||
});
|
||||
}
|
||||
|
||||
// see also:
|
||||
// - http://www.cocoawithlove.com/2009/01/demystifying-nsapplication-by.html
|
||||
// - https://github.com/gnustep/gui/blob/master/Source/NSApplication.m
|
||||
int mainStep(struct nextEventArgs *nea, BOOL (^interceptEvent)(NSEvent *e))
|
||||
{
|
||||
NSDate *expire;
|
||||
NSEvent *e;
|
||||
NSEventType type;
|
||||
|
||||
@autoreleasepool {
|
||||
if (!isRunning())
|
||||
return 0;
|
||||
|
||||
e = [realNSApp() nextEventMatchingMask:nea->mask
|
||||
untilDate:nea->duration
|
||||
inMode:nea->mode
|
||||
dequeue:nea->dequeue];
|
||||
if (e == nil)
|
||||
return 1;
|
||||
|
||||
type = [e type];
|
||||
if (!interceptEvent(e))
|
||||
[realNSApp() sendEvent:e];
|
||||
[realNSApp() updateWindows];
|
||||
|
||||
// GNUstep does this
|
||||
// it also updates the Services menu but there doesn't seem to be a public API for that so
|
||||
if (type != NSPeriodic && type != NSMouseMoved)
|
||||
[[realNSApp() mainMenu] update];
|
||||
|
||||
return 1;
|
||||
}
|
||||
}
|
||||
|
||||
void uiQuit(void)
|
||||
{
|
||||
canQuit = YES;
|
||||
[realNSApp() terminate:realNSApp()];
|
||||
}
|
||||
|
||||
// thanks to mikeash in irc.freenode.net/#macdev for suggesting the use of Grand Central Dispatch for this
|
||||
// LONGTERM will dispatch_get_main_queue() break after _CFRunLoopSetCurrent()?
|
||||
void uiQueueMain(void (*f)(void *data), void *data)
|
||||
{
|
||||
// dispatch_get_main_queue() is a serial queue so it will not execute multiple uiQueueMain() functions concurrently
|
||||
// the signature of f matches dispatch_function_t
|
||||
dispatch_async_f(dispatch_get_main_queue(), data, f);
|
||||
}
|
||||
Vendored
+59
@@ -0,0 +1,59 @@
|
||||
// 17 august 2015
|
||||
#import "uipriv_darwin.h"
|
||||
|
||||
// unfortunately NSMutableDictionary copies its keys, meaning we can't use it for pointers
|
||||
// hence, this file
|
||||
// we could expose a NSMapTable directly, but let's treat all pointers as opaque and hide the implementation, just to be safe and prevent even more rewrites later
|
||||
struct mapTable {
|
||||
NSMapTable *m;
|
||||
};
|
||||
|
||||
struct mapTable *newMap(void)
|
||||
{
|
||||
struct mapTable *m;
|
||||
|
||||
m = uiNew(struct mapTable);
|
||||
m->m = [[NSMapTable alloc] initWithKeyOptions:(NSPointerFunctionsOpaqueMemory | NSPointerFunctionsOpaquePersonality)
|
||||
valueOptions:(NSPointerFunctionsOpaqueMemory | NSPointerFunctionsOpaquePersonality)
|
||||
capacity:0];
|
||||
return m;
|
||||
}
|
||||
|
||||
void mapDestroy(struct mapTable *m)
|
||||
{
|
||||
if ([m->m count] != 0)
|
||||
implbug("attempt to destroy map with items inside");
|
||||
[m->m release];
|
||||
uiFree(m);
|
||||
}
|
||||
|
||||
void *mapGet(struct mapTable *m, void *key)
|
||||
{
|
||||
return NSMapGet(m->m, key);
|
||||
}
|
||||
|
||||
void mapSet(struct mapTable *m, void *key, void *value)
|
||||
{
|
||||
NSMapInsert(m->m, key, value);
|
||||
}
|
||||
|
||||
void mapDelete(struct mapTable *m, void *key)
|
||||
{
|
||||
NSMapRemove(m->m, key);
|
||||
}
|
||||
|
||||
void mapWalk(struct mapTable *m, void (*f)(void *key, void *value))
|
||||
{
|
||||
NSMapEnumerator e = NSEnumerateMapTable(m->m);
|
||||
void *k = NULL;
|
||||
void *v = NULL;
|
||||
while (NSNextMapEnumeratorPair(&e, &k, &v)) {
|
||||
f(k, v);
|
||||
}
|
||||
NSEndMapTableEnumeration(&e);
|
||||
}
|
||||
|
||||
void mapReset(struct mapTable *m)
|
||||
{
|
||||
NSResetMapTable(m->m);
|
||||
}
|
||||
Vendored
+368
@@ -0,0 +1,368 @@
|
||||
// 28 april 2015
|
||||
#import "uipriv_darwin.h"
|
||||
|
||||
static NSMutableArray *menus = nil;
|
||||
static BOOL menusFinalized = NO;
|
||||
|
||||
struct uiMenu {
|
||||
NSMenu *menu;
|
||||
NSMenuItem *item;
|
||||
NSMutableArray *items;
|
||||
};
|
||||
|
||||
struct uiMenuItem {
|
||||
NSMenuItem *item;
|
||||
int type;
|
||||
BOOL disabled;
|
||||
void (*onClicked)(uiMenuItem *, uiWindow *, void *);
|
||||
void *onClickedData;
|
||||
};
|
||||
|
||||
enum {
|
||||
typeRegular,
|
||||
typeCheckbox,
|
||||
typeQuit,
|
||||
typePreferences,
|
||||
typeAbout,
|
||||
typeSeparator,
|
||||
};
|
||||
|
||||
static void mapItemReleaser(void *key, void *value)
|
||||
{
|
||||
uiMenuItem *item;
|
||||
|
||||
item = (uiMenuItem *)value;
|
||||
[item->item release];
|
||||
}
|
||||
|
||||
@implementation menuManager
|
||||
|
||||
- (id)init
|
||||
{
|
||||
self = [super init];
|
||||
if (self) {
|
||||
self->items = newMap();
|
||||
self->hasQuit = NO;
|
||||
self->hasPreferences = NO;
|
||||
self->hasAbout = NO;
|
||||
}
|
||||
return self;
|
||||
}
|
||||
|
||||
- (void)dealloc
|
||||
{
|
||||
mapWalk(self->items, mapItemReleaser);
|
||||
mapReset(self->items);
|
||||
mapDestroy(self->items);
|
||||
uninitMenus();
|
||||
[super dealloc];
|
||||
}
|
||||
|
||||
- (IBAction)onClicked:(id)sender
|
||||
{
|
||||
uiMenuItem *item;
|
||||
|
||||
item = (uiMenuItem *) mapGet(self->items, sender);
|
||||
if (item->type == typeCheckbox)
|
||||
uiMenuItemSetChecked(item, !uiMenuItemChecked(item));
|
||||
// use the key window as the source of the menu event; it's the active window
|
||||
(*(item->onClicked))(item, windowFromNSWindow([realNSApp() keyWindow]), item->onClickedData);
|
||||
}
|
||||
|
||||
- (IBAction)onQuitClicked:(id)sender
|
||||
{
|
||||
if (shouldQuit())
|
||||
uiQuit();
|
||||
}
|
||||
|
||||
- (void)register:(NSMenuItem *)item to:(uiMenuItem *)smi
|
||||
{
|
||||
switch (smi->type) {
|
||||
case typeQuit:
|
||||
if (self->hasQuit)
|
||||
userbug("You can't have multiple Quit menu items in one program.");
|
||||
self->hasQuit = YES;
|
||||
break;
|
||||
case typePreferences:
|
||||
if (self->hasPreferences)
|
||||
userbug("You can't have multiple Preferences menu items in one program.");
|
||||
self->hasPreferences = YES;
|
||||
break;
|
||||
case typeAbout:
|
||||
if (self->hasAbout)
|
||||
userbug("You can't have multiple About menu items in one program.");
|
||||
self->hasAbout = YES;
|
||||
break;
|
||||
}
|
||||
mapSet(self->items, item, smi);
|
||||
}
|
||||
|
||||
// on OS X there are two ways to handle menu items being enabled or disabled: automatically and manually
|
||||
// unfortunately, the application menu requires automatic menu handling for the Hide, Hide Others, and Show All items to work correctly
|
||||
// therefore, we have to handle enabling of the other options ourselves
|
||||
- (BOOL)validateMenuItem:(NSMenuItem *)item
|
||||
{
|
||||
uiMenuItem *smi;
|
||||
|
||||
// disable the special items if they aren't present
|
||||
if (item == self.quitItem && !self->hasQuit)
|
||||
return NO;
|
||||
if (item == self.preferencesItem && !self->hasPreferences)
|
||||
return NO;
|
||||
if (item == self.aboutItem && !self->hasAbout)
|
||||
return NO;
|
||||
// then poll the item's enabled/disabled state
|
||||
smi = (uiMenuItem *) mapGet(self->items, item);
|
||||
return !smi->disabled;
|
||||
}
|
||||
|
||||
// Cocoa constructs the default application menu by hand for each program; that's what MainMenu.[nx]ib does
|
||||
- (void)buildApplicationMenu:(NSMenu *)menubar
|
||||
{
|
||||
NSString *appName;
|
||||
NSMenuItem *appMenuItem;
|
||||
NSMenu *appMenu;
|
||||
NSMenuItem *item;
|
||||
NSString *title;
|
||||
NSMenu *servicesMenu;
|
||||
|
||||
// note: no need to call setAppleMenu: on this anymore; see https://developer.apple.com/library/mac/releasenotes/AppKit/RN-AppKitOlderNotes/#X10_6Notes
|
||||
appName = [[NSProcessInfo processInfo] processName];
|
||||
appMenuItem = [[[NSMenuItem alloc] initWithTitle:appName action:NULL keyEquivalent:@""] autorelease];
|
||||
appMenu = [[[NSMenu alloc] initWithTitle:appName] autorelease];
|
||||
[appMenuItem setSubmenu:appMenu];
|
||||
[menubar addItem:appMenuItem];
|
||||
|
||||
// first is About
|
||||
title = [@"About " stringByAppendingString:appName];
|
||||
item = [[[NSMenuItem alloc] initWithTitle:title action:@selector(onClicked:) keyEquivalent:@""] autorelease];
|
||||
[item setTarget:self];
|
||||
[appMenu addItem:item];
|
||||
self.aboutItem = item;
|
||||
|
||||
[appMenu addItem:[NSMenuItem separatorItem]];
|
||||
|
||||
// next is Preferences
|
||||
item = [[[NSMenuItem alloc] initWithTitle:@"Preferences…" action:@selector(onClicked:) keyEquivalent:@","] autorelease];
|
||||
[item setTarget:self];
|
||||
[appMenu addItem:item];
|
||||
self.preferencesItem = item;
|
||||
|
||||
[appMenu addItem:[NSMenuItem separatorItem]];
|
||||
|
||||
// next is Services
|
||||
item = [[[NSMenuItem alloc] initWithTitle:@"Services" action:NULL keyEquivalent:@""] autorelease];
|
||||
servicesMenu = [[[NSMenu alloc] initWithTitle:@"Services"] autorelease];
|
||||
[item setSubmenu:servicesMenu];
|
||||
[realNSApp() setServicesMenu:servicesMenu];
|
||||
[appMenu addItem:item];
|
||||
|
||||
[appMenu addItem:[NSMenuItem separatorItem]];
|
||||
|
||||
// next are the three hiding options
|
||||
title = [@"Hide " stringByAppendingString:appName];
|
||||
item = [[[NSMenuItem alloc] initWithTitle:title action:@selector(hide:) keyEquivalent:@"h"] autorelease];
|
||||
// the .xib file says they go to -1 ("First Responder", which sounds wrong...)
|
||||
// to do that, we simply leave the target as nil
|
||||
[appMenu addItem:item];
|
||||
item = [[[NSMenuItem alloc] initWithTitle:@"Hide Others" action:@selector(hideOtherApplications:) keyEquivalent:@"h"] autorelease];
|
||||
[item setKeyEquivalentModifierMask:(NSAlternateKeyMask | NSCommandKeyMask)];
|
||||
[appMenu addItem:item];
|
||||
item = [[[NSMenuItem alloc] initWithTitle:@"Show All" action:@selector(unhideAllApplications:) keyEquivalent:@""] autorelease];
|
||||
[appMenu addItem:item];
|
||||
|
||||
[appMenu addItem:[NSMenuItem separatorItem]];
|
||||
|
||||
// and finally Quit
|
||||
// DON'T use @selector(terminate:) as the action; we handle termination ourselves
|
||||
title = [@"Quit " stringByAppendingString:appName];
|
||||
item = [[[NSMenuItem alloc] initWithTitle:title action:@selector(onQuitClicked:) keyEquivalent:@"q"] autorelease];
|
||||
[item setTarget:self];
|
||||
[appMenu addItem:item];
|
||||
self.quitItem = item;
|
||||
}
|
||||
|
||||
- (NSMenu *)makeMenubar
|
||||
{
|
||||
NSMenu *menubar;
|
||||
|
||||
menubar = [[[NSMenu alloc] initWithTitle:@""] autorelease];
|
||||
[self buildApplicationMenu:menubar];
|
||||
return menubar;
|
||||
}
|
||||
|
||||
@end
|
||||
|
||||
static void defaultOnClicked(uiMenuItem *item, uiWindow *w, void *data)
|
||||
{
|
||||
// do nothing
|
||||
}
|
||||
|
||||
void uiMenuItemEnable(uiMenuItem *item)
|
||||
{
|
||||
item->disabled = NO;
|
||||
// we don't need to explicitly update the menus here; they'll be updated the next time they're opened (thanks mikeash in irc.freenode.net/#macdev)
|
||||
}
|
||||
|
||||
void uiMenuItemDisable(uiMenuItem *item)
|
||||
{
|
||||
item->disabled = YES;
|
||||
}
|
||||
|
||||
void uiMenuItemOnClicked(uiMenuItem *item, void (*f)(uiMenuItem *, uiWindow *, void *), void *data)
|
||||
{
|
||||
if (item->type == typeQuit)
|
||||
userbug("You can't call uiMenuItemOnClicked() on a Quit item; use uiOnShouldQuit() instead.");
|
||||
item->onClicked = f;
|
||||
item->onClickedData = data;
|
||||
}
|
||||
|
||||
int uiMenuItemChecked(uiMenuItem *item)
|
||||
{
|
||||
return [item->item state] != NSOffState;
|
||||
}
|
||||
|
||||
void uiMenuItemSetChecked(uiMenuItem *item, int checked)
|
||||
{
|
||||
NSInteger state;
|
||||
|
||||
state = NSOffState;
|
||||
if ([item->item state] == NSOffState)
|
||||
state = NSOnState;
|
||||
[item->item setState:state];
|
||||
}
|
||||
|
||||
static uiMenuItem *newItem(uiMenu *m, int type, const char *name)
|
||||
{
|
||||
@autoreleasepool {
|
||||
|
||||
uiMenuItem *item;
|
||||
|
||||
if (menusFinalized)
|
||||
userbug("You can't create a new menu item after menus have been finalized.");
|
||||
|
||||
item = uiNew(uiMenuItem);
|
||||
|
||||
item->type = type;
|
||||
switch (item->type) {
|
||||
case typeQuit:
|
||||
item->item = [appDelegate().menuManager.quitItem retain];
|
||||
break;
|
||||
case typePreferences:
|
||||
item->item = [appDelegate().menuManager.preferencesItem retain];
|
||||
break;
|
||||
case typeAbout:
|
||||
item->item = [appDelegate().menuManager.aboutItem retain];
|
||||
break;
|
||||
case typeSeparator:
|
||||
item->item = [[NSMenuItem separatorItem] retain];
|
||||
[m->menu addItem:item->item];
|
||||
break;
|
||||
default:
|
||||
item->item = [[NSMenuItem alloc] initWithTitle:toNSString(name) action:@selector(onClicked:) keyEquivalent:@""];
|
||||
[item->item setTarget:appDelegate().menuManager];
|
||||
[m->menu addItem:item->item];
|
||||
break;
|
||||
}
|
||||
|
||||
[appDelegate().menuManager register:item->item to:item];
|
||||
item->onClicked = defaultOnClicked;
|
||||
|
||||
[m->items addObject:[NSValue valueWithPointer:item]];
|
||||
|
||||
return item;
|
||||
|
||||
} // @autoreleasepool
|
||||
}
|
||||
|
||||
uiMenuItem *uiMenuAppendItem(uiMenu *m, const char *name)
|
||||
{
|
||||
return newItem(m, typeRegular, name);
|
||||
}
|
||||
|
||||
uiMenuItem *uiMenuAppendCheckItem(uiMenu *m, const char *name)
|
||||
{
|
||||
return newItem(m, typeCheckbox, name);
|
||||
}
|
||||
|
||||
uiMenuItem *uiMenuAppendQuitItem(uiMenu *m)
|
||||
{
|
||||
// duplicate check is in the register:to: selector
|
||||
return newItem(m, typeQuit, NULL);
|
||||
}
|
||||
|
||||
uiMenuItem *uiMenuAppendPreferencesItem(uiMenu *m)
|
||||
{
|
||||
// duplicate check is in the register:to: selector
|
||||
return newItem(m, typePreferences, NULL);
|
||||
}
|
||||
|
||||
uiMenuItem *uiMenuAppendAboutItem(uiMenu *m)
|
||||
{
|
||||
// duplicate check is in the register:to: selector
|
||||
return newItem(m, typeAbout, NULL);
|
||||
}
|
||||
|
||||
void uiMenuAppendSeparator(uiMenu *m)
|
||||
{
|
||||
newItem(m, typeSeparator, NULL);
|
||||
}
|
||||
|
||||
uiMenu *uiNewMenu(const char *name)
|
||||
{
|
||||
@autoreleasepool {
|
||||
|
||||
uiMenu *m;
|
||||
|
||||
if (menusFinalized)
|
||||
userbug("You can't create a new menu after menus have been finalized.");
|
||||
if (menus == nil)
|
||||
menus = [NSMutableArray new];
|
||||
|
||||
m = uiNew(uiMenu);
|
||||
|
||||
m->menu = [[NSMenu alloc] initWithTitle:toNSString(name)];
|
||||
// use automatic menu item enabling for all menus for consistency's sake
|
||||
|
||||
m->item = [[NSMenuItem alloc] initWithTitle:toNSString(name) action:NULL keyEquivalent:@""];
|
||||
[m->item setSubmenu:m->menu];
|
||||
|
||||
m->items = [NSMutableArray new];
|
||||
|
||||
[[realNSApp() mainMenu] addItem:m->item];
|
||||
|
||||
[menus addObject:[NSValue valueWithPointer:m]];
|
||||
|
||||
return m;
|
||||
|
||||
} // @autoreleasepool
|
||||
}
|
||||
|
||||
void finalizeMenus(void)
|
||||
{
|
||||
menusFinalized = YES;
|
||||
}
|
||||
|
||||
void uninitMenus(void)
|
||||
{
|
||||
if (menus == NULL)
|
||||
return;
|
||||
[menus enumerateObjectsUsingBlock:^(id obj, NSUInteger index, BOOL *stop) {
|
||||
NSValue *v;
|
||||
uiMenu *m;
|
||||
|
||||
v = (NSValue *) obj;
|
||||
m = (uiMenu *) [v pointerValue];
|
||||
[m->items enumerateObjectsUsingBlock:^(id obj, NSUInteger index, BOOL *stop) {
|
||||
NSValue *v;
|
||||
uiMenuItem *mi;
|
||||
|
||||
v = (NSValue *) obj;
|
||||
mi = (uiMenuItem *) [v pointerValue];
|
||||
uiFree(mi);
|
||||
}];
|
||||
[m->items release];
|
||||
uiFree(m);
|
||||
}];
|
||||
[menus release];
|
||||
}
|
||||
Vendored
+233
@@ -0,0 +1,233 @@
|
||||
// 8 december 2015
|
||||
#import "uipriv_darwin.h"
|
||||
|
||||
// NSTextView has no intrinsic content size by default, which wreaks havoc on a pure-Auto Layout system
|
||||
// we'll have to take over to get it to work
|
||||
// see also http://stackoverflow.com/questions/24210153/nstextview-not-properly-resizing-with-auto-layout and http://stackoverflow.com/questions/11237622/using-autolayout-with-expanding-nstextviews
|
||||
@interface intrinsicSizeTextView : NSTextView {
|
||||
uiMultilineEntry *libui_e;
|
||||
}
|
||||
- (id)initWithFrame:(NSRect)r e:(uiMultilineEntry *)e;
|
||||
@end
|
||||
|
||||
struct uiMultilineEntry {
|
||||
uiDarwinControl c;
|
||||
NSScrollView *sv;
|
||||
intrinsicSizeTextView *tv;
|
||||
struct scrollViewData *d;
|
||||
void (*onChanged)(uiMultilineEntry *, void *);
|
||||
void *onChangedData;
|
||||
BOOL changing;
|
||||
};
|
||||
|
||||
@implementation intrinsicSizeTextView
|
||||
|
||||
- (id)initWithFrame:(NSRect)r e:(uiMultilineEntry *)e
|
||||
{
|
||||
self = [super initWithFrame:r];
|
||||
if (self)
|
||||
self->libui_e = e;
|
||||
return self;
|
||||
}
|
||||
|
||||
- (NSSize)intrinsicContentSize
|
||||
{
|
||||
NSTextContainer *textContainer;
|
||||
NSLayoutManager *layoutManager;
|
||||
NSRect rect;
|
||||
|
||||
textContainer = [self textContainer];
|
||||
layoutManager = [self layoutManager];
|
||||
[layoutManager ensureLayoutForTextContainer:textContainer];
|
||||
rect = [layoutManager usedRectForTextContainer:textContainer];
|
||||
return rect.size;
|
||||
}
|
||||
|
||||
- (void)didChangeText
|
||||
{
|
||||
[super didChangeText];
|
||||
[self invalidateIntrinsicContentSize];
|
||||
if (!self->libui_e->changing)
|
||||
(*(self->libui_e->onChanged))(self->libui_e, self->libui_e->onChangedData);
|
||||
}
|
||||
|
||||
@end
|
||||
|
||||
uiDarwinControlAllDefaultsExceptDestroy(uiMultilineEntry, sv)
|
||||
|
||||
static void uiMultilineEntryDestroy(uiControl *c)
|
||||
{
|
||||
uiMultilineEntry *e = uiMultilineEntry(c);
|
||||
|
||||
scrollViewFreeData(e->sv, e->d);
|
||||
[e->tv release];
|
||||
[e->sv release];
|
||||
uiFreeControl(uiControl(e));
|
||||
}
|
||||
|
||||
static void defaultOnChanged(uiMultilineEntry *e, void *data)
|
||||
{
|
||||
// do nothing
|
||||
}
|
||||
|
||||
char *uiMultilineEntryText(uiMultilineEntry *e)
|
||||
{
|
||||
return uiDarwinNSStringToText([e->tv string]);
|
||||
}
|
||||
|
||||
void uiMultilineEntrySetText(uiMultilineEntry *e, const char *text)
|
||||
{
|
||||
[[e->tv textStorage] replaceCharactersInRange:NSMakeRange(0, [[e->tv string] length])
|
||||
withString:toNSString(text)];
|
||||
// must be called explicitly according to the documentation of shouldChangeTextInRange:replacementString:
|
||||
e->changing = YES;
|
||||
[e->tv didChangeText];
|
||||
e->changing = NO;
|
||||
}
|
||||
|
||||
// TODO scroll to end?
|
||||
void uiMultilineEntryAppend(uiMultilineEntry *e, const char *text)
|
||||
{
|
||||
[[e->tv textStorage] replaceCharactersInRange:NSMakeRange([[e->tv string] length], 0)
|
||||
withString:toNSString(text)];
|
||||
e->changing = YES;
|
||||
[e->tv didChangeText];
|
||||
e->changing = NO;
|
||||
}
|
||||
|
||||
void uiMultilineEntryOnChanged(uiMultilineEntry *e, void (*f)(uiMultilineEntry *e, void *data), void *data)
|
||||
{
|
||||
e->onChanged = f;
|
||||
e->onChangedData = data;
|
||||
}
|
||||
|
||||
int uiMultilineEntryReadOnly(uiMultilineEntry *e)
|
||||
{
|
||||
return [e->tv isEditable] == NO;
|
||||
}
|
||||
|
||||
void uiMultilineEntrySetReadOnly(uiMultilineEntry *e, int readonly)
|
||||
{
|
||||
BOOL editable;
|
||||
|
||||
editable = YES;
|
||||
if (readonly)
|
||||
editable = NO;
|
||||
[e->tv setEditable:editable];
|
||||
}
|
||||
|
||||
static uiMultilineEntry *finishMultilineEntry(BOOL hscroll)
|
||||
{
|
||||
uiMultilineEntry *e;
|
||||
NSFont *font;
|
||||
struct scrollViewCreateParams p;
|
||||
|
||||
uiDarwinNewControl(uiMultilineEntry, e);
|
||||
|
||||
e->tv = [[intrinsicSizeTextView alloc] initWithFrame:NSZeroRect e:e];
|
||||
|
||||
// verified against Interface Builder for a sufficiently customized text view
|
||||
|
||||
// NSText properties:
|
||||
// this is what Interface Builder sets the background color to
|
||||
[e->tv setBackgroundColor:[NSColor colorWithCalibratedWhite:1.0 alpha:1.0]];
|
||||
[e->tv setDrawsBackground:YES];
|
||||
[e->tv setEditable:YES];
|
||||
[e->tv setSelectable:YES];
|
||||
[e->tv setFieldEditor:NO];
|
||||
[e->tv setRichText:NO];
|
||||
[e->tv setImportsGraphics:NO];
|
||||
[e->tv setUsesFontPanel:NO];
|
||||
[e->tv setRulerVisible:NO];
|
||||
// we'll handle font last
|
||||
// while setAlignment: has been around since 10.0, the named constant "NSTextAlignmentNatural" seems to have only been introduced in 10.11
|
||||
#define ourNSTextAlignmentNatural 4
|
||||
[e->tv setAlignment:ourNSTextAlignmentNatural];
|
||||
// textColor is set to nil, just keep the dfault
|
||||
[e->tv setBaseWritingDirection:NSWritingDirectionNatural];
|
||||
[e->tv setHorizontallyResizable:NO];
|
||||
[e->tv setVerticallyResizable:YES];
|
||||
|
||||
// NSTextView properties:
|
||||
[e->tv setAllowsDocumentBackgroundColorChange:NO];
|
||||
[e->tv setAllowsUndo:YES];
|
||||
// default paragraph style is nil; keep default
|
||||
[e->tv setAllowsImageEditing:NO];
|
||||
[e->tv setAutomaticQuoteSubstitutionEnabled:NO];
|
||||
[e->tv setAutomaticLinkDetectionEnabled:NO];
|
||||
[e->tv setDisplaysLinkToolTips:YES];
|
||||
[e->tv setUsesRuler:NO];
|
||||
[e->tv setUsesInspectorBar:NO];
|
||||
[e->tv setSelectionGranularity:NSSelectByCharacter];
|
||||
// there is a dedicated named insertion point color but oh well
|
||||
[e->tv setInsertionPointColor:[NSColor controlTextColor]];
|
||||
// typing attributes is nil; keep default (we change it below for fonts though)
|
||||
[e->tv setSmartInsertDeleteEnabled:NO];
|
||||
[e->tv setContinuousSpellCheckingEnabled:NO];
|
||||
[e->tv setGrammarCheckingEnabled:NO];
|
||||
[e->tv setUsesFindPanel:YES];
|
||||
[e->tv setEnabledTextCheckingTypes:0];
|
||||
[e->tv setAutomaticDashSubstitutionEnabled:NO];
|
||||
[e->tv setAutomaticDataDetectionEnabled:NO];
|
||||
[e->tv setAutomaticSpellingCorrectionEnabled:NO];
|
||||
[e->tv setAutomaticTextReplacementEnabled:NO];
|
||||
[e->tv setUsesFindBar:NO];
|
||||
[e->tv setIncrementalSearchingEnabled:NO];
|
||||
|
||||
// NSTextContainer properties:
|
||||
[[e->tv textContainer] setWidthTracksTextView:YES];
|
||||
[[e->tv textContainer] setHeightTracksTextView:NO];
|
||||
|
||||
// NSLayoutManager properties:
|
||||
[[e->tv layoutManager] setAllowsNonContiguousLayout:YES];
|
||||
|
||||
// now just to be safe; this will do some of the above but whatever
|
||||
disableAutocorrect(e->tv);
|
||||
|
||||
// see https://developer.apple.com/library/mac/documentation/Cocoa/Conceptual/TextUILayer/Tasks/TextInScrollView.html
|
||||
// notice we don't use the Auto Layout code; see scrollview.m for more details
|
||||
[e->tv setMaxSize:NSMakeSize(CGFLOAT_MAX, CGFLOAT_MAX)];
|
||||
[e->tv setVerticallyResizable:YES];
|
||||
[e->tv setHorizontallyResizable:hscroll];
|
||||
if (hscroll) {
|
||||
[e->tv setAutoresizingMask:(NSViewWidthSizable | NSViewHeightSizable)];
|
||||
[[e->tv textContainer] setWidthTracksTextView:NO];
|
||||
} else {
|
||||
[e->tv setAutoresizingMask:NSViewWidthSizable];
|
||||
[[e->tv textContainer] setWidthTracksTextView:YES];
|
||||
}
|
||||
[[e->tv textContainer] setContainerSize:NSMakeSize(CGFLOAT_MAX, CGFLOAT_MAX)];
|
||||
|
||||
// don't use uiDarwinSetControlFont() directly; we have to do a little extra work to set the font
|
||||
font = [NSFont systemFontOfSize:[NSFont systemFontSizeForControlSize:NSRegularControlSize]];
|
||||
[e->tv setTypingAttributes:[NSDictionary
|
||||
dictionaryWithObject:font
|
||||
forKey:NSFontAttributeName]];
|
||||
// e->tv font from Interface Builder is nil, but setFont:nil throws an exception
|
||||
// let's just set it to the standard control font anyway, just to be safe
|
||||
[e->tv setFont:font];
|
||||
|
||||
memset(&p, 0, sizeof (struct scrollViewCreateParams));
|
||||
p.DocumentView = e->tv;
|
||||
// this is what Interface Builder sets it to
|
||||
p.BackgroundColor = [NSColor colorWithCalibratedWhite:1.0 alpha:1.0];
|
||||
p.DrawsBackground = YES;
|
||||
p.Bordered = YES;
|
||||
p.HScroll = hscroll;
|
||||
p.VScroll = YES;
|
||||
e->sv = mkScrollView(&p, &(e->d));
|
||||
|
||||
uiMultilineEntryOnChanged(e, defaultOnChanged, NULL);
|
||||
|
||||
return e;
|
||||
}
|
||||
|
||||
uiMultilineEntry *uiNewMultilineEntry(void)
|
||||
{
|
||||
return finishMultilineEntry(NO);
|
||||
}
|
||||
|
||||
uiMultilineEntry *uiNewNonWrappingMultilineEntry(void)
|
||||
{
|
||||
return finishMultilineEntry(YES);
|
||||
}
|
||||
Vendored
+78
@@ -0,0 +1,78 @@
|
||||
// 14 august 2015
|
||||
#import "uipriv_darwin.h"
|
||||
|
||||
// NSProgressIndicator has no intrinsic width by default; use the default width in Interface Builder
|
||||
#define progressIndicatorWidth 100
|
||||
|
||||
@interface intrinsicWidthNSProgressIndicator : NSProgressIndicator
|
||||
@end
|
||||
|
||||
@implementation intrinsicWidthNSProgressIndicator
|
||||
|
||||
- (NSSize)intrinsicContentSize
|
||||
{
|
||||
NSSize s;
|
||||
|
||||
s = [super intrinsicContentSize];
|
||||
s.width = progressIndicatorWidth;
|
||||
return s;
|
||||
}
|
||||
|
||||
@end
|
||||
|
||||
struct uiProgressBar {
|
||||
uiDarwinControl c;
|
||||
NSProgressIndicator *pi;
|
||||
};
|
||||
|
||||
uiDarwinControlAllDefaults(uiProgressBar, pi)
|
||||
|
||||
int uiProgressBarValue(uiProgressBar *p)
|
||||
{
|
||||
if ([p->pi isIndeterminate])
|
||||
return -1;
|
||||
return [p->pi doubleValue];
|
||||
}
|
||||
|
||||
void uiProgressBarSetValue(uiProgressBar *p, int value)
|
||||
{
|
||||
if (value == -1) {
|
||||
[p->pi setIndeterminate:YES];
|
||||
[p->pi startAnimation:p->pi];
|
||||
return;
|
||||
}
|
||||
|
||||
if ([p->pi isIndeterminate]) {
|
||||
[p->pi setIndeterminate:NO];
|
||||
[p->pi stopAnimation:p->pi];
|
||||
}
|
||||
|
||||
if (value < 0 || value > 100)
|
||||
userbug("Value %d out of range for a uiProgressBar.", value);
|
||||
|
||||
// on 10.8 there's an animation when the progress bar increases, just like with Aero
|
||||
if (value == 100) {
|
||||
[p->pi setMaxValue:101];
|
||||
[p->pi setDoubleValue:101];
|
||||
[p->pi setDoubleValue:100];
|
||||
[p->pi setMaxValue:100];
|
||||
return;
|
||||
}
|
||||
[p->pi setDoubleValue:((double) (value + 1))];
|
||||
[p->pi setDoubleValue:((double) value)];
|
||||
}
|
||||
|
||||
uiProgressBar *uiNewProgressBar(void)
|
||||
{
|
||||
uiProgressBar *p;
|
||||
|
||||
uiDarwinNewControl(uiProgressBar, p);
|
||||
|
||||
p->pi = [[intrinsicWidthNSProgressIndicator alloc] initWithFrame:NSZeroRect];
|
||||
[p->pi setControlSize:NSRegularControlSize];
|
||||
[p->pi setBezeled:YES];
|
||||
[p->pi setStyle:NSProgressIndicatorBarStyle];
|
||||
[p->pi setIndeterminate:NO];
|
||||
|
||||
return p;
|
||||
}
|
||||
Vendored
+207
@@ -0,0 +1,207 @@
|
||||
// 14 august 2015
|
||||
#import "uipriv_darwin.h"
|
||||
|
||||
// TODO resizing the controlgallery vertically causes the third button to still resize :|
|
||||
|
||||
// In the old days you would use a NSMatrix for this; as of OS X 10.8 this was deprecated and now you need just a bunch of NSButtons with the same superview AND same action method.
|
||||
// This is documented on the NSMatrix page, but the rest of the OS X documentation says to still use NSMatrix.
|
||||
// NSMatrix has weird quirks anyway...
|
||||
|
||||
// LONGTERM 6 units of spacing between buttons, as suggested by Interface Builder?
|
||||
|
||||
@interface radioButtonsDelegate : NSObject {
|
||||
uiRadioButtons *libui_r;
|
||||
}
|
||||
- (id)initWithR:(uiRadioButtons *)r;
|
||||
- (IBAction)onClicked:(id)sender;
|
||||
@end
|
||||
|
||||
struct uiRadioButtons {
|
||||
uiDarwinControl c;
|
||||
NSView *view;
|
||||
NSMutableArray *buttons;
|
||||
NSMutableArray *constraints;
|
||||
NSLayoutConstraint *lastv;
|
||||
radioButtonsDelegate *delegate;
|
||||
void (*onSelected)(uiRadioButtons *, void *);
|
||||
void *onSelectedData;
|
||||
};
|
||||
|
||||
@implementation radioButtonsDelegate
|
||||
|
||||
- (id)initWithR:(uiRadioButtons *)r
|
||||
{
|
||||
self = [super init];
|
||||
if (self)
|
||||
self->libui_r = r;
|
||||
return self;
|
||||
}
|
||||
|
||||
- (IBAction)onClicked:(id)sender
|
||||
{
|
||||
uiRadioButtons *r = self->libui_r;
|
||||
|
||||
(*(r->onSelected))(r, r->onSelectedData);
|
||||
}
|
||||
|
||||
@end
|
||||
|
||||
uiDarwinControlAllDefaultsExceptDestroy(uiRadioButtons, view)
|
||||
|
||||
static void defaultOnSelected(uiRadioButtons *r, void *data)
|
||||
{
|
||||
// do nothing
|
||||
}
|
||||
|
||||
static void uiRadioButtonsDestroy(uiControl *c)
|
||||
{
|
||||
uiRadioButtons *r = uiRadioButtons(c);
|
||||
NSButton *b;
|
||||
|
||||
// drop the constraints
|
||||
[r->view removeConstraints:r->constraints];
|
||||
[r->constraints release];
|
||||
if (r->lastv != nil)
|
||||
[r->lastv release];
|
||||
// destroy the buttons
|
||||
for (b in r->buttons) {
|
||||
[b setTarget:nil];
|
||||
[b removeFromSuperview];
|
||||
}
|
||||
[r->buttons release];
|
||||
// destroy the delegate
|
||||
[r->delegate release];
|
||||
// and destroy ourselves
|
||||
[r->view release];
|
||||
uiFreeControl(uiControl(r));
|
||||
}
|
||||
|
||||
static NSButton *buttonAt(uiRadioButtons *r, int n)
|
||||
{
|
||||
return (NSButton *) [r->buttons objectAtIndex:n];
|
||||
}
|
||||
|
||||
void uiRadioButtonsAppend(uiRadioButtons *r, const char *text)
|
||||
{
|
||||
NSButton *b, *b2;
|
||||
NSLayoutConstraint *constraint;
|
||||
|
||||
b = [[NSButton alloc] initWithFrame:NSZeroRect];
|
||||
[b setTitle:toNSString(text)];
|
||||
[b setButtonType:NSRadioButton];
|
||||
// doesn't seem to have an associated bezel style
|
||||
[b setBordered:NO];
|
||||
[b setTransparent:NO];
|
||||
uiDarwinSetControlFont(b, NSRegularControlSize);
|
||||
[b setTranslatesAutoresizingMaskIntoConstraints:NO];
|
||||
|
||||
[b setTarget:r->delegate];
|
||||
[b setAction:@selector(onClicked:)];
|
||||
|
||||
[r->buttons addObject:b];
|
||||
[r->view addSubview:b];
|
||||
|
||||
// pin horizontally to the edges of the superview
|
||||
constraint = mkConstraint(b, NSLayoutAttributeLeading,
|
||||
NSLayoutRelationEqual,
|
||||
r->view, NSLayoutAttributeLeading,
|
||||
1, 0,
|
||||
@"uiRadioButtons button leading constraint");
|
||||
[r->view addConstraint:constraint];
|
||||
[r->constraints addObject:constraint];
|
||||
constraint = mkConstraint(b, NSLayoutAttributeTrailing,
|
||||
NSLayoutRelationEqual,
|
||||
r->view, NSLayoutAttributeTrailing,
|
||||
1, 0,
|
||||
@"uiRadioButtons button trailing constraint");
|
||||
[r->view addConstraint:constraint];
|
||||
[r->constraints addObject:constraint];
|
||||
|
||||
// if this is the first view, pin it to the top
|
||||
// otherwise pin to the bottom of the last
|
||||
if ([r->buttons count] == 1)
|
||||
constraint = mkConstraint(b, NSLayoutAttributeTop,
|
||||
NSLayoutRelationEqual,
|
||||
r->view, NSLayoutAttributeTop,
|
||||
1, 0,
|
||||
@"uiRadioButtons first button top constraint");
|
||||
else {
|
||||
b2 = buttonAt(r, [r->buttons count] - 2);
|
||||
constraint = mkConstraint(b, NSLayoutAttributeTop,
|
||||
NSLayoutRelationEqual,
|
||||
b2, NSLayoutAttributeBottom,
|
||||
1, 0,
|
||||
@"uiRadioButtons non-first button top constraint");
|
||||
}
|
||||
[r->view addConstraint:constraint];
|
||||
[r->constraints addObject:constraint];
|
||||
|
||||
// if there is a previous bottom constraint, remove it
|
||||
if (r->lastv != nil) {
|
||||
[r->view removeConstraint:r->lastv];
|
||||
[r->constraints removeObject:r->lastv];
|
||||
[r->lastv release];
|
||||
}
|
||||
|
||||
// and make the new bottom constraint
|
||||
r->lastv = mkConstraint(b, NSLayoutAttributeBottom,
|
||||
NSLayoutRelationEqual,
|
||||
r->view, NSLayoutAttributeBottom,
|
||||
1, 0,
|
||||
@"uiRadioButtons last button bottom constraint");
|
||||
[r->view addConstraint:r->lastv];
|
||||
[r->constraints addObject:r->lastv];
|
||||
[r->lastv retain];
|
||||
}
|
||||
|
||||
int uiRadioButtonsSelected(uiRadioButtons *r)
|
||||
{
|
||||
NSButton *b;
|
||||
NSUInteger i;
|
||||
|
||||
for (i = 0; i < [r->buttons count]; i++) {
|
||||
b = (NSButton *) [r->buttons objectAtIndex:i];
|
||||
if ([b state] == NSOnState)
|
||||
return i;
|
||||
}
|
||||
return -1;
|
||||
}
|
||||
|
||||
void uiRadioButtonsSetSelected(uiRadioButtons *r, int n)
|
||||
{
|
||||
NSButton *b;
|
||||
NSInteger state;
|
||||
|
||||
state = NSOnState;
|
||||
if (n == -1) {
|
||||
n = uiRadioButtonsSelected(r);
|
||||
if (n == -1) // from nothing to nothing; do nothing
|
||||
return;
|
||||
state = NSOffState;
|
||||
}
|
||||
b = (NSButton *) [r->buttons objectAtIndex:n];
|
||||
[b setState:state];
|
||||
}
|
||||
|
||||
void uiRadioButtonsOnSelected(uiRadioButtons *r, void (*f)(uiRadioButtons *, void *), void *data)
|
||||
{
|
||||
r->onSelected = f;
|
||||
r->onSelectedData = data;
|
||||
}
|
||||
|
||||
uiRadioButtons *uiNewRadioButtons(void)
|
||||
{
|
||||
uiRadioButtons *r;
|
||||
|
||||
uiDarwinNewControl(uiRadioButtons, r);
|
||||
|
||||
r->view = [[NSView alloc] initWithFrame:NSZeroRect];
|
||||
r->buttons = [NSMutableArray new];
|
||||
r->constraints = [NSMutableArray new];
|
||||
|
||||
r->delegate = [[radioButtonsDelegate alloc] initWithR:r];
|
||||
|
||||
uiRadioButtonsOnSelected(r, defaultOnSelected, NULL);
|
||||
|
||||
return r;
|
||||
}
|
||||
Vendored
+61
@@ -0,0 +1,61 @@
|
||||
// 27 may 2016
|
||||
#include "uipriv_darwin.h"
|
||||
|
||||
// see http://stackoverflow.com/questions/37979445/how-do-i-properly-set-up-a-scrolling-nstableview-using-auto-layout-what-ive-tr for why we don't use auto layout
|
||||
// TODO do the same with uiGroup and uiTab?
|
||||
|
||||
struct scrollViewData {
|
||||
BOOL hscroll;
|
||||
BOOL vscroll;
|
||||
};
|
||||
|
||||
NSScrollView *mkScrollView(struct scrollViewCreateParams *p, struct scrollViewData **dout)
|
||||
{
|
||||
NSScrollView *sv;
|
||||
NSBorderType border;
|
||||
struct scrollViewData *d;
|
||||
|
||||
sv = [[NSScrollView alloc] initWithFrame:NSZeroRect];
|
||||
if (p->BackgroundColor != nil)
|
||||
[sv setBackgroundColor:p->BackgroundColor];
|
||||
[sv setDrawsBackground:p->DrawsBackground];
|
||||
border = NSNoBorder;
|
||||
if (p->Bordered)
|
||||
border = NSBezelBorder;
|
||||
// document view seems to set the cursor properly
|
||||
[sv setBorderType:border];
|
||||
[sv setAutohidesScrollers:YES];
|
||||
[sv setHasHorizontalRuler:NO];
|
||||
[sv setHasVerticalRuler:NO];
|
||||
[sv setRulersVisible:NO];
|
||||
[sv setScrollerKnobStyle:NSScrollerKnobStyleDefault];
|
||||
// the scroller style is documented as being set by default for us
|
||||
// LONGTERM verify line and page for programmatically created NSTableView
|
||||
[sv setScrollsDynamically:YES];
|
||||
[sv setFindBarPosition:NSScrollViewFindBarPositionAboveContent];
|
||||
[sv setUsesPredominantAxisScrolling:NO];
|
||||
[sv setHorizontalScrollElasticity:NSScrollElasticityAutomatic];
|
||||
[sv setVerticalScrollElasticity:NSScrollElasticityAutomatic];
|
||||
[sv setAllowsMagnification:NO];
|
||||
|
||||
[sv setDocumentView:p->DocumentView];
|
||||
d = uiNew(struct scrollViewData);
|
||||
scrollViewSetScrolling(sv, d, p->HScroll, p->VScroll);
|
||||
|
||||
*dout = d;
|
||||
return sv;
|
||||
}
|
||||
|
||||
// based on http://blog.bjhomer.com/2014/08/nsscrollview-and-autolayout.html because (as pointed out there) Apple's official guide is really only for iOS
|
||||
void scrollViewSetScrolling(NSScrollView *sv, struct scrollViewData *d, BOOL hscroll, BOOL vscroll)
|
||||
{
|
||||
d->hscroll = hscroll;
|
||||
[sv setHasHorizontalScroller:d->hscroll];
|
||||
d->vscroll = vscroll;
|
||||
[sv setHasVerticalScroller:d->vscroll];
|
||||
}
|
||||
|
||||
void scrollViewFreeData(NSScrollView *sv, struct scrollViewData *d)
|
||||
{
|
||||
uiFree(d);
|
||||
}
|
||||
Vendored
+45
@@ -0,0 +1,45 @@
|
||||
// 14 august 2015
|
||||
#import "uipriv_darwin.h"
|
||||
|
||||
// TODO make this intrinsic
|
||||
#define separatorWidth 96
|
||||
#define separatorHeight 96
|
||||
|
||||
struct uiSeparator {
|
||||
uiDarwinControl c;
|
||||
NSBox *box;
|
||||
};
|
||||
|
||||
uiDarwinControlAllDefaults(uiSeparator, box)
|
||||
|
||||
uiSeparator *uiNewHorizontalSeparator(void)
|
||||
{
|
||||
uiSeparator *s;
|
||||
|
||||
uiDarwinNewControl(uiSeparator, s);
|
||||
|
||||
// make the initial width >= initial height to force horizontal
|
||||
s->box = [[NSBox alloc] initWithFrame:NSMakeRect(0, 0, 100, 1)];
|
||||
[s->box setBoxType:NSBoxSeparator];
|
||||
[s->box setBorderType:NSGrooveBorder];
|
||||
[s->box setTransparent:NO];
|
||||
[s->box setTitlePosition:NSNoTitle];
|
||||
|
||||
return s;
|
||||
}
|
||||
|
||||
uiSeparator *uiNewVerticalSeparator(void)
|
||||
{
|
||||
uiSeparator *s;
|
||||
|
||||
uiDarwinNewControl(uiSeparator, s);
|
||||
|
||||
// make the initial height >= initial width to force vertical
|
||||
s->box = [[NSBox alloc] initWithFrame:NSMakeRect(0, 0, 1, 100)];
|
||||
[s->box setBoxType:NSBoxSeparator];
|
||||
[s->box setBorderType:NSGrooveBorder];
|
||||
[s->box setTransparent:NO];
|
||||
[s->box setTitlePosition:NSNoTitle];
|
||||
|
||||
return s;
|
||||
}
|
||||
Vendored
+147
@@ -0,0 +1,147 @@
|
||||
// 14 august 2015
|
||||
#import "uipriv_darwin.h"
|
||||
|
||||
// Horizontal sliders have no intrinsic width; we'll use the default Interface Builder width for them.
|
||||
// This will also be used for the initial frame size, to ensure the slider is always horizontal (see below).
|
||||
#define sliderWidth 92
|
||||
|
||||
@interface libui_intrinsicWidthNSSlider : NSSlider
|
||||
@end
|
||||
|
||||
@implementation libui_intrinsicWidthNSSlider
|
||||
|
||||
- (NSSize)intrinsicContentSize
|
||||
{
|
||||
NSSize s;
|
||||
|
||||
s = [super intrinsicContentSize];
|
||||
s.width = sliderWidth;
|
||||
return s;
|
||||
}
|
||||
|
||||
@end
|
||||
|
||||
struct uiSlider {
|
||||
uiDarwinControl c;
|
||||
NSSlider *slider;
|
||||
void (*onChanged)(uiSlider *, void *);
|
||||
void *onChangedData;
|
||||
};
|
||||
|
||||
@interface sliderDelegateClass : NSObject {
|
||||
struct mapTable *sliders;
|
||||
}
|
||||
- (IBAction)onChanged:(id)sender;
|
||||
- (void)registerSlider:(uiSlider *)b;
|
||||
- (void)unregisterSlider:(uiSlider *)b;
|
||||
@end
|
||||
|
||||
@implementation sliderDelegateClass
|
||||
|
||||
- (id)init
|
||||
{
|
||||
self = [super init];
|
||||
if (self)
|
||||
self->sliders = newMap();
|
||||
return self;
|
||||
}
|
||||
|
||||
- (void)dealloc
|
||||
{
|
||||
mapDestroy(self->sliders);
|
||||
[super dealloc];
|
||||
}
|
||||
|
||||
- (IBAction)onChanged:(id)sender
|
||||
{
|
||||
uiSlider *s;
|
||||
|
||||
s = (uiSlider *) mapGet(self->sliders, sender);
|
||||
(*(s->onChanged))(s, s->onChangedData);
|
||||
}
|
||||
|
||||
- (void)registerSlider:(uiSlider *)s
|
||||
{
|
||||
mapSet(self->sliders, s->slider, s);
|
||||
[s->slider setTarget:self];
|
||||
[s->slider setAction:@selector(onChanged:)];
|
||||
}
|
||||
|
||||
- (void)unregisterSlider:(uiSlider *)s
|
||||
{
|
||||
[s->slider setTarget:nil];
|
||||
mapDelete(self->sliders, s->slider);
|
||||
}
|
||||
|
||||
@end
|
||||
|
||||
static sliderDelegateClass *sliderDelegate = nil;
|
||||
|
||||
uiDarwinControlAllDefaultsExceptDestroy(uiSlider, slider)
|
||||
|
||||
static void uiSliderDestroy(uiControl *c)
|
||||
{
|
||||
uiSlider *s = uiSlider(c);
|
||||
|
||||
[sliderDelegate unregisterSlider:s];
|
||||
[s->slider release];
|
||||
uiFreeControl(uiControl(s));
|
||||
}
|
||||
|
||||
int uiSliderValue(uiSlider *s)
|
||||
{
|
||||
return [s->slider integerValue];
|
||||
}
|
||||
|
||||
void uiSliderSetValue(uiSlider *s, int value)
|
||||
{
|
||||
[s->slider setIntegerValue:value];
|
||||
}
|
||||
|
||||
void uiSliderOnChanged(uiSlider *s, void (*f)(uiSlider *, void *), void *data)
|
||||
{
|
||||
s->onChanged = f;
|
||||
s->onChangedData = data;
|
||||
}
|
||||
|
||||
static void defaultOnChanged(uiSlider *s, void *data)
|
||||
{
|
||||
// do nothing
|
||||
}
|
||||
|
||||
uiSlider *uiNewSlider(int min, int max)
|
||||
{
|
||||
uiSlider *s;
|
||||
NSSliderCell *cell;
|
||||
int temp;
|
||||
|
||||
if (min >= max) {
|
||||
temp = min;
|
||||
min = max;
|
||||
max = temp;
|
||||
}
|
||||
|
||||
uiDarwinNewControl(uiSlider, s);
|
||||
|
||||
// a horizontal slider is defined as one where the width > height, not by a flag
|
||||
// to be safe, don't use NSZeroRect, but make it horizontal from the get-go
|
||||
s->slider = [[libui_intrinsicWidthNSSlider alloc]
|
||||
initWithFrame:NSMakeRect(0, 0, sliderWidth, 2)];
|
||||
[s->slider setMinValue:min];
|
||||
[s->slider setMaxValue:max];
|
||||
[s->slider setAllowsTickMarkValuesOnly:NO];
|
||||
[s->slider setNumberOfTickMarks:0];
|
||||
[s->slider setTickMarkPosition:NSTickMarkAbove];
|
||||
|
||||
cell = (NSSliderCell *) [s->slider cell];
|
||||
[cell setSliderType:NSLinearSlider];
|
||||
|
||||
if (sliderDelegate == nil) {
|
||||
sliderDelegate = [[sliderDelegateClass new] autorelease];
|
||||
[delegates addObject:sliderDelegate];
|
||||
}
|
||||
[sliderDelegate registerSlider:s];
|
||||
uiSliderOnChanged(s, defaultOnChanged, NULL);
|
||||
|
||||
return s;
|
||||
}
|
||||
Vendored
+214
@@ -0,0 +1,214 @@
|
||||
// 14 august 2015
|
||||
#import "uipriv_darwin.h"
|
||||
|
||||
@interface libui_spinbox : NSView<NSTextFieldDelegate> {
|
||||
NSTextField *tf;
|
||||
NSNumberFormatter *formatter;
|
||||
NSStepper *stepper;
|
||||
|
||||
NSInteger value;
|
||||
NSInteger minimum;
|
||||
NSInteger maximum;
|
||||
|
||||
uiSpinbox *spinbox;
|
||||
}
|
||||
- (id)initWithFrame:(NSRect)r spinbox:(uiSpinbox *)sb;
|
||||
// see https://github.com/andlabs/ui/issues/82
|
||||
- (NSInteger)libui_value;
|
||||
- (void)libui_setValue:(NSInteger)val;
|
||||
- (void)setMinimum:(NSInteger)min;
|
||||
- (void)setMaximum:(NSInteger)max;
|
||||
- (IBAction)stepperClicked:(id)sender;
|
||||
- (void)controlTextDidChange:(NSNotification *)note;
|
||||
@end
|
||||
|
||||
struct uiSpinbox {
|
||||
uiDarwinControl c;
|
||||
libui_spinbox *spinbox;
|
||||
void (*onChanged)(uiSpinbox *, void *);
|
||||
void *onChangedData;
|
||||
};
|
||||
|
||||
// yes folks, this varies by operating system! woo!
|
||||
// 10.10 started drawing the NSStepper one point too low, so we have to fix it up conditionally
|
||||
// TODO test this; we'll probably have to substitute 10_9
|
||||
static CGFloat stepperYDelta(void)
|
||||
{
|
||||
// via https://developer.apple.com/library/mac/releasenotes/AppKit/RN-AppKit/
|
||||
if (floor(NSAppKitVersionNumber) <= NSAppKitVersionNumber10_9)
|
||||
return 0;
|
||||
return -1;
|
||||
}
|
||||
|
||||
@implementation libui_spinbox
|
||||
|
||||
- (id)initWithFrame:(NSRect)r spinbox:(uiSpinbox *)sb
|
||||
{
|
||||
self = [super initWithFrame:r];
|
||||
if (self) {
|
||||
self->tf = newEditableTextField();
|
||||
[self->tf setTranslatesAutoresizingMaskIntoConstraints:NO];
|
||||
|
||||
self->formatter = [NSNumberFormatter new];
|
||||
[self->formatter setFormatterBehavior:NSNumberFormatterBehavior10_4];
|
||||
[self->formatter setLocalizesFormat:NO];
|
||||
[self->formatter setUsesGroupingSeparator:NO];
|
||||
[self->formatter setHasThousandSeparators:NO];
|
||||
[self->formatter setAllowsFloats:NO];
|
||||
[self->tf setFormatter:self->formatter];
|
||||
|
||||
self->stepper = [[NSStepper alloc] initWithFrame:NSZeroRect];
|
||||
[self->stepper setIncrement:1];
|
||||
[self->stepper setValueWraps:NO];
|
||||
[self->stepper setAutorepeat:YES]; // hold mouse button to step repeatedly
|
||||
[self->stepper setTranslatesAutoresizingMaskIntoConstraints:NO];
|
||||
|
||||
[self->tf setDelegate:self];
|
||||
[self->stepper setTarget:self];
|
||||
[self->stepper setAction:@selector(stepperClicked:)];
|
||||
|
||||
[self addSubview:self->tf];
|
||||
[self addSubview:self->stepper];
|
||||
|
||||
[self addConstraint:mkConstraint(self->tf, NSLayoutAttributeLeading,
|
||||
NSLayoutRelationEqual,
|
||||
self, NSLayoutAttributeLeading,
|
||||
1, 0,
|
||||
@"uiSpinbox left edge")];
|
||||
[self addConstraint:mkConstraint(self->stepper, NSLayoutAttributeTrailing,
|
||||
NSLayoutRelationEqual,
|
||||
self, NSLayoutAttributeTrailing,
|
||||
1, 0,
|
||||
@"uiSpinbox right edge")];
|
||||
[self addConstraint:mkConstraint(self->tf, NSLayoutAttributeTop,
|
||||
NSLayoutRelationEqual,
|
||||
self, NSLayoutAttributeTop,
|
||||
1, 0,
|
||||
@"uiSpinbox top edge text field")];
|
||||
[self addConstraint:mkConstraint(self->tf, NSLayoutAttributeBottom,
|
||||
NSLayoutRelationEqual,
|
||||
self, NSLayoutAttributeBottom,
|
||||
1, 0,
|
||||
@"uiSpinbox bottom edge text field")];
|
||||
[self addConstraint:mkConstraint(self->stepper, NSLayoutAttributeTop,
|
||||
NSLayoutRelationEqual,
|
||||
self, NSLayoutAttributeTop,
|
||||
1, stepperYDelta(),
|
||||
@"uiSpinbox top edge stepper")];
|
||||
[self addConstraint:mkConstraint(self->stepper, NSLayoutAttributeBottom,
|
||||
NSLayoutRelationEqual,
|
||||
self, NSLayoutAttributeBottom,
|
||||
1, stepperYDelta(),
|
||||
@"uiSpinbox bottom edge stepper")];
|
||||
[self addConstraint:mkConstraint(self->tf, NSLayoutAttributeTrailing,
|
||||
NSLayoutRelationEqual,
|
||||
self->stepper, NSLayoutAttributeLeading,
|
||||
1, -3, // arbitrary amount; good enough visually (and it seems to match NSDatePicker too, at least on 10.11, which is even better)
|
||||
@"uiSpinbox space between text field and stepper")];
|
||||
|
||||
self->spinbox = sb;
|
||||
}
|
||||
return self;
|
||||
}
|
||||
|
||||
- (void)dealloc
|
||||
{
|
||||
[self->tf setDelegate:nil];
|
||||
[self->tf removeFromSuperview];
|
||||
[self->tf release];
|
||||
[self->formatter release];
|
||||
[self->stepper setTarget:nil];
|
||||
[self->stepper removeFromSuperview];
|
||||
[self->stepper release];
|
||||
[super dealloc];
|
||||
}
|
||||
|
||||
- (NSInteger)libui_value
|
||||
{
|
||||
return self->value;
|
||||
}
|
||||
|
||||
- (void)libui_setValue:(NSInteger)val
|
||||
{
|
||||
self->value = val;
|
||||
if (self->value < self->minimum)
|
||||
self->value = self->minimum;
|
||||
if (self->value > self->maximum)
|
||||
self->value = self->maximum;
|
||||
[self->tf setIntegerValue:self->value];
|
||||
[self->stepper setIntegerValue:self->value];
|
||||
}
|
||||
|
||||
- (void)setMinimum:(NSInteger)min
|
||||
{
|
||||
self->minimum = min;
|
||||
[self->formatter setMinimum:[NSNumber numberWithInteger:self->minimum]];
|
||||
[self->stepper setMinValue:((double) (self->minimum))];
|
||||
}
|
||||
|
||||
- (void)setMaximum:(NSInteger)max
|
||||
{
|
||||
self->maximum = max;
|
||||
[self->formatter setMaximum:[NSNumber numberWithInteger:self->maximum]];
|
||||
[self->stepper setMaxValue:((double) (self->maximum))];
|
||||
}
|
||||
|
||||
- (IBAction)stepperClicked:(id)sender
|
||||
{
|
||||
[self libui_setValue:[self->stepper integerValue]];
|
||||
(*(self->spinbox->onChanged))(self->spinbox, self->spinbox->onChangedData);
|
||||
}
|
||||
|
||||
- (void)controlTextDidChange:(NSNotification *)note
|
||||
{
|
||||
[self libui_setValue:[self->tf integerValue]];
|
||||
(*(self->spinbox->onChanged))(self->spinbox, self->spinbox->onChangedData);
|
||||
}
|
||||
|
||||
@end
|
||||
|
||||
uiDarwinControlAllDefaults(uiSpinbox, spinbox)
|
||||
|
||||
int uiSpinboxValue(uiSpinbox *s)
|
||||
{
|
||||
return [s->spinbox libui_value];
|
||||
}
|
||||
|
||||
void uiSpinboxSetValue(uiSpinbox *s, int value)
|
||||
{
|
||||
[s->spinbox libui_setValue:value];
|
||||
}
|
||||
|
||||
void uiSpinboxOnChanged(uiSpinbox *s, void (*f)(uiSpinbox *, void *), void *data)
|
||||
{
|
||||
s->onChanged = f;
|
||||
s->onChangedData = data;
|
||||
}
|
||||
|
||||
static void defaultOnChanged(uiSpinbox *s, void *data)
|
||||
{
|
||||
// do nothing
|
||||
}
|
||||
|
||||
uiSpinbox *uiNewSpinbox(int min, int max)
|
||||
{
|
||||
uiSpinbox *s;
|
||||
int temp;
|
||||
|
||||
if (min >= max) {
|
||||
temp = min;
|
||||
min = max;
|
||||
max = temp;
|
||||
}
|
||||
|
||||
uiDarwinNewControl(uiSpinbox, s);
|
||||
|
||||
s->spinbox = [[libui_spinbox alloc] initWithFrame:NSZeroRect spinbox:s];
|
||||
[s->spinbox setMinimum:min];
|
||||
[s->spinbox setMaximum:max];
|
||||
[s->spinbox libui_setValue:min];
|
||||
|
||||
uiSpinboxOnChanged(s, defaultOnChanged, NULL);
|
||||
|
||||
return s;
|
||||
}
|
||||
Vendored
+123
@@ -0,0 +1,123 @@
|
||||
// 26 june 2015
|
||||
#import "uipriv_darwin.h"
|
||||
|
||||
// LONGTERM restructure this whole file
|
||||
// LONGTERM explicitly document this works as we want
|
||||
// LONGTERM note that font and color buttons also do this
|
||||
|
||||
#define windowWindow(w) ((NSWindow *) uiControlHandle(uiControl(w)))
|
||||
|
||||
// source of code modal logic: http://stackoverflow.com/questions/604768/wait-for-nsalert-beginsheetmodalforwindow
|
||||
|
||||
// note: whether extensions are actually shown depends on a user setting in Finder; we can't control it here
|
||||
static void setupSavePanel(NSSavePanel *s)
|
||||
{
|
||||
[s setCanCreateDirectories:YES];
|
||||
[s setShowsHiddenFiles:YES];
|
||||
[s setExtensionHidden:NO];
|
||||
[s setCanSelectHiddenExtension:NO];
|
||||
[s setTreatsFilePackagesAsDirectories:YES];
|
||||
}
|
||||
|
||||
static char *runSavePanel(NSWindow *parent, NSSavePanel *s)
|
||||
{
|
||||
char *filename;
|
||||
|
||||
[s beginSheetModalForWindow:parent completionHandler:^(NSInteger result) {
|
||||
[realNSApp() stopModalWithCode:result];
|
||||
}];
|
||||
if ([realNSApp() runModalForWindow:s] != NSFileHandlingPanelOKButton)
|
||||
return NULL;
|
||||
filename = uiDarwinNSStringToText([[s URL] path]);
|
||||
return filename;
|
||||
}
|
||||
|
||||
char *uiOpenFile(uiWindow *parent)
|
||||
{
|
||||
NSOpenPanel *o;
|
||||
|
||||
o = [NSOpenPanel openPanel];
|
||||
[o setCanChooseFiles:YES];
|
||||
[o setCanChooseDirectories:NO];
|
||||
[o setResolvesAliases:NO];
|
||||
[o setAllowsMultipleSelection:NO];
|
||||
setupSavePanel(o);
|
||||
// panel is autoreleased
|
||||
return runSavePanel(windowWindow(parent), o);
|
||||
}
|
||||
|
||||
char *uiSaveFile(uiWindow *parent)
|
||||
{
|
||||
NSSavePanel *s;
|
||||
|
||||
s = [NSSavePanel savePanel];
|
||||
setupSavePanel(s);
|
||||
// panel is autoreleased
|
||||
return runSavePanel(windowWindow(parent), s);
|
||||
}
|
||||
|
||||
// I would use a completion handler for NSAlert as well, but alas NSAlert's are 10.9 and higher only
|
||||
@interface libuiCodeModalAlertPanel : NSObject {
|
||||
NSAlert *panel;
|
||||
NSWindow *parent;
|
||||
}
|
||||
- (id)initWithPanel:(NSAlert *)p parent:(NSWindow *)w;
|
||||
- (NSInteger)run;
|
||||
- (void)panelEnded:(NSAlert *)panel result:(NSInteger)result data:(void *)data;
|
||||
@end
|
||||
|
||||
@implementation libuiCodeModalAlertPanel
|
||||
|
||||
- (id)initWithPanel:(NSAlert *)p parent:(NSWindow *)w
|
||||
{
|
||||
self = [super init];
|
||||
if (self) {
|
||||
self->panel = p;
|
||||
self->parent = w;
|
||||
}
|
||||
return self;
|
||||
}
|
||||
|
||||
- (NSInteger)run
|
||||
{
|
||||
[self->panel beginSheetModalForWindow:self->parent
|
||||
modalDelegate:self
|
||||
didEndSelector:@selector(panelEnded:result:data:)
|
||||
contextInfo:NULL];
|
||||
return [realNSApp() runModalForWindow:[self->panel window]];
|
||||
}
|
||||
|
||||
- (void)panelEnded:(NSAlert *)panel result:(NSInteger)result data:(void *)data
|
||||
{
|
||||
[realNSApp() stopModalWithCode:result];
|
||||
}
|
||||
|
||||
@end
|
||||
|
||||
static void msgbox(NSWindow *parent, const char *title, const char *description, NSAlertStyle style)
|
||||
{
|
||||
NSAlert *a;
|
||||
libuiCodeModalAlertPanel *cm;
|
||||
|
||||
a = [NSAlert new];
|
||||
[a setAlertStyle:style];
|
||||
[a setShowsHelp:NO];
|
||||
[a setShowsSuppressionButton:NO];
|
||||
[a setMessageText:toNSString(title)];
|
||||
[a setInformativeText:toNSString(description)];
|
||||
[a addButtonWithTitle:@"OK"];
|
||||
cm = [[libuiCodeModalAlertPanel alloc] initWithPanel:a parent:parent];
|
||||
[cm run];
|
||||
[cm release];
|
||||
[a release];
|
||||
}
|
||||
|
||||
void uiMsgBox(uiWindow *parent, const char *title, const char *description)
|
||||
{
|
||||
msgbox(windowWindow(parent), title, description, NSInformationalAlertStyle);
|
||||
}
|
||||
|
||||
void uiMsgBoxError(uiWindow *parent, const char *title, const char *description)
|
||||
{
|
||||
msgbox(windowWindow(parent), title, description, NSCriticalAlertStyle);
|
||||
}
|
||||
Vendored
+292
@@ -0,0 +1,292 @@
|
||||
// 15 august 2015
|
||||
#import "uipriv_darwin.h"
|
||||
|
||||
// TODO need to jiggle on tab change too (second page disabled tab label initially ambiguous)
|
||||
|
||||
@interface tabPage : NSObject {
|
||||
struct singleChildConstraints constraints;
|
||||
int margined;
|
||||
NSView *view; // the NSTabViewItem view itself
|
||||
NSObject *pageID;
|
||||
}
|
||||
@property uiControl *c;
|
||||
@property NSLayoutPriority oldHorzHuggingPri;
|
||||
@property NSLayoutPriority oldVertHuggingPri;
|
||||
- (id)initWithView:(NSView *)v pageID:(NSObject *)o;
|
||||
- (NSView *)childView;
|
||||
- (void)establishChildConstraints;
|
||||
- (void)removeChildConstraints;
|
||||
- (int)isMargined;
|
||||
- (void)setMargined:(int)m;
|
||||
@end
|
||||
|
||||
struct uiTab {
|
||||
uiDarwinControl c;
|
||||
NSTabView *tabview;
|
||||
NSMutableArray *pages;
|
||||
NSLayoutPriority horzHuggingPri;
|
||||
NSLayoutPriority vertHuggingPri;
|
||||
};
|
||||
|
||||
@implementation tabPage
|
||||
|
||||
- (id)initWithView:(NSView *)v pageID:(NSObject *)o
|
||||
{
|
||||
self = [super init];
|
||||
if (self != nil) {
|
||||
self->view = [v retain];
|
||||
self->pageID = [o retain];
|
||||
}
|
||||
return self;
|
||||
}
|
||||
|
||||
- (void)dealloc
|
||||
{
|
||||
[self removeChildConstraints];
|
||||
[self->view release];
|
||||
[self->pageID release];
|
||||
[super dealloc];
|
||||
}
|
||||
|
||||
- (NSView *)childView
|
||||
{
|
||||
return (NSView *) uiControlHandle(self.c);
|
||||
}
|
||||
|
||||
- (void)establishChildConstraints
|
||||
{
|
||||
[self removeChildConstraints];
|
||||
if (self.c == NULL)
|
||||
return;
|
||||
singleChildConstraintsEstablish(&(self->constraints),
|
||||
self->view, [self childView],
|
||||
uiDarwinControlHugsTrailingEdge(uiDarwinControl(self.c)),
|
||||
uiDarwinControlHugsBottom(uiDarwinControl(self.c)),
|
||||
self->margined,
|
||||
@"uiTab page");
|
||||
}
|
||||
|
||||
- (void)removeChildConstraints
|
||||
{
|
||||
singleChildConstraintsRemove(&(self->constraints), self->view);
|
||||
}
|
||||
|
||||
- (int)isMargined
|
||||
{
|
||||
return self->margined;
|
||||
}
|
||||
|
||||
- (void)setMargined:(int)m
|
||||
{
|
||||
self->margined = m;
|
||||
singleChildConstraintsSetMargined(&(self->constraints), self->margined);
|
||||
}
|
||||
|
||||
@end
|
||||
|
||||
static void uiTabDestroy(uiControl *c)
|
||||
{
|
||||
uiTab *t = uiTab(c);
|
||||
tabPage *page;
|
||||
|
||||
// first remove all tab pages so we can destroy all the children
|
||||
while ([t->tabview numberOfTabViewItems] != 0)
|
||||
[t->tabview removeTabViewItem:[t->tabview tabViewItemAtIndex:0]];
|
||||
// then destroy all the children
|
||||
for (page in t->pages) {
|
||||
[page removeChildConstraints];
|
||||
uiControlSetParent(page.c, NULL);
|
||||
uiDarwinControlSetSuperview(uiDarwinControl(page.c), nil);
|
||||
uiControlDestroy(page.c);
|
||||
}
|
||||
// and finally destroy ourselves
|
||||
[t->pages release];
|
||||
[t->tabview release];
|
||||
uiFreeControl(uiControl(t));
|
||||
}
|
||||
|
||||
uiDarwinControlDefaultHandle(uiTab, tabview)
|
||||
uiDarwinControlDefaultParent(uiTab, tabview)
|
||||
uiDarwinControlDefaultSetParent(uiTab, tabview)
|
||||
uiDarwinControlDefaultToplevel(uiTab, tabview)
|
||||
uiDarwinControlDefaultVisible(uiTab, tabview)
|
||||
uiDarwinControlDefaultShow(uiTab, tabview)
|
||||
uiDarwinControlDefaultHide(uiTab, tabview)
|
||||
uiDarwinControlDefaultEnabled(uiTab, tabview)
|
||||
uiDarwinControlDefaultEnable(uiTab, tabview)
|
||||
uiDarwinControlDefaultDisable(uiTab, tabview)
|
||||
|
||||
static void uiTabSyncEnableState(uiDarwinControl *c, int enabled)
|
||||
{
|
||||
uiTab *t = uiTab(c);
|
||||
tabPage *page;
|
||||
|
||||
if (uiDarwinShouldStopSyncEnableState(uiDarwinControl(t), enabled))
|
||||
return;
|
||||
for (page in t->pages)
|
||||
uiDarwinControlSyncEnableState(uiDarwinControl(page.c), enabled);
|
||||
}
|
||||
|
||||
uiDarwinControlDefaultSetSuperview(uiTab, tabview)
|
||||
|
||||
static void tabRelayout(uiTab *t)
|
||||
{
|
||||
tabPage *page;
|
||||
|
||||
for (page in t->pages)
|
||||
[page establishChildConstraints];
|
||||
// and this gets rid of some weird issues with regards to box alignment
|
||||
jiggleViewLayout(t->tabview);
|
||||
}
|
||||
|
||||
BOOL uiTabHugsTrailingEdge(uiDarwinControl *c)
|
||||
{
|
||||
uiTab *t = uiTab(c);
|
||||
|
||||
return t->horzHuggingPri < NSLayoutPriorityWindowSizeStayPut;
|
||||
}
|
||||
|
||||
BOOL uiTabHugsBottom(uiDarwinControl *c)
|
||||
{
|
||||
uiTab *t = uiTab(c);
|
||||
|
||||
return t->vertHuggingPri < NSLayoutPriorityWindowSizeStayPut;
|
||||
}
|
||||
|
||||
static void uiTabChildEdgeHuggingChanged(uiDarwinControl *c)
|
||||
{
|
||||
uiTab *t = uiTab(c);
|
||||
|
||||
tabRelayout(t);
|
||||
}
|
||||
|
||||
static NSLayoutPriority uiTabHuggingPriority(uiDarwinControl *c, NSLayoutConstraintOrientation orientation)
|
||||
{
|
||||
uiTab *t = uiTab(c);
|
||||
|
||||
if (orientation == NSLayoutConstraintOrientationHorizontal)
|
||||
return t->horzHuggingPri;
|
||||
return t->vertHuggingPri;
|
||||
}
|
||||
|
||||
static void uiTabSetHuggingPriority(uiDarwinControl *c, NSLayoutPriority priority, NSLayoutConstraintOrientation orientation)
|
||||
{
|
||||
uiTab *t = uiTab(c);
|
||||
|
||||
if (orientation == NSLayoutConstraintOrientationHorizontal)
|
||||
t->horzHuggingPri = priority;
|
||||
else
|
||||
t->vertHuggingPri = priority;
|
||||
uiDarwinNotifyEdgeHuggingChanged(uiDarwinControl(t));
|
||||
}
|
||||
|
||||
static void uiTabChildVisibilityChanged(uiDarwinControl *c)
|
||||
{
|
||||
uiTab *t = uiTab(c);
|
||||
|
||||
tabRelayout(t);
|
||||
}
|
||||
|
||||
void uiTabAppend(uiTab *t, const char *name, uiControl *child)
|
||||
{
|
||||
uiTabInsertAt(t, name, [t->pages count], child);
|
||||
}
|
||||
|
||||
void uiTabInsertAt(uiTab *t, const char *name, int n, uiControl *child)
|
||||
{
|
||||
tabPage *page;
|
||||
NSView *view;
|
||||
NSTabViewItem *i;
|
||||
NSObject *pageID;
|
||||
|
||||
uiControlSetParent(child, uiControl(t));
|
||||
|
||||
view = [[[NSView alloc] initWithFrame:NSZeroRect] autorelease];
|
||||
// note: if we turn off the autoresizing mask, nothing shows up
|
||||
uiDarwinControlSetSuperview(uiDarwinControl(child), view);
|
||||
uiDarwinControlSyncEnableState(uiDarwinControl(child), uiControlEnabledToUser(uiControl(t)));
|
||||
|
||||
// the documentation says these can be nil but the headers say these must not be; let's be safe and make them non-nil anyway
|
||||
pageID = [NSObject new];
|
||||
page = [[[tabPage alloc] initWithView:view pageID:pageID] autorelease];
|
||||
page.c = child;
|
||||
|
||||
// don't hug, just in case we're a stretchy tab
|
||||
page.oldHorzHuggingPri = uiDarwinControlHuggingPriority(uiDarwinControl(page.c), NSLayoutConstraintOrientationHorizontal);
|
||||
page.oldVertHuggingPri = uiDarwinControlHuggingPriority(uiDarwinControl(page.c), NSLayoutConstraintOrientationVertical);
|
||||
uiDarwinControlSetHuggingPriority(uiDarwinControl(page.c), NSLayoutPriorityDefaultLow, NSLayoutConstraintOrientationHorizontal);
|
||||
uiDarwinControlSetHuggingPriority(uiDarwinControl(page.c), NSLayoutPriorityDefaultLow, NSLayoutConstraintOrientationVertical);
|
||||
|
||||
[t->pages insertObject:page atIndex:n];
|
||||
|
||||
i = [[[NSTabViewItem alloc] initWithIdentifier:pageID] autorelease];
|
||||
[i setLabel:toNSString(name)];
|
||||
[i setView:view];
|
||||
[t->tabview insertTabViewItem:i atIndex:n];
|
||||
|
||||
tabRelayout(t);
|
||||
}
|
||||
|
||||
void uiTabDelete(uiTab *t, int n)
|
||||
{
|
||||
tabPage *page;
|
||||
uiControl *child;
|
||||
NSTabViewItem *i;
|
||||
|
||||
page = (tabPage *) [t->pages objectAtIndex:n];
|
||||
|
||||
uiDarwinControlSetHuggingPriority(uiDarwinControl(page.c), page.oldHorzHuggingPri, NSLayoutConstraintOrientationHorizontal);
|
||||
uiDarwinControlSetHuggingPriority(uiDarwinControl(page.c), page.oldVertHuggingPri, NSLayoutConstraintOrientationVertical);
|
||||
|
||||
child = page.c;
|
||||
[page removeChildConstraints];
|
||||
[t->pages removeObjectAtIndex:n];
|
||||
|
||||
uiControlSetParent(child, NULL);
|
||||
uiDarwinControlSetSuperview(uiDarwinControl(child), nil);
|
||||
|
||||
i = [t->tabview tabViewItemAtIndex:n];
|
||||
[t->tabview removeTabViewItem:i];
|
||||
|
||||
tabRelayout(t);
|
||||
}
|
||||
|
||||
int uiTabNumPages(uiTab *t)
|
||||
{
|
||||
return [t->pages count];
|
||||
}
|
||||
|
||||
int uiTabMargined(uiTab *t, int n)
|
||||
{
|
||||
tabPage *page;
|
||||
|
||||
page = (tabPage *) [t->pages objectAtIndex:n];
|
||||
return [page isMargined];
|
||||
}
|
||||
|
||||
void uiTabSetMargined(uiTab *t, int n, int margined)
|
||||
{
|
||||
tabPage *page;
|
||||
|
||||
page = (tabPage *) [t->pages objectAtIndex:n];
|
||||
[page setMargined:margined];
|
||||
}
|
||||
|
||||
uiTab *uiNewTab(void)
|
||||
{
|
||||
uiTab *t;
|
||||
|
||||
uiDarwinNewControl(uiTab, t);
|
||||
|
||||
t->tabview = [[NSTabView alloc] initWithFrame:NSZeroRect];
|
||||
// also good for NSTabView (same selector and everything)
|
||||
uiDarwinSetControlFont((NSControl *) (t->tabview), NSRegularControlSize);
|
||||
|
||||
t->pages = [NSMutableArray new];
|
||||
|
||||
// default to low hugging to not hug edges
|
||||
t->horzHuggingPri = NSLayoutPriorityDefaultLow;
|
||||
t->vertHuggingPri = NSLayoutPriorityDefaultLow;
|
||||
|
||||
return t;
|
||||
}
|
||||
Vendored
+19
@@ -0,0 +1,19 @@
|
||||
// 10 april 2015
|
||||
#import "uipriv_darwin.h"
|
||||
|
||||
char *uiDarwinNSStringToText(NSString *s)
|
||||
{
|
||||
char *out;
|
||||
|
||||
out = strdup([s UTF8String]);
|
||||
if (out == NULL) {
|
||||
fprintf(stderr, "memory exhausted in uiDarwinNSStringToText()\n");
|
||||
abort();
|
||||
}
|
||||
return out;
|
||||
}
|
||||
|
||||
void uiFreeText(char *s)
|
||||
{
|
||||
free(s);
|
||||
}
|
||||
Vendored
+146
@@ -0,0 +1,146 @@
|
||||
// 6 january 2015
|
||||
#define MAC_OS_X_VERSION_MIN_REQUIRED MAC_OS_X_VERSION_10_8
|
||||
#define MAC_OS_X_VERSION_MAX_ALLOWED MAC_OS_X_VERSION_10_8
|
||||
#import <Cocoa/Cocoa.h>
|
||||
#import "../ui.h"
|
||||
#import "../ui_darwin.h"
|
||||
#import "../common/uipriv.h"
|
||||
|
||||
#if __has_feature(objc_arc)
|
||||
#error Sorry, libui cannot be compiled with ARC.
|
||||
#endif
|
||||
|
||||
#define toNSString(str) [NSString stringWithUTF8String:(str)]
|
||||
#define fromNSString(str) [(str) UTF8String]
|
||||
|
||||
#ifndef NSAppKitVersionNumber10_9
|
||||
#define NSAppKitVersionNumber10_9 1265
|
||||
#endif
|
||||
|
||||
/*TODO remove this*/typedef struct uiImage uiImage;
|
||||
|
||||
// menu.m
|
||||
@interface menuManager : NSObject {
|
||||
struct mapTable *items;
|
||||
BOOL hasQuit;
|
||||
BOOL hasPreferences;
|
||||
BOOL hasAbout;
|
||||
}
|
||||
@property (strong) NSMenuItem *quitItem;
|
||||
@property (strong) NSMenuItem *preferencesItem;
|
||||
@property (strong) NSMenuItem *aboutItem;
|
||||
// NSMenuValidation is only informal
|
||||
- (BOOL)validateMenuItem:(NSMenuItem *)item;
|
||||
- (NSMenu *)makeMenubar;
|
||||
@end
|
||||
extern void finalizeMenus(void);
|
||||
extern void uninitMenus(void);
|
||||
|
||||
// main.m
|
||||
@interface applicationClass : NSApplication
|
||||
@end
|
||||
// this is needed because NSApp is of type id, confusing clang
|
||||
#define realNSApp() ((applicationClass *) NSApp)
|
||||
@interface appDelegate : NSObject <NSApplicationDelegate>
|
||||
@property (strong) menuManager *menuManager;
|
||||
@end
|
||||
#define appDelegate() ((appDelegate *) [realNSApp() delegate])
|
||||
struct nextEventArgs {
|
||||
NSEventMask mask;
|
||||
NSDate *duration;
|
||||
// LONGTERM no NSRunLoopMode?
|
||||
NSString *mode;
|
||||
BOOL dequeue;
|
||||
};
|
||||
extern int mainStep(struct nextEventArgs *nea, BOOL (^interceptEvent)(NSEvent *));
|
||||
|
||||
// util.m
|
||||
extern void disableAutocorrect(NSTextView *);
|
||||
|
||||
// entry.m
|
||||
extern void finishNewTextField(NSTextField *, BOOL);
|
||||
extern NSTextField *newEditableTextField(void);
|
||||
|
||||
// window.m
|
||||
@interface libuiNSWindow : NSWindow
|
||||
- (void)libui_doMove:(NSEvent *)initialEvent;
|
||||
- (void)libui_doResize:(NSEvent *)initialEvent on:(uiWindowResizeEdge)edge;
|
||||
@end
|
||||
extern uiWindow *windowFromNSWindow(NSWindow *);
|
||||
|
||||
// alloc.m
|
||||
extern NSMutableArray *delegates;
|
||||
extern void initAlloc(void);
|
||||
extern void uninitAlloc(void);
|
||||
|
||||
// autolayout.m
|
||||
extern NSLayoutConstraint *mkConstraint(id view1, NSLayoutAttribute attr1, NSLayoutRelation relation, id view2, NSLayoutAttribute attr2, CGFloat multiplier, CGFloat c, NSString *desc);
|
||||
extern void jiggleViewLayout(NSView *view);
|
||||
struct singleChildConstraints {
|
||||
NSLayoutConstraint *leadingConstraint;
|
||||
NSLayoutConstraint *topConstraint;
|
||||
NSLayoutConstraint *trailingConstraintGreater;
|
||||
NSLayoutConstraint *trailingConstraintEqual;
|
||||
NSLayoutConstraint *bottomConstraintGreater;
|
||||
NSLayoutConstraint *bottomConstraintEqual;
|
||||
};
|
||||
extern void singleChildConstraintsEstablish(struct singleChildConstraints *c, NSView *contentView, NSView *childView, BOOL hugsTrailing, BOOL hugsBottom, int margined, NSString *desc);
|
||||
extern void singleChildConstraintsRemove(struct singleChildConstraints *c, NSView *cv);
|
||||
extern void singleChildConstraintsSetMargined(struct singleChildConstraints *c, int margined);
|
||||
|
||||
// map.m
|
||||
extern struct mapTable *newMap(void);
|
||||
extern void mapDestroy(struct mapTable *m);
|
||||
extern void *mapGet(struct mapTable *m, void *key);
|
||||
extern void mapSet(struct mapTable *m, void *key, void *value);
|
||||
extern void mapDelete(struct mapTable *m, void *key);
|
||||
extern void mapWalk(struct mapTable *m, void (*f)(void *key, void *value));
|
||||
extern void mapReset(struct mapTable *m);
|
||||
|
||||
// area.m
|
||||
extern int sendAreaEvents(NSEvent *);
|
||||
|
||||
// areaevents.m
|
||||
extern BOOL fromKeycode(unsigned short keycode, uiAreaKeyEvent *ke);
|
||||
extern BOOL keycodeModifier(unsigned short keycode, uiModifiers *mod);
|
||||
|
||||
// draw.m
|
||||
extern uiDrawContext *newContext(CGContextRef, CGFloat);
|
||||
extern void freeContext(uiDrawContext *);
|
||||
|
||||
// drawtext.m
|
||||
extern uiDrawTextFont *mkTextFont(CTFontRef f, BOOL retain);
|
||||
extern uiDrawTextFont *mkTextFontFromNSFont(NSFont *f);
|
||||
extern void doDrawText(CGContextRef c, CGFloat cheight, double x, double y, uiDrawTextLayout *layout);
|
||||
|
||||
// fontbutton.m
|
||||
extern BOOL fontButtonInhibitSendAction(SEL sel, id from, id to);
|
||||
extern BOOL fontButtonOverrideTargetForAction(SEL sel, id from, id to, id *override);
|
||||
extern void setupFontPanel(void);
|
||||
|
||||
// colorbutton.m
|
||||
extern BOOL colorButtonInhibitSendAction(SEL sel, id from, id to);
|
||||
|
||||
// scrollview.m
|
||||
struct scrollViewCreateParams {
|
||||
NSView *DocumentView;
|
||||
NSColor *BackgroundColor;
|
||||
BOOL DrawsBackground;
|
||||
BOOL Bordered;
|
||||
BOOL HScroll;
|
||||
BOOL VScroll;
|
||||
};
|
||||
struct scrollViewData;
|
||||
extern NSScrollView *mkScrollView(struct scrollViewCreateParams *p, struct scrollViewData **dout);
|
||||
extern void scrollViewSetScrolling(NSScrollView *sv, struct scrollViewData *d, BOOL hscroll, BOOL vscroll);
|
||||
extern void scrollViewFreeData(NSScrollView *sv, struct scrollViewData *d);
|
||||
|
||||
// label.m
|
||||
extern NSTextField *newLabel(NSString *str);
|
||||
|
||||
// image.m
|
||||
extern NSImage *imageImage(uiImage *);
|
||||
|
||||
// winmoveresize.m
|
||||
extern void doManualMove(NSWindow *w, NSEvent *initialEvent);
|
||||
extern void doManualResize(NSWindow *w, NSEvent *initialEvent, uiWindowResizeEdge edge);
|
||||
Vendored
+15
@@ -0,0 +1,15 @@
|
||||
// 7 april 2015
|
||||
#import "uipriv_darwin.h"
|
||||
|
||||
// LONGTERM do we really want to do this? make it an option?
|
||||
void disableAutocorrect(NSTextView *tv)
|
||||
{
|
||||
[tv setEnabledTextCheckingTypes:0];
|
||||
[tv setAutomaticDashSubstitutionEnabled:NO];
|
||||
// don't worry about automatic data detection; it won't change stringValue (thanks pretty_function in irc.freenode.net/#macdev)
|
||||
[tv setAutomaticSpellingCorrectionEnabled:NO];
|
||||
[tv setAutomaticTextReplacementEnabled:NO];
|
||||
[tv setAutomaticQuoteSubstitutionEnabled:NO];
|
||||
[tv setAutomaticLinkDetectionEnabled:NO];
|
||||
[tv setSmartInsertDeleteEnabled:NO];
|
||||
}
|
||||
Vendored
+407
@@ -0,0 +1,407 @@
|
||||
// 15 august 2015
|
||||
#import "uipriv_darwin.h"
|
||||
|
||||
#define defaultStyleMask (NSTitledWindowMask | NSClosableWindowMask | NSMiniaturizableWindowMask | NSResizableWindowMask)
|
||||
|
||||
struct uiWindow {
|
||||
uiDarwinControl c;
|
||||
NSWindow *window;
|
||||
uiControl *child;
|
||||
int margined;
|
||||
int (*onClosing)(uiWindow *, void *);
|
||||
void *onClosingData;
|
||||
struct singleChildConstraints constraints;
|
||||
void (*onContentSizeChanged)(uiWindow *, void *);
|
||||
void *onContentSizeChangedData;
|
||||
BOOL suppressSizeChanged;
|
||||
int fullscreen;
|
||||
int borderless;
|
||||
};
|
||||
|
||||
@implementation libuiNSWindow
|
||||
|
||||
- (void)libui_doMove:(NSEvent *)initialEvent
|
||||
{
|
||||
doManualMove(self, initialEvent);
|
||||
}
|
||||
|
||||
- (void)libui_doResize:(NSEvent *)initialEvent on:(uiWindowResizeEdge)edge
|
||||
{
|
||||
doManualResize(self, initialEvent, edge);
|
||||
}
|
||||
|
||||
@end
|
||||
|
||||
@interface windowDelegateClass : NSObject<NSWindowDelegate> {
|
||||
struct mapTable *windows;
|
||||
}
|
||||
- (BOOL)windowShouldClose:(id)sender;
|
||||
- (void)windowDidResize:(NSNotification *)note;
|
||||
- (void)windowDidEnterFullScreen:(NSNotification *)note;
|
||||
- (void)windowDidExitFullScreen:(NSNotification *)note;
|
||||
- (void)registerWindow:(uiWindow *)w;
|
||||
- (void)unregisterWindow:(uiWindow *)w;
|
||||
- (uiWindow *)lookupWindow:(NSWindow *)w;
|
||||
@end
|
||||
|
||||
@implementation windowDelegateClass
|
||||
|
||||
- (id)init
|
||||
{
|
||||
self = [super init];
|
||||
if (self)
|
||||
self->windows = newMap();
|
||||
return self;
|
||||
}
|
||||
|
||||
- (void)dealloc
|
||||
{
|
||||
mapDestroy(self->windows);
|
||||
[super dealloc];
|
||||
}
|
||||
|
||||
- (BOOL)windowShouldClose:(id)sender
|
||||
{
|
||||
uiWindow *w;
|
||||
|
||||
w = [self lookupWindow:((NSWindow *) sender)];
|
||||
// w should not be NULL; we are only the delegate of registered windows
|
||||
if ((*(w->onClosing))(w, w->onClosingData))
|
||||
uiControlDestroy(uiControl(w));
|
||||
return NO;
|
||||
}
|
||||
|
||||
- (void)windowDidResize:(NSNotification *)note
|
||||
{
|
||||
uiWindow *w;
|
||||
|
||||
w = [self lookupWindow:((NSWindow *) [note object])];
|
||||
if (!w->suppressSizeChanged)
|
||||
(*(w->onContentSizeChanged))(w, w->onContentSizeChangedData);
|
||||
}
|
||||
|
||||
- (void)windowDidEnterFullScreen:(NSNotification *)note
|
||||
{
|
||||
uiWindow *w;
|
||||
|
||||
w = [self lookupWindow:((NSWindow *) [note object])];
|
||||
if (!w->suppressSizeChanged)
|
||||
w->fullscreen = 1;
|
||||
}
|
||||
|
||||
- (void)windowDidExitFullScreen:(NSNotification *)note
|
||||
{
|
||||
uiWindow *w;
|
||||
|
||||
w = [self lookupWindow:((NSWindow *) [note object])];
|
||||
if (!w->suppressSizeChanged)
|
||||
w->fullscreen = 0;
|
||||
}
|
||||
|
||||
- (void)registerWindow:(uiWindow *)w
|
||||
{
|
||||
mapSet(self->windows, w->window, w);
|
||||
[w->window setDelegate:self];
|
||||
}
|
||||
|
||||
- (void)unregisterWindow:(uiWindow *)w
|
||||
{
|
||||
[w->window setDelegate:nil];
|
||||
mapDelete(self->windows, w->window);
|
||||
}
|
||||
|
||||
- (uiWindow *)lookupWindow:(NSWindow *)w
|
||||
{
|
||||
uiWindow *v;
|
||||
|
||||
v = uiWindow(mapGet(self->windows, w));
|
||||
// this CAN (and IS ALLOWED TO) return NULL, just in case we're called with some OS X-provided window as the key window
|
||||
return v;
|
||||
}
|
||||
|
||||
@end
|
||||
|
||||
static windowDelegateClass *windowDelegate = nil;
|
||||
|
||||
static void removeConstraints(uiWindow *w)
|
||||
{
|
||||
NSView *cv;
|
||||
|
||||
cv = [w->window contentView];
|
||||
singleChildConstraintsRemove(&(w->constraints), cv);
|
||||
}
|
||||
|
||||
static void uiWindowDestroy(uiControl *c)
|
||||
{
|
||||
uiWindow *w = uiWindow(c);
|
||||
|
||||
// hide the window
|
||||
[w->window orderOut:w->window];
|
||||
removeConstraints(w);
|
||||
if (w->child != NULL) {
|
||||
uiControlSetParent(w->child, NULL);
|
||||
uiDarwinControlSetSuperview(uiDarwinControl(w->child), nil);
|
||||
uiControlDestroy(w->child);
|
||||
}
|
||||
[windowDelegate unregisterWindow:w];
|
||||
[w->window release];
|
||||
uiFreeControl(uiControl(w));
|
||||
}
|
||||
|
||||
uiDarwinControlDefaultHandle(uiWindow, window)
|
||||
|
||||
uiControl *uiWindowParent(uiControl *c)
|
||||
{
|
||||
return NULL;
|
||||
}
|
||||
|
||||
void uiWindowSetParent(uiControl *c, uiControl *parent)
|
||||
{
|
||||
uiUserBugCannotSetParentOnToplevel("uiWindow");
|
||||
}
|
||||
|
||||
static int uiWindowToplevel(uiControl *c)
|
||||
{
|
||||
return 1;
|
||||
}
|
||||
|
||||
static int uiWindowVisible(uiControl *c)
|
||||
{
|
||||
uiWindow *w = uiWindow(c);
|
||||
|
||||
return [w->window isVisible];
|
||||
}
|
||||
|
||||
static void uiWindowShow(uiControl *c)
|
||||
{
|
||||
uiWindow *w = (uiWindow *) c;
|
||||
|
||||
[w->window makeKeyAndOrderFront:w->window];
|
||||
}
|
||||
|
||||
static void uiWindowHide(uiControl *c)
|
||||
{
|
||||
uiWindow *w = (uiWindow *) c;
|
||||
|
||||
[w->window orderOut:w->window];
|
||||
}
|
||||
|
||||
uiDarwinControlDefaultEnabled(uiWindow, window)
|
||||
uiDarwinControlDefaultEnable(uiWindow, window)
|
||||
uiDarwinControlDefaultDisable(uiWindow, window)
|
||||
|
||||
static void uiWindowSyncEnableState(uiDarwinControl *c, int enabled)
|
||||
{
|
||||
uiWindow *w = uiWindow(c);
|
||||
|
||||
if (uiDarwinShouldStopSyncEnableState(uiDarwinControl(w), enabled))
|
||||
return;
|
||||
if (w->child != NULL)
|
||||
uiDarwinControlSyncEnableState(uiDarwinControl(w->child), enabled);
|
||||
}
|
||||
|
||||
static void uiWindowSetSuperview(uiDarwinControl *c, NSView *superview)
|
||||
{
|
||||
// TODO
|
||||
}
|
||||
|
||||
static void windowRelayout(uiWindow *w)
|
||||
{
|
||||
NSView *childView;
|
||||
NSView *contentView;
|
||||
|
||||
removeConstraints(w);
|
||||
if (w->child == NULL)
|
||||
return;
|
||||
childView = (NSView *) uiControlHandle(w->child);
|
||||
contentView = [w->window contentView];
|
||||
singleChildConstraintsEstablish(&(w->constraints),
|
||||
contentView, childView,
|
||||
uiDarwinControlHugsTrailingEdge(uiDarwinControl(w->child)),
|
||||
uiDarwinControlHugsBottom(uiDarwinControl(w->child)),
|
||||
w->margined,
|
||||
@"uiWindow");
|
||||
}
|
||||
|
||||
uiDarwinControlDefaultHugsTrailingEdge(uiWindow, window)
|
||||
uiDarwinControlDefaultHugsBottom(uiWindow, window)
|
||||
|
||||
static void uiWindowChildEdgeHuggingChanged(uiDarwinControl *c)
|
||||
{
|
||||
uiWindow *w = uiWindow(c);
|
||||
|
||||
windowRelayout(w);
|
||||
}
|
||||
|
||||
// TODO
|
||||
uiDarwinControlDefaultHuggingPriority(uiWindow, window)
|
||||
uiDarwinControlDefaultSetHuggingPriority(uiWindow, window)
|
||||
// end TODO
|
||||
|
||||
static void uiWindowChildVisibilityChanged(uiDarwinControl *c)
|
||||
{
|
||||
uiWindow *w = uiWindow(c);
|
||||
|
||||
windowRelayout(w);
|
||||
}
|
||||
|
||||
char *uiWindowTitle(uiWindow *w)
|
||||
{
|
||||
return uiDarwinNSStringToText([w->window title]);
|
||||
}
|
||||
|
||||
void uiWindowSetTitle(uiWindow *w, const char *title)
|
||||
{
|
||||
[w->window setTitle:toNSString(title)];
|
||||
}
|
||||
|
||||
void uiWindowContentSize(uiWindow *w, int *width, int *height)
|
||||
{
|
||||
NSRect r;
|
||||
|
||||
r = [w->window contentRectForFrameRect:[w->window frame]];
|
||||
*width = r.size.width;
|
||||
*height = r.size.height;
|
||||
}
|
||||
|
||||
void uiWindowSetContentSize(uiWindow *w, int width, int height)
|
||||
{
|
||||
w->suppressSizeChanged = YES;
|
||||
[w->window setContentSize:NSMakeSize(width, height)];
|
||||
w->suppressSizeChanged = NO;
|
||||
}
|
||||
|
||||
int uiWindowFullscreen(uiWindow *w)
|
||||
{
|
||||
return w->fullscreen;
|
||||
}
|
||||
|
||||
void uiWindowSetFullscreen(uiWindow *w, int fullscreen)
|
||||
{
|
||||
if (w->fullscreen && fullscreen)
|
||||
return;
|
||||
if (!w->fullscreen && !fullscreen)
|
||||
return;
|
||||
w->fullscreen = fullscreen;
|
||||
if (w->fullscreen && w->borderless) // borderless doesn't play nice with fullscreen; don't toggle while borderless
|
||||
return;
|
||||
w->suppressSizeChanged = YES;
|
||||
[w->window toggleFullScreen:w->window];
|
||||
w->suppressSizeChanged = NO;
|
||||
if (!w->fullscreen && w->borderless) // borderless doesn't play nice with fullscreen; restore borderless after removing
|
||||
[w->window setStyleMask:NSBorderlessWindowMask];
|
||||
}
|
||||
|
||||
void uiWindowOnContentSizeChanged(uiWindow *w, void (*f)(uiWindow *, void *), void *data)
|
||||
{
|
||||
w->onContentSizeChanged = f;
|
||||
w->onContentSizeChangedData = data;
|
||||
}
|
||||
|
||||
void uiWindowOnClosing(uiWindow *w, int (*f)(uiWindow *, void *), void *data)
|
||||
{
|
||||
w->onClosing = f;
|
||||
w->onClosingData = data;
|
||||
}
|
||||
|
||||
int uiWindowBorderless(uiWindow *w)
|
||||
{
|
||||
return w->borderless;
|
||||
}
|
||||
|
||||
void uiWindowSetBorderless(uiWindow *w, int borderless)
|
||||
{
|
||||
w->borderless = borderless;
|
||||
if (w->borderless) {
|
||||
// borderless doesn't play nice with fullscreen; wait for later
|
||||
if (!w->fullscreen)
|
||||
[w->window setStyleMask:NSBorderlessWindowMask];
|
||||
} else {
|
||||
[w->window setStyleMask:defaultStyleMask];
|
||||
// borderless doesn't play nice with fullscreen; restore state
|
||||
if (w->fullscreen) {
|
||||
w->suppressSizeChanged = YES;
|
||||
[w->window toggleFullScreen:w->window];
|
||||
w->suppressSizeChanged = NO;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void uiWindowSetChild(uiWindow *w, uiControl *child)
|
||||
{
|
||||
NSView *childView;
|
||||
|
||||
if (w->child != NULL) {
|
||||
childView = (NSView *) uiControlHandle(w->child);
|
||||
[childView removeFromSuperview];
|
||||
uiControlSetParent(w->child, NULL);
|
||||
}
|
||||
w->child = child;
|
||||
if (w->child != NULL) {
|
||||
uiControlSetParent(w->child, uiControl(w));
|
||||
childView = (NSView *) uiControlHandle(w->child);
|
||||
uiDarwinControlSetSuperview(uiDarwinControl(w->child), [w->window contentView]);
|
||||
uiDarwinControlSyncEnableState(uiDarwinControl(w->child), uiControlEnabledToUser(uiControl(w)));
|
||||
}
|
||||
windowRelayout(w);
|
||||
}
|
||||
|
||||
int uiWindowMargined(uiWindow *w)
|
||||
{
|
||||
return w->margined;
|
||||
}
|
||||
|
||||
void uiWindowSetMargined(uiWindow *w, int margined)
|
||||
{
|
||||
w->margined = margined;
|
||||
singleChildConstraintsSetMargined(&(w->constraints), w->margined);
|
||||
}
|
||||
|
||||
static int defaultOnClosing(uiWindow *w, void *data)
|
||||
{
|
||||
return 0;
|
||||
}
|
||||
|
||||
static void defaultOnPositionContentSizeChanged(uiWindow *w, void *data)
|
||||
{
|
||||
// do nothing
|
||||
}
|
||||
|
||||
uiWindow *uiNewWindow(const char *title, int width, int height, int hasMenubar)
|
||||
{
|
||||
uiWindow *w;
|
||||
|
||||
finalizeMenus();
|
||||
|
||||
uiDarwinNewControl(uiWindow, w);
|
||||
|
||||
w->window = [[libuiNSWindow alloc] initWithContentRect:NSMakeRect(0, 0, (CGFloat) width, (CGFloat) height)
|
||||
styleMask:defaultStyleMask
|
||||
backing:NSBackingStoreBuffered
|
||||
defer:YES];
|
||||
[w->window setTitle:toNSString(title)];
|
||||
|
||||
// do NOT release when closed
|
||||
// we manually do this in uiWindowDestroy() above
|
||||
[w->window setReleasedWhenClosed:NO];
|
||||
|
||||
if (windowDelegate == nil) {
|
||||
windowDelegate = [[windowDelegateClass new] autorelease];
|
||||
[delegates addObject:windowDelegate];
|
||||
}
|
||||
[windowDelegate registerWindow:w];
|
||||
uiWindowOnClosing(w, defaultOnClosing, NULL);
|
||||
uiWindowOnContentSizeChanged(w, defaultOnPositionContentSizeChanged, NULL);
|
||||
|
||||
return w;
|
||||
}
|
||||
|
||||
// utility function for menus
|
||||
uiWindow *windowFromNSWindow(NSWindow *w)
|
||||
{
|
||||
if (w == nil)
|
||||
return NULL;
|
||||
if (windowDelegate == nil) // no windows were created yet; we're called with some OS X-provided window
|
||||
return NULL;
|
||||
return [windowDelegate lookupWindow:w];
|
||||
}
|
||||
Vendored
+253
@@ -0,0 +1,253 @@
|
||||
// 1 november 2016
|
||||
#import "uipriv_darwin.h"
|
||||
|
||||
// because we are changing the window frame each time the mouse moves, the successive -[NSEvent locationInWindow]s cannot be meaningfully used together
|
||||
// make sure they are all following some sort of standard to avoid this problem; the screen is the most obvious possibility since it requires only one conversion (the only one that a NSWindow provides)
|
||||
static NSPoint makeIndependent(NSPoint p, NSWindow *w)
|
||||
{
|
||||
NSRect r;
|
||||
|
||||
r.origin = p;
|
||||
// mikeash in irc.freenode.net/#macdev confirms both that any size will do and that we can safely ignore the resultant size
|
||||
r.size = NSZeroSize;
|
||||
return [w convertRectToScreen:r].origin;
|
||||
}
|
||||
|
||||
struct onMoveDragParams {
|
||||
NSWindow *w;
|
||||
// using the previous point causes weird issues like the mouse seeming to fall behind the window edge... so do this instead
|
||||
// TODO will this make things like the menubar and dock easier too?
|
||||
NSRect initialFrame;
|
||||
NSPoint initialPoint;
|
||||
};
|
||||
|
||||
void onMoveDrag(struct onMoveDragParams *p, NSEvent *e)
|
||||
{
|
||||
NSPoint new;
|
||||
NSRect frame;
|
||||
CGFloat offx, offy;
|
||||
|
||||
new = makeIndependent([e locationInWindow], p->w);
|
||||
frame = p->initialFrame;
|
||||
|
||||
offx = new.x - p->initialPoint.x;
|
||||
offy = new.y - p->initialPoint.y;
|
||||
frame.origin.x += offx;
|
||||
frame.origin.y += offy;
|
||||
|
||||
// TODO handle the menubar
|
||||
// TODO wait the system does this for us already?!
|
||||
|
||||
[p->w setFrameOrigin:frame.origin];
|
||||
}
|
||||
|
||||
void doManualMove(NSWindow *w, NSEvent *initialEvent)
|
||||
{
|
||||
__block struct onMoveDragParams mdp;
|
||||
struct nextEventArgs nea;
|
||||
BOOL (^handleEvent)(NSEvent *e);
|
||||
__block BOOL done;
|
||||
|
||||
// this is only available on 10.11 and newer (LONGTERM FUTURE)
|
||||
// but use it if available; this lets us use the real OS dragging code, which means we can take advantage of OS features like Spaces
|
||||
if ([w respondsToSelector:@selector(performWindowDragWithEvent:)]) {
|
||||
[((id) w) performWindowDragWithEvent:initialEvent];
|
||||
return;
|
||||
}
|
||||
|
||||
mdp.w = w;
|
||||
mdp.initialFrame = [mdp.w frame];
|
||||
mdp.initialPoint = makeIndependent([initialEvent locationInWindow], mdp.w);
|
||||
|
||||
nea.mask = NSLeftMouseDraggedMask | NSLeftMouseUpMask;
|
||||
nea.duration = [NSDate distantFuture];
|
||||
nea.mode = NSEventTrackingRunLoopMode; // nextEventMatchingMask: docs suggest using this for manual mouse tracking
|
||||
nea.dequeue = YES;
|
||||
handleEvent = ^(NSEvent *e) {
|
||||
if ([e type] == NSLeftMouseUp) {
|
||||
done = YES;
|
||||
return YES; // do not send
|
||||
}
|
||||
onMoveDrag(&mdp, e);
|
||||
return YES; // do not send
|
||||
};
|
||||
done = NO;
|
||||
while (mainStep(&nea, handleEvent))
|
||||
if (done)
|
||||
break;
|
||||
}
|
||||
|
||||
// see http://stackoverflow.com/a/40352996/3408572
|
||||
static void minMaxAutoLayoutSizes(NSWindow *w, NSSize *min, NSSize *max)
|
||||
{
|
||||
NSLayoutConstraint *cw, *ch;
|
||||
NSView *contentView;
|
||||
NSRect prevFrame;
|
||||
|
||||
// if adding these constraints causes the window to change size somehow, don't show it to the user and change it back afterwards
|
||||
NSDisableScreenUpdates();
|
||||
prevFrame = [w frame];
|
||||
|
||||
// minimum: encourage the window to be as small as possible
|
||||
contentView = [w contentView];
|
||||
cw = mkConstraint(contentView, NSLayoutAttributeWidth,
|
||||
NSLayoutRelationEqual,
|
||||
nil, NSLayoutAttributeNotAnAttribute,
|
||||
0, 0,
|
||||
@"window minimum width finding constraint");
|
||||
[cw setPriority:NSLayoutPriorityDragThatCanResizeWindow];
|
||||
[contentView addConstraint:cw];
|
||||
ch = mkConstraint(contentView, NSLayoutAttributeHeight,
|
||||
NSLayoutRelationEqual,
|
||||
nil, NSLayoutAttributeNotAnAttribute,
|
||||
0, 0,
|
||||
@"window minimum height finding constraint");
|
||||
[ch setPriority:NSLayoutPriorityDragThatCanResizeWindow];
|
||||
[contentView addConstraint:ch];
|
||||
*min = [contentView fittingSize];
|
||||
[contentView removeConstraint:cw];
|
||||
[contentView removeConstraint:ch];
|
||||
|
||||
// maximum: encourage the window to be as large as possible
|
||||
contentView = [w contentView];
|
||||
cw = mkConstraint(contentView, NSLayoutAttributeWidth,
|
||||
NSLayoutRelationEqual,
|
||||
nil, NSLayoutAttributeNotAnAttribute,
|
||||
0, CGFLOAT_MAX,
|
||||
@"window maximum width finding constraint");
|
||||
[cw setPriority:NSLayoutPriorityDragThatCanResizeWindow];
|
||||
[contentView addConstraint:cw];
|
||||
ch = mkConstraint(contentView, NSLayoutAttributeHeight,
|
||||
NSLayoutRelationEqual,
|
||||
nil, NSLayoutAttributeNotAnAttribute,
|
||||
0, CGFLOAT_MAX,
|
||||
@"window maximum height finding constraint");
|
||||
[ch setPriority:NSLayoutPriorityDragThatCanResizeWindow];
|
||||
[contentView addConstraint:ch];
|
||||
*max = [contentView fittingSize];
|
||||
[contentView removeConstraint:cw];
|
||||
[contentView removeConstraint:ch];
|
||||
|
||||
[w setFrame:prevFrame display:YES]; // TODO really YES?
|
||||
NSEnableScreenUpdates();
|
||||
}
|
||||
|
||||
static void handleResizeLeft(NSRect *frame, NSPoint old, NSPoint new)
|
||||
{
|
||||
frame->origin.x += new.x - old.x;
|
||||
frame->size.width -= new.x - old.x;
|
||||
}
|
||||
|
||||
// TODO properly handle the menubar
|
||||
// TODO wait, OS X does it for us?!
|
||||
static void handleResizeTop(NSRect *frame, NSPoint old, NSPoint new)
|
||||
{
|
||||
frame->size.height += new.y - old.y;
|
||||
}
|
||||
|
||||
static void handleResizeRight(NSRect *frame, NSPoint old, NSPoint new)
|
||||
{
|
||||
frame->size.width += new.x - old.x;
|
||||
}
|
||||
|
||||
|
||||
// TODO properly handle the menubar
|
||||
static void handleResizeBottom(NSRect *frame, NSPoint old, NSPoint new)
|
||||
{
|
||||
frame->origin.y += new.y - old.y;
|
||||
frame->size.height -= new.y - old.y;
|
||||
}
|
||||
|
||||
struct onResizeDragParams {
|
||||
NSWindow *w;
|
||||
// using the previous point causes weird issues like the mouse seeming to fall behind the window edge... so do this instead
|
||||
// TODO will this make things like the menubar and dock easier too?
|
||||
NSRect initialFrame;
|
||||
NSPoint initialPoint;
|
||||
uiWindowResizeEdge edge;
|
||||
NSSize min;
|
||||
NSSize max;
|
||||
};
|
||||
|
||||
static void onResizeDrag(struct onResizeDragParams *p, NSEvent *e)
|
||||
{
|
||||
NSPoint new;
|
||||
NSRect frame;
|
||||
|
||||
new = makeIndependent([e locationInWindow], p->w);
|
||||
frame = p->initialFrame;
|
||||
|
||||
// horizontal
|
||||
switch (p->edge) {
|
||||
case uiWindowResizeEdgeLeft:
|
||||
case uiWindowResizeEdgeTopLeft:
|
||||
case uiWindowResizeEdgeBottomLeft:
|
||||
handleResizeLeft(&frame, p->initialPoint, new);
|
||||
break;
|
||||
case uiWindowResizeEdgeRight:
|
||||
case uiWindowResizeEdgeTopRight:
|
||||
case uiWindowResizeEdgeBottomRight:
|
||||
handleResizeRight(&frame, p->initialPoint, new);
|
||||
break;
|
||||
}
|
||||
// vertical
|
||||
switch (p->edge) {
|
||||
case uiWindowResizeEdgeTop:
|
||||
case uiWindowResizeEdgeTopLeft:
|
||||
case uiWindowResizeEdgeTopRight:
|
||||
handleResizeTop(&frame, p->initialPoint, new);
|
||||
break;
|
||||
case uiWindowResizeEdgeBottom:
|
||||
case uiWindowResizeEdgeBottomLeft:
|
||||
case uiWindowResizeEdgeBottomRight:
|
||||
handleResizeBottom(&frame, p->initialPoint, new);
|
||||
break;
|
||||
}
|
||||
|
||||
// constrain
|
||||
// TODO should we constrain against anything else as well? minMaxAutoLayoutSizes() already gives us nonnegative sizes, but...
|
||||
if (frame.size.width < p->min.width)
|
||||
frame.size.width = p->min.width;
|
||||
if (frame.size.height < p->min.height)
|
||||
frame.size.height = p->min.height;
|
||||
// TODO > or >= ?
|
||||
if (frame.size.width > p->max.width)
|
||||
frame.size.width = p->max.width;
|
||||
if (frame.size.height > p->max.height)
|
||||
frame.size.height = p->max.height;
|
||||
|
||||
[p->w setFrame:frame display:YES]; // and do reflect the new frame immediately
|
||||
}
|
||||
|
||||
// TODO do our events get fired with this? *should* they?
|
||||
void doManualResize(NSWindow *w, NSEvent *initialEvent, uiWindowResizeEdge edge)
|
||||
{
|
||||
__block struct onResizeDragParams rdp;
|
||||
struct nextEventArgs nea;
|
||||
BOOL (^handleEvent)(NSEvent *e);
|
||||
__block BOOL done;
|
||||
|
||||
rdp.w = w;
|
||||
rdp.initialFrame = [rdp.w frame];
|
||||
rdp.initialPoint = makeIndependent([initialEvent locationInWindow], rdp.w);
|
||||
rdp.edge = edge;
|
||||
// TODO what happens if these change during the loop?
|
||||
minMaxAutoLayoutSizes(rdp.w, &(rdp.min), &(rdp.max));
|
||||
|
||||
nea.mask = NSLeftMouseDraggedMask | NSLeftMouseUpMask;
|
||||
nea.duration = [NSDate distantFuture];
|
||||
nea.mode = NSEventTrackingRunLoopMode; // nextEventMatchingMask: docs suggest using this for manual mouse tracking
|
||||
nea.dequeue = YES;
|
||||
handleEvent = ^(NSEvent *e) {
|
||||
if ([e type] == NSLeftMouseUp) {
|
||||
done = YES;
|
||||
return YES; // do not send
|
||||
}
|
||||
onResizeDrag(&rdp, e);
|
||||
return YES; // do not send
|
||||
};
|
||||
done = NO;
|
||||
while (mainStep(&nea, handleEvent))
|
||||
if (done)
|
||||
break;
|
||||
}
|
||||
Vendored
+543
@@ -0,0 +1,543 @@
|
||||
/* 2 september 2015 */
|
||||
#include <stdio.h>
|
||||
#include <string.h>
|
||||
#include "ui.h"
|
||||
#include "../../verbosity.h"
|
||||
|
||||
static int onClosing(uiWindow *w, void *data)
|
||||
{
|
||||
uiQuit();
|
||||
return 1;
|
||||
}
|
||||
|
||||
static int onShouldQuit(void *data)
|
||||
{
|
||||
uiWindow *mainwin = uiWindow(data);
|
||||
|
||||
uiControlDestroy(uiControl(mainwin));
|
||||
return 1;
|
||||
}
|
||||
|
||||
static uiControl *makeBasicControlsPage(void)
|
||||
{
|
||||
uiBox *vbox;
|
||||
uiBox *hbox;
|
||||
uiGroup *group;
|
||||
uiForm *entryForm;
|
||||
|
||||
vbox = uiNewVerticalBox();
|
||||
uiBoxSetPadded(vbox, 1);
|
||||
|
||||
hbox = uiNewHorizontalBox();
|
||||
uiBoxSetPadded(hbox, 1);
|
||||
uiBoxAppend(vbox, uiControl(hbox), 0);
|
||||
|
||||
uiBoxAppend(hbox,
|
||||
uiControl(uiNewButton("Button")),
|
||||
0);
|
||||
uiBoxAppend(hbox,
|
||||
uiControl(uiNewCheckbox("Checkbox")),
|
||||
0);
|
||||
|
||||
uiBoxAppend(vbox,
|
||||
uiControl(uiNewLabel("This is a label. Right now, labels can only span one line.")),
|
||||
0);
|
||||
|
||||
uiBoxAppend(vbox,
|
||||
uiControl(uiNewHorizontalSeparator()),
|
||||
0);
|
||||
|
||||
group = uiNewGroup("Entries");
|
||||
uiGroupSetMargined(group, 1);
|
||||
uiBoxAppend(vbox, uiControl(group), 1);
|
||||
|
||||
entryForm = uiNewForm();
|
||||
uiFormSetPadded(entryForm, 1);
|
||||
uiGroupSetChild(group, uiControl(entryForm));
|
||||
|
||||
uiFormAppend(entryForm,
|
||||
"Entry",
|
||||
uiControl(uiNewEntry()),
|
||||
0);
|
||||
uiFormAppend(entryForm,
|
||||
"Password Entry",
|
||||
uiControl(uiNewPasswordEntry()),
|
||||
0);
|
||||
uiFormAppend(entryForm,
|
||||
"Search Entry",
|
||||
uiControl(uiNewSearchEntry()),
|
||||
0);
|
||||
uiFormAppend(entryForm,
|
||||
"Multiline Entry",
|
||||
uiControl(uiNewMultilineEntry()),
|
||||
1);
|
||||
uiFormAppend(entryForm,
|
||||
"Multiline Entry No Wrap",
|
||||
uiControl(uiNewNonWrappingMultilineEntry()),
|
||||
1);
|
||||
|
||||
return uiControl(vbox);
|
||||
}
|
||||
|
||||
// TODO make these not global
|
||||
static uiSpinbox *spinbox;
|
||||
static uiSlider *slider;
|
||||
static uiProgressBar *pbar;
|
||||
|
||||
static void onSpinboxChanged(uiSpinbox *s, void *data)
|
||||
{
|
||||
uiSliderSetValue(slider, uiSpinboxValue(s));
|
||||
uiProgressBarSetValue(pbar, uiSpinboxValue(s));
|
||||
}
|
||||
|
||||
static void onSliderChanged(uiSlider *s, void *data)
|
||||
{
|
||||
uiSpinboxSetValue(spinbox, uiSliderValue(s));
|
||||
uiProgressBarSetValue(pbar, uiSliderValue(s));
|
||||
}
|
||||
|
||||
static uiControl *makeNumbersPage()
|
||||
{
|
||||
uiBox *hbox;
|
||||
uiGroup *group;
|
||||
uiBox *vbox;
|
||||
uiProgressBar *ip;
|
||||
uiCombobox *cbox;
|
||||
uiEditableCombobox *ecbox;
|
||||
uiRadioButtons *rb;
|
||||
|
||||
hbox = uiNewHorizontalBox();
|
||||
uiBoxSetPadded(hbox, 1);
|
||||
|
||||
group = uiNewGroup("Numbers");
|
||||
uiGroupSetMargined(group, 1);
|
||||
uiBoxAppend(hbox, uiControl(group), 1);
|
||||
|
||||
vbox = uiNewVerticalBox();
|
||||
uiBoxSetPadded(vbox, 1);
|
||||
uiGroupSetChild(group, uiControl(vbox));
|
||||
|
||||
spinbox = uiNewSpinbox(0, 100);
|
||||
slider = uiNewSlider(0, 100);
|
||||
pbar = uiNewProgressBar();
|
||||
uiSpinboxOnChanged(spinbox, onSpinboxChanged, NULL);
|
||||
uiSliderOnChanged(slider, onSliderChanged, NULL);
|
||||
uiBoxAppend(vbox, uiControl(spinbox), 0);
|
||||
uiBoxAppend(vbox, uiControl(slider), 0);
|
||||
uiBoxAppend(vbox, uiControl(pbar), 0);
|
||||
|
||||
ip = uiNewProgressBar();
|
||||
uiProgressBarSetValue(ip, -1);
|
||||
uiBoxAppend(vbox, uiControl(ip), 0);
|
||||
|
||||
group = uiNewGroup("Lists");
|
||||
uiGroupSetMargined(group, 1);
|
||||
uiBoxAppend(hbox, uiControl(group), 1);
|
||||
|
||||
vbox = uiNewVerticalBox();
|
||||
uiBoxSetPadded(vbox, 1);
|
||||
uiGroupSetChild(group, uiControl(vbox));
|
||||
|
||||
cbox = uiNewCombobox();
|
||||
uiComboboxAppend(cbox, "Combobox Item 1");
|
||||
uiComboboxAppend(cbox, "Combobox Item 2");
|
||||
uiComboboxAppend(cbox, "Combobox Item 3");
|
||||
uiBoxAppend(vbox, uiControl(cbox), 0);
|
||||
|
||||
ecbox = uiNewEditableCombobox();
|
||||
uiEditableComboboxAppend(ecbox, "Editable Item 1");
|
||||
uiEditableComboboxAppend(ecbox, "Editable Item 2");
|
||||
uiEditableComboboxAppend(ecbox, "Editable Item 3");
|
||||
uiBoxAppend(vbox, uiControl(ecbox), 0);
|
||||
|
||||
rb = uiNewRadioButtons();
|
||||
uiRadioButtonsAppend(rb, "Radio Button 1");
|
||||
uiRadioButtonsAppend(rb, "Radio Button 2");
|
||||
uiRadioButtonsAppend(rb, "Radio Button 3");
|
||||
uiBoxAppend(vbox, uiControl(rb), 0);
|
||||
|
||||
return uiControl(hbox);
|
||||
}
|
||||
|
||||
// TODO make this not global
|
||||
static uiWindow *mainwin;
|
||||
|
||||
static void onOpenFileClicked(uiButton *b, void *data)
|
||||
{
|
||||
uiEntry *entry = uiEntry(data);
|
||||
char *filename;
|
||||
|
||||
filename = uiOpenFile(mainwin);
|
||||
if (filename == NULL) {
|
||||
uiEntrySetText(entry, "(cancelled)");
|
||||
return;
|
||||
}
|
||||
uiEntrySetText(entry, filename);
|
||||
uiFreeText(filename);
|
||||
}
|
||||
|
||||
static void onSaveFileClicked(uiButton *b, void *data)
|
||||
{
|
||||
uiEntry *entry = uiEntry(data);
|
||||
char *filename;
|
||||
|
||||
filename = uiSaveFile(mainwin);
|
||||
if (filename == NULL) {
|
||||
uiEntrySetText(entry, "(cancelled)");
|
||||
return;
|
||||
}
|
||||
uiEntrySetText(entry, filename);
|
||||
uiFreeText(filename);
|
||||
}
|
||||
|
||||
static void onMsgBoxClicked(uiButton *b, void *data)
|
||||
{
|
||||
uiMsgBox(mainwin,
|
||||
"This is a normal message box.",
|
||||
"More detailed information can be shown here.");
|
||||
}
|
||||
|
||||
static void onMsgBoxErrorClicked(uiButton *b, void *data)
|
||||
{
|
||||
uiMsgBoxError(mainwin,
|
||||
"This message box describes an error.",
|
||||
"More detailed information can be shown here.");
|
||||
}
|
||||
|
||||
static uiControl *makeDataChoosersPage(void)
|
||||
{
|
||||
uiBox *hbox;
|
||||
uiBox *vbox;
|
||||
uiGrid *grid;
|
||||
uiButton *button;
|
||||
uiEntry *entry;
|
||||
uiGrid *msggrid;
|
||||
|
||||
hbox = uiNewHorizontalBox();
|
||||
uiBoxSetPadded(hbox, 1);
|
||||
|
||||
vbox = uiNewVerticalBox();
|
||||
uiBoxSetPadded(vbox, 1);
|
||||
uiBoxAppend(hbox, uiControl(vbox), 0);
|
||||
|
||||
uiBoxAppend(vbox,
|
||||
uiControl(uiNewDatePicker()),
|
||||
0);
|
||||
uiBoxAppend(vbox,
|
||||
uiControl(uiNewTimePicker()),
|
||||
0);
|
||||
uiBoxAppend(vbox,
|
||||
uiControl(uiNewDateTimePicker()),
|
||||
0);
|
||||
|
||||
uiBoxAppend(vbox,
|
||||
uiControl(uiNewFontButton()),
|
||||
0);
|
||||
uiBoxAppend(vbox,
|
||||
uiControl(uiNewColorButton()),
|
||||
0);
|
||||
|
||||
uiBoxAppend(hbox,
|
||||
uiControl(uiNewVerticalSeparator()),
|
||||
0);
|
||||
|
||||
vbox = uiNewVerticalBox();
|
||||
uiBoxSetPadded(vbox, 1);
|
||||
uiBoxAppend(hbox, uiControl(vbox), 1);
|
||||
|
||||
grid = uiNewGrid();
|
||||
uiGridSetPadded(grid, 1);
|
||||
uiBoxAppend(vbox, uiControl(grid), 0);
|
||||
|
||||
button = uiNewButton("Open File");
|
||||
entry = uiNewEntry();
|
||||
uiEntrySetReadOnly(entry, 1);
|
||||
uiButtonOnClicked(button, onOpenFileClicked, entry);
|
||||
uiGridAppend(grid, uiControl(button),
|
||||
0, 0, 1, 1,
|
||||
0, uiAlignFill, 0, uiAlignFill);
|
||||
uiGridAppend(grid, uiControl(entry),
|
||||
1, 0, 1, 1,
|
||||
1, uiAlignFill, 0, uiAlignFill);
|
||||
|
||||
button = uiNewButton("Save File");
|
||||
entry = uiNewEntry();
|
||||
uiEntrySetReadOnly(entry, 1);
|
||||
uiButtonOnClicked(button, onSaveFileClicked, entry);
|
||||
uiGridAppend(grid, uiControl(button),
|
||||
0, 1, 1, 1,
|
||||
0, uiAlignFill, 0, uiAlignFill);
|
||||
uiGridAppend(grid, uiControl(entry),
|
||||
1, 1, 1, 1,
|
||||
1, uiAlignFill, 0, uiAlignFill);
|
||||
|
||||
msggrid = uiNewGrid();
|
||||
uiGridSetPadded(msggrid, 1);
|
||||
uiGridAppend(grid, uiControl(msggrid),
|
||||
0, 2, 2, 1,
|
||||
0, uiAlignCenter, 0, uiAlignStart);
|
||||
|
||||
button = uiNewButton("Message Box");
|
||||
uiButtonOnClicked(button, onMsgBoxClicked, NULL);
|
||||
uiGridAppend(msggrid, uiControl(button),
|
||||
0, 0, 1, 1,
|
||||
0, uiAlignFill, 0, uiAlignFill);
|
||||
button = uiNewButton("Error Box");
|
||||
uiButtonOnClicked(button, onMsgBoxErrorClicked, NULL);
|
||||
uiGridAppend(msggrid, uiControl(button),
|
||||
1, 0, 1, 1,
|
||||
0, uiAlignFill, 0, uiAlignFill);
|
||||
|
||||
return uiControl(hbox);
|
||||
}
|
||||
|
||||
int libui_main(void)
|
||||
{
|
||||
uiInitOptions options;
|
||||
const char *err;
|
||||
uiTab *tab;
|
||||
|
||||
memset(&options, 0, sizeof (uiInitOptions));
|
||||
err = uiInit(&options);
|
||||
if (err != NULL)
|
||||
{
|
||||
RARCH_ERR("Failed to initialize uiInit\n");
|
||||
fprintf(stderr, "error initializing libui: %s", err);
|
||||
uiFreeInitError(err);
|
||||
return 1;
|
||||
}
|
||||
|
||||
mainwin = uiNewWindow("libui Control Gallery", 640, 480, 1);
|
||||
uiWindowOnClosing(mainwin, onClosing, NULL);
|
||||
uiOnShouldQuit(onShouldQuit, mainwin);
|
||||
|
||||
tab = uiNewTab();
|
||||
uiWindowSetChild(mainwin, uiControl(tab));
|
||||
uiWindowSetMargined(mainwin, 1);
|
||||
|
||||
uiTabAppend(tab, "Basic Controls", makeBasicControlsPage());
|
||||
uiTabSetMargined(tab, 0, 1);
|
||||
|
||||
uiTabAppend(tab, "Numbers and Lists", makeNumbersPage());
|
||||
uiTabSetMargined(tab, 1, 1);
|
||||
|
||||
uiTabAppend(tab, "Data Choosers", makeDataChoosersPage());
|
||||
uiTabSetMargined(tab, 2, 1);
|
||||
|
||||
uiControlShow(uiControl(mainwin));
|
||||
uiMain();
|
||||
return 0;
|
||||
}
|
||||
|
||||
#if 0
|
||||
|
||||
static void openClicked(uiMenuItem *item, uiWindow *w, void *data)
|
||||
{
|
||||
char *filename;
|
||||
|
||||
filename = uiOpenFile(mainwin);
|
||||
if (filename == NULL) {
|
||||
uiMsgBoxError(mainwin, "No file selected", "Don't be alarmed!");
|
||||
return;
|
||||
}
|
||||
uiMsgBox(mainwin, "File selected", filename);
|
||||
uiFreeText(filename);
|
||||
}
|
||||
|
||||
static void saveClicked(uiMenuItem *item, uiWindow *w, void *data)
|
||||
{
|
||||
char *filename;
|
||||
|
||||
filename = uiSaveFile(mainwin);
|
||||
if (filename == NULL) {
|
||||
uiMsgBoxError(mainwin, "No file selected", "Don't be alarmed!");
|
||||
return;
|
||||
}
|
||||
uiMsgBox(mainwin, "File selected (don't worry, it's still there)", filename);
|
||||
uiFreeText(filename);
|
||||
}
|
||||
|
||||
static uiSpinbox *spinbox;
|
||||
static uiSlider *slider;
|
||||
static uiProgressBar *progressbar;
|
||||
|
||||
static void update(int value)
|
||||
{
|
||||
uiSpinboxSetValue(spinbox, value);
|
||||
uiSliderSetValue(slider, value);
|
||||
uiProgressBarSetValue(progressbar, value);
|
||||
}
|
||||
|
||||
static void onSpinboxChanged(uiSpinbox *s, void *data)
|
||||
{
|
||||
update(uiSpinboxValue(spinbox));
|
||||
}
|
||||
|
||||
static void onSliderChanged(uiSlider *s, void *data)
|
||||
{
|
||||
update(uiSliderValue(slider));
|
||||
}
|
||||
|
||||
int main(void)
|
||||
{
|
||||
uiInitOptions o;
|
||||
const char *err;
|
||||
uiMenu *menu;
|
||||
uiMenuItem *item;
|
||||
uiBox *box;
|
||||
uiBox *hbox;
|
||||
uiGroup *group;
|
||||
uiBox *inner;
|
||||
uiBox *inner2;
|
||||
uiEntry *entry;
|
||||
uiCombobox *cbox;
|
||||
uiEditableCombobox *ecbox;
|
||||
uiRadioButtons *rb;
|
||||
uiTab *tab;
|
||||
|
||||
memset(&o, 0, sizeof (uiInitOptions));
|
||||
err = uiInit(&o);
|
||||
if (err != NULL) {
|
||||
fprintf(stderr, "error initializing ui: %s\n", err);
|
||||
uiFreeInitError(err);
|
||||
return 1;
|
||||
}
|
||||
|
||||
menu = uiNewMenu("File");
|
||||
item = uiMenuAppendItem(menu, "Open");
|
||||
uiMenuItemOnClicked(item, openClicked, NULL);
|
||||
item = uiMenuAppendItem(menu, "Save");
|
||||
uiMenuItemOnClicked(item, saveClicked, NULL);
|
||||
item = uiMenuAppendQuitItem(menu);
|
||||
uiOnShouldQuit(shouldQuit, NULL);
|
||||
|
||||
menu = uiNewMenu("Edit");
|
||||
item = uiMenuAppendCheckItem(menu, "Checkable Item");
|
||||
uiMenuAppendSeparator(menu);
|
||||
item = uiMenuAppendItem(menu, "Disabled Item");
|
||||
uiMenuItemDisable(item);
|
||||
item = uiMenuAppendPreferencesItem(menu);
|
||||
|
||||
menu = uiNewMenu("Help");
|
||||
item = uiMenuAppendItem(menu, "Help");
|
||||
item = uiMenuAppendAboutItem(menu);
|
||||
|
||||
mainwin = uiNewWindow("libui Control Gallery", 640, 480, 1);
|
||||
uiWindowSetMargined(mainwin, 1);
|
||||
uiWindowOnClosing(mainwin, onClosing, NULL);
|
||||
|
||||
box = uiNewVerticalBox();
|
||||
uiBoxSetPadded(box, 1);
|
||||
uiWindowSetChild(mainwin, uiControl(box));
|
||||
|
||||
hbox = uiNewHorizontalBox();
|
||||
uiBoxSetPadded(hbox, 1);
|
||||
uiBoxAppend(box, uiControl(hbox), 1);
|
||||
|
||||
group = uiNewGroup("Basic Controls");
|
||||
uiGroupSetMargined(group, 1);
|
||||
uiBoxAppend(hbox, uiControl(group), 0);
|
||||
|
||||
inner = uiNewVerticalBox();
|
||||
uiBoxSetPadded(inner, 1);
|
||||
uiGroupSetChild(group, uiControl(inner));
|
||||
|
||||
uiBoxAppend(inner,
|
||||
uiControl(uiNewButton("Button")),
|
||||
0);
|
||||
uiBoxAppend(inner,
|
||||
uiControl(uiNewCheckbox("Checkbox")),
|
||||
0);
|
||||
entry = uiNewEntry();
|
||||
uiEntrySetText(entry, "Entry");
|
||||
uiBoxAppend(inner,
|
||||
uiControl(entry),
|
||||
0);
|
||||
uiBoxAppend(inner,
|
||||
uiControl(uiNewLabel("Label")),
|
||||
0);
|
||||
|
||||
uiBoxAppend(inner,
|
||||
uiControl(uiNewHorizontalSeparator()),
|
||||
0);
|
||||
|
||||
uiBoxAppend(inner,
|
||||
uiControl(uiNewDatePicker()),
|
||||
0);
|
||||
uiBoxAppend(inner,
|
||||
uiControl(uiNewTimePicker()),
|
||||
0);
|
||||
uiBoxAppend(inner,
|
||||
uiControl(uiNewDateTimePicker()),
|
||||
0);
|
||||
|
||||
uiBoxAppend(inner,
|
||||
uiControl(uiNewFontButton()),
|
||||
0);
|
||||
|
||||
uiBoxAppend(inner,
|
||||
uiControl(uiNewColorButton()),
|
||||
0);
|
||||
|
||||
inner2 = uiNewVerticalBox();
|
||||
uiBoxSetPadded(inner2, 1);
|
||||
uiBoxAppend(hbox, uiControl(inner2), 1);
|
||||
|
||||
group = uiNewGroup("Numbers");
|
||||
uiGroupSetMargined(group, 1);
|
||||
uiBoxAppend(inner2, uiControl(group), 0);
|
||||
|
||||
inner = uiNewVerticalBox();
|
||||
uiBoxSetPadded(inner, 1);
|
||||
uiGroupSetChild(group, uiControl(inner));
|
||||
|
||||
spinbox = uiNewSpinbox(0, 100);
|
||||
uiSpinboxOnChanged(spinbox, onSpinboxChanged, NULL);
|
||||
uiBoxAppend(inner, uiControl(spinbox), 0);
|
||||
|
||||
slider = uiNewSlider(0, 100);
|
||||
uiSliderOnChanged(slider, onSliderChanged, NULL);
|
||||
uiBoxAppend(inner, uiControl(slider), 0);
|
||||
|
||||
progressbar = uiNewProgressBar();
|
||||
uiBoxAppend(inner, uiControl(progressbar), 0);
|
||||
|
||||
group = uiNewGroup("Lists");
|
||||
uiGroupSetMargined(group, 1);
|
||||
uiBoxAppend(inner2, uiControl(group), 0);
|
||||
|
||||
inner = uiNewVerticalBox();
|
||||
uiBoxSetPadded(inner, 1);
|
||||
uiGroupSetChild(group, uiControl(inner));
|
||||
|
||||
cbox = uiNewCombobox();
|
||||
uiComboboxAppend(cbox, "Combobox Item 1");
|
||||
uiComboboxAppend(cbox, "Combobox Item 2");
|
||||
uiComboboxAppend(cbox, "Combobox Item 3");
|
||||
uiBoxAppend(inner, uiControl(cbox), 0);
|
||||
|
||||
ecbox = uiNewEditableCombobox();
|
||||
uiEditableComboboxAppend(ecbox, "Editable Item 1");
|
||||
uiEditableComboboxAppend(ecbox, "Editable Item 2");
|
||||
uiEditableComboboxAppend(ecbox, "Editable Item 3");
|
||||
uiBoxAppend(inner, uiControl(ecbox), 0);
|
||||
|
||||
rb = uiNewRadioButtons();
|
||||
uiRadioButtonsAppend(rb, "Radio Button 1");
|
||||
uiRadioButtonsAppend(rb, "Radio Button 2");
|
||||
uiRadioButtonsAppend(rb, "Radio Button 3");
|
||||
uiBoxAppend(inner, uiControl(rb), 1);
|
||||
|
||||
tab = uiNewTab();
|
||||
uiTabAppend(tab, "Page 1", uiControl(uiNewHorizontalBox()));
|
||||
uiTabAppend(tab, "Page 2", uiControl(uiNewHorizontalBox()));
|
||||
uiTabAppend(tab, "Page 3", uiControl(uiNewHorizontalBox()));
|
||||
uiBoxAppend(inner2, uiControl(tab), 1);
|
||||
|
||||
uiControlShow(uiControl(mainwin));
|
||||
uiMain();
|
||||
uiUninit();
|
||||
return 0;
|
||||
}
|
||||
|
||||
#endif
|
||||
Vendored
+691
@@ -0,0 +1,691 @@
|
||||
// 6 april 2015
|
||||
|
||||
// TODO add a uiVerifyControlType() function that can be used by control implementations to verify controls
|
||||
|
||||
#ifndef __LIBUI_UI_H__
|
||||
#define __LIBUI_UI_H__
|
||||
|
||||
#include <stddef.h>
|
||||
#include <stdint.h>
|
||||
|
||||
#ifdef __cplusplus
|
||||
extern "C" {
|
||||
#endif
|
||||
|
||||
// this macro is generated by cmake
|
||||
#ifdef libui_EXPORTS
|
||||
#ifdef _WIN32
|
||||
#define _UI_EXTERN __declspec(dllexport) extern
|
||||
#else
|
||||
#define _UI_EXTERN __attribute__((visibility("default"))) extern
|
||||
#endif
|
||||
#else
|
||||
// TODO add __declspec(dllimport) on windows, but only if not static
|
||||
#define _UI_EXTERN extern
|
||||
#endif
|
||||
|
||||
// C++ is really really really really really really dumb about enums, so screw that and just make them anonymous
|
||||
// This has the advantage of being ABI-able should we ever need an ABI...
|
||||
#define _UI_ENUM(s) typedef unsigned int s; enum
|
||||
|
||||
// This constant is provided because M_PI is nonstandard.
|
||||
// This comes from Go's math.Pi, which in turn comes from http://oeis.org/A000796.
|
||||
#define uiPi 3.14159265358979323846264338327950288419716939937510582097494459
|
||||
|
||||
// TODO uiBool?
|
||||
|
||||
typedef struct uiInitOptions uiInitOptions;
|
||||
|
||||
struct uiInitOptions {
|
||||
size_t Size;
|
||||
};
|
||||
|
||||
_UI_EXTERN const char *uiInit(uiInitOptions *options);
|
||||
_UI_EXTERN void uiUninit(void);
|
||||
_UI_EXTERN void uiFreeInitError(const char *err);
|
||||
|
||||
_UI_EXTERN void uiMain(void);
|
||||
_UI_EXTERN void uiMainSteps(void);
|
||||
_UI_EXTERN int uiMainStep(int wait);
|
||||
_UI_EXTERN void uiQuit(void);
|
||||
|
||||
_UI_EXTERN void uiQueueMain(void (*f)(void *data), void *data);
|
||||
|
||||
_UI_EXTERN void uiOnShouldQuit(int (*f)(void *data), void *data);
|
||||
|
||||
_UI_EXTERN void uiFreeText(char *text);
|
||||
|
||||
typedef struct uiControl uiControl;
|
||||
|
||||
struct uiControl {
|
||||
uint32_t Signature;
|
||||
uint32_t OSSignature;
|
||||
uint32_t TypeSignature;
|
||||
void (*Destroy)(uiControl *);
|
||||
uintptr_t (*Handle)(uiControl *);
|
||||
uiControl *(*Parent)(uiControl *);
|
||||
void (*SetParent)(uiControl *, uiControl *);
|
||||
int (*Toplevel)(uiControl *);
|
||||
int (*Visible)(uiControl *);
|
||||
void (*Show)(uiControl *);
|
||||
void (*Hide)(uiControl *);
|
||||
int (*Enabled)(uiControl *);
|
||||
void (*Enable)(uiControl *);
|
||||
void (*Disable)(uiControl *);
|
||||
};
|
||||
// TOOD add argument names to all arguments
|
||||
#define uiControl(this) ((uiControl *) (this))
|
||||
_UI_EXTERN void uiControlDestroy(uiControl *);
|
||||
_UI_EXTERN uintptr_t uiControlHandle(uiControl *);
|
||||
_UI_EXTERN uiControl *uiControlParent(uiControl *);
|
||||
_UI_EXTERN void uiControlSetParent(uiControl *, uiControl *);
|
||||
_UI_EXTERN int uiControlToplevel(uiControl *);
|
||||
_UI_EXTERN int uiControlVisible(uiControl *);
|
||||
_UI_EXTERN void uiControlShow(uiControl *);
|
||||
_UI_EXTERN void uiControlHide(uiControl *);
|
||||
_UI_EXTERN int uiControlEnabled(uiControl *);
|
||||
_UI_EXTERN void uiControlEnable(uiControl *);
|
||||
_UI_EXTERN void uiControlDisable(uiControl *);
|
||||
|
||||
_UI_EXTERN uiControl *uiAllocControl(size_t n, uint32_t OSsig, uint32_t typesig, const char *typenamestr);
|
||||
_UI_EXTERN void uiFreeControl(uiControl *);
|
||||
|
||||
// TODO make sure all controls have these
|
||||
_UI_EXTERN void uiControlVerifySetParent(uiControl *, uiControl *);
|
||||
_UI_EXTERN int uiControlEnabledToUser(uiControl *);
|
||||
|
||||
_UI_EXTERN void uiUserBugCannotSetParentOnToplevel(const char *type);
|
||||
|
||||
typedef struct uiWindow uiWindow;
|
||||
#define uiWindow(this) ((uiWindow *) (this))
|
||||
_UI_EXTERN char *uiWindowTitle(uiWindow *w);
|
||||
_UI_EXTERN void uiWindowSetTitle(uiWindow *w, const char *title);
|
||||
_UI_EXTERN void uiWindowContentSize(uiWindow *w, int *width, int *height);
|
||||
_UI_EXTERN void uiWindowSetContentSize(uiWindow *w, int width, int height);
|
||||
_UI_EXTERN int uiWindowFullscreen(uiWindow *w);
|
||||
_UI_EXTERN void uiWindowSetFullscreen(uiWindow *w, int fullscreen);
|
||||
_UI_EXTERN void uiWindowOnContentSizeChanged(uiWindow *w, void (*f)(uiWindow *, void *), void *data);
|
||||
_UI_EXTERN void uiWindowOnClosing(uiWindow *w, int (*f)(uiWindow *w, void *data), void *data);
|
||||
_UI_EXTERN int uiWindowBorderless(uiWindow *w);
|
||||
_UI_EXTERN void uiWindowSetBorderless(uiWindow *w, int borderless);
|
||||
_UI_EXTERN void uiWindowSetChild(uiWindow *w, uiControl *child);
|
||||
_UI_EXTERN int uiWindowMargined(uiWindow *w);
|
||||
_UI_EXTERN void uiWindowSetMargined(uiWindow *w, int margined);
|
||||
_UI_EXTERN uiWindow *uiNewWindow(const char *title, int width, int height, int hasMenubar);
|
||||
|
||||
typedef struct uiButton uiButton;
|
||||
#define uiButton(this) ((uiButton *) (this))
|
||||
_UI_EXTERN char *uiButtonText(uiButton *b);
|
||||
_UI_EXTERN void uiButtonSetText(uiButton *b, const char *text);
|
||||
_UI_EXTERN void uiButtonOnClicked(uiButton *b, void (*f)(uiButton *b, void *data), void *data);
|
||||
_UI_EXTERN uiButton *uiNewButton(const char *text);
|
||||
|
||||
typedef struct uiBox uiBox;
|
||||
#define uiBox(this) ((uiBox *) (this))
|
||||
_UI_EXTERN void uiBoxAppend(uiBox *b, uiControl *child, int stretchy);
|
||||
_UI_EXTERN void uiBoxDelete(uiBox *b, int index);
|
||||
_UI_EXTERN int uiBoxPadded(uiBox *b);
|
||||
_UI_EXTERN void uiBoxSetPadded(uiBox *b, int padded);
|
||||
_UI_EXTERN uiBox *uiNewHorizontalBox(void);
|
||||
_UI_EXTERN uiBox *uiNewVerticalBox(void);
|
||||
|
||||
typedef struct uiCheckbox uiCheckbox;
|
||||
#define uiCheckbox(this) ((uiCheckbox *) (this))
|
||||
_UI_EXTERN char *uiCheckboxText(uiCheckbox *c);
|
||||
_UI_EXTERN void uiCheckboxSetText(uiCheckbox *c, const char *text);
|
||||
_UI_EXTERN void uiCheckboxOnToggled(uiCheckbox *c, void (*f)(uiCheckbox *c, void *data), void *data);
|
||||
_UI_EXTERN int uiCheckboxChecked(uiCheckbox *c);
|
||||
_UI_EXTERN void uiCheckboxSetChecked(uiCheckbox *c, int checked);
|
||||
_UI_EXTERN uiCheckbox *uiNewCheckbox(const char *text);
|
||||
|
||||
typedef struct uiEntry uiEntry;
|
||||
#define uiEntry(this) ((uiEntry *) (this))
|
||||
_UI_EXTERN char *uiEntryText(uiEntry *e);
|
||||
_UI_EXTERN void uiEntrySetText(uiEntry *e, const char *text);
|
||||
_UI_EXTERN void uiEntryOnChanged(uiEntry *e, void (*f)(uiEntry *e, void *data), void *data);
|
||||
_UI_EXTERN int uiEntryReadOnly(uiEntry *e);
|
||||
_UI_EXTERN void uiEntrySetReadOnly(uiEntry *e, int readonly);
|
||||
_UI_EXTERN uiEntry *uiNewEntry(void);
|
||||
_UI_EXTERN uiEntry *uiNewPasswordEntry(void);
|
||||
_UI_EXTERN uiEntry *uiNewSearchEntry(void);
|
||||
|
||||
typedef struct uiLabel uiLabel;
|
||||
#define uiLabel(this) ((uiLabel *) (this))
|
||||
_UI_EXTERN char *uiLabelText(uiLabel *l);
|
||||
_UI_EXTERN void uiLabelSetText(uiLabel *l, const char *text);
|
||||
_UI_EXTERN uiLabel *uiNewLabel(const char *text);
|
||||
|
||||
typedef struct uiTab uiTab;
|
||||
#define uiTab(this) ((uiTab *) (this))
|
||||
_UI_EXTERN void uiTabAppend(uiTab *t, const char *name, uiControl *c);
|
||||
_UI_EXTERN void uiTabInsertAt(uiTab *t, const char *name, int before, uiControl *c);
|
||||
_UI_EXTERN void uiTabDelete(uiTab *t, int index);
|
||||
_UI_EXTERN int uiTabNumPages(uiTab *t);
|
||||
_UI_EXTERN int uiTabMargined(uiTab *t, int page);
|
||||
_UI_EXTERN void uiTabSetMargined(uiTab *t, int page, int margined);
|
||||
_UI_EXTERN uiTab *uiNewTab(void);
|
||||
|
||||
typedef struct uiGroup uiGroup;
|
||||
#define uiGroup(this) ((uiGroup *) (this))
|
||||
_UI_EXTERN char *uiGroupTitle(uiGroup *g);
|
||||
_UI_EXTERN void uiGroupSetTitle(uiGroup *g, const char *title);
|
||||
_UI_EXTERN void uiGroupSetChild(uiGroup *g, uiControl *c);
|
||||
_UI_EXTERN int uiGroupMargined(uiGroup *g);
|
||||
_UI_EXTERN void uiGroupSetMargined(uiGroup *g, int margined);
|
||||
_UI_EXTERN uiGroup *uiNewGroup(const char *title);
|
||||
|
||||
// spinbox/slider rules:
|
||||
// setting value outside of range will automatically clamp
|
||||
// initial value is minimum
|
||||
// complaint if min >= max?
|
||||
|
||||
typedef struct uiSpinbox uiSpinbox;
|
||||
#define uiSpinbox(this) ((uiSpinbox *) (this))
|
||||
_UI_EXTERN int uiSpinboxValue(uiSpinbox *s);
|
||||
_UI_EXTERN void uiSpinboxSetValue(uiSpinbox *s, int value);
|
||||
_UI_EXTERN void uiSpinboxOnChanged(uiSpinbox *s, void (*f)(uiSpinbox *s, void *data), void *data);
|
||||
_UI_EXTERN uiSpinbox *uiNewSpinbox(int min, int max);
|
||||
|
||||
typedef struct uiSlider uiSlider;
|
||||
#define uiSlider(this) ((uiSlider *) (this))
|
||||
_UI_EXTERN int uiSliderValue(uiSlider *s);
|
||||
_UI_EXTERN void uiSliderSetValue(uiSlider *s, int value);
|
||||
_UI_EXTERN void uiSliderOnChanged(uiSlider *s, void (*f)(uiSlider *s, void *data), void *data);
|
||||
_UI_EXTERN uiSlider *uiNewSlider(int min, int max);
|
||||
|
||||
typedef struct uiProgressBar uiProgressBar;
|
||||
#define uiProgressBar(this) ((uiProgressBar *) (this))
|
||||
_UI_EXTERN int uiProgressBarValue(uiProgressBar *p);
|
||||
_UI_EXTERN void uiProgressBarSetValue(uiProgressBar *p, int n);
|
||||
_UI_EXTERN uiProgressBar *uiNewProgressBar(void);
|
||||
|
||||
typedef struct uiSeparator uiSeparator;
|
||||
#define uiSeparator(this) ((uiSeparator *) (this))
|
||||
_UI_EXTERN uiSeparator *uiNewHorizontalSeparator(void);
|
||||
_UI_EXTERN uiSeparator *uiNewVerticalSeparator(void);
|
||||
|
||||
typedef struct uiCombobox uiCombobox;
|
||||
#define uiCombobox(this) ((uiCombobox *) (this))
|
||||
_UI_EXTERN void uiComboboxAppend(uiCombobox *c, const char *text);
|
||||
_UI_EXTERN int uiComboboxSelected(uiCombobox *c);
|
||||
_UI_EXTERN void uiComboboxSetSelected(uiCombobox *c, int n);
|
||||
_UI_EXTERN void uiComboboxOnSelected(uiCombobox *c, void (*f)(uiCombobox *c, void *data), void *data);
|
||||
_UI_EXTERN uiCombobox *uiNewCombobox(void);
|
||||
|
||||
typedef struct uiEditableCombobox uiEditableCombobox;
|
||||
#define uiEditableCombobox(this) ((uiEditableCombobox *) (this))
|
||||
_UI_EXTERN void uiEditableComboboxAppend(uiEditableCombobox *c, const char *text);
|
||||
_UI_EXTERN char *uiEditableComboboxText(uiEditableCombobox *c);
|
||||
_UI_EXTERN void uiEditableComboboxSetText(uiEditableCombobox *c, const char *text);
|
||||
// TODO what do we call a function that sets the currently selected item and fills the text field with it? editable comboboxes have no consistent concept of selected item
|
||||
_UI_EXTERN void uiEditableComboboxOnChanged(uiEditableCombobox *c, void (*f)(uiEditableCombobox *c, void *data), void *data);
|
||||
_UI_EXTERN uiEditableCombobox *uiNewEditableCombobox(void);
|
||||
|
||||
typedef struct uiRadioButtons uiRadioButtons;
|
||||
#define uiRadioButtons(this) ((uiRadioButtons *) (this))
|
||||
_UI_EXTERN void uiRadioButtonsAppend(uiRadioButtons *r, const char *text);
|
||||
_UI_EXTERN int uiRadioButtonsSelected(uiRadioButtons *r);
|
||||
_UI_EXTERN void uiRadioButtonsSetSelected(uiRadioButtons *r, int n);
|
||||
_UI_EXTERN void uiRadioButtonsOnSelected(uiRadioButtons *r, void (*f)(uiRadioButtons *, void *), void *data);
|
||||
_UI_EXTERN uiRadioButtons *uiNewRadioButtons(void);
|
||||
|
||||
typedef struct uiDateTimePicker uiDateTimePicker;
|
||||
#define uiDateTimePicker(this) ((uiDateTimePicker *) (this))
|
||||
_UI_EXTERN uiDateTimePicker *uiNewDateTimePicker(void);
|
||||
_UI_EXTERN uiDateTimePicker *uiNewDatePicker(void);
|
||||
_UI_EXTERN uiDateTimePicker *uiNewTimePicker(void);
|
||||
|
||||
// TODO provide a facility for entering tab stops?
|
||||
typedef struct uiMultilineEntry uiMultilineEntry;
|
||||
#define uiMultilineEntry(this) ((uiMultilineEntry *) (this))
|
||||
_UI_EXTERN char *uiMultilineEntryText(uiMultilineEntry *e);
|
||||
_UI_EXTERN void uiMultilineEntrySetText(uiMultilineEntry *e, const char *text);
|
||||
_UI_EXTERN void uiMultilineEntryAppend(uiMultilineEntry *e, const char *text);
|
||||
_UI_EXTERN void uiMultilineEntryOnChanged(uiMultilineEntry *e, void (*f)(uiMultilineEntry *e, void *data), void *data);
|
||||
_UI_EXTERN int uiMultilineEntryReadOnly(uiMultilineEntry *e);
|
||||
_UI_EXTERN void uiMultilineEntrySetReadOnly(uiMultilineEntry *e, int readonly);
|
||||
_UI_EXTERN uiMultilineEntry *uiNewMultilineEntry(void);
|
||||
_UI_EXTERN uiMultilineEntry *uiNewNonWrappingMultilineEntry(void);
|
||||
|
||||
typedef struct uiMenuItem uiMenuItem;
|
||||
#define uiMenuItem(this) ((uiMenuItem *) (this))
|
||||
_UI_EXTERN void uiMenuItemEnable(uiMenuItem *m);
|
||||
_UI_EXTERN void uiMenuItemDisable(uiMenuItem *m);
|
||||
_UI_EXTERN void uiMenuItemOnClicked(uiMenuItem *m, void (*f)(uiMenuItem *sender, uiWindow *window, void *data), void *data);
|
||||
_UI_EXTERN int uiMenuItemChecked(uiMenuItem *m);
|
||||
_UI_EXTERN void uiMenuItemSetChecked(uiMenuItem *m, int checked);
|
||||
|
||||
typedef struct uiMenu uiMenu;
|
||||
#define uiMenu(this) ((uiMenu *) (this))
|
||||
_UI_EXTERN uiMenuItem *uiMenuAppendItem(uiMenu *m, const char *name);
|
||||
_UI_EXTERN uiMenuItem *uiMenuAppendCheckItem(uiMenu *m, const char *name);
|
||||
_UI_EXTERN uiMenuItem *uiMenuAppendQuitItem(uiMenu *m);
|
||||
_UI_EXTERN uiMenuItem *uiMenuAppendPreferencesItem(uiMenu *m);
|
||||
_UI_EXTERN uiMenuItem *uiMenuAppendAboutItem(uiMenu *m);
|
||||
_UI_EXTERN void uiMenuAppendSeparator(uiMenu *m);
|
||||
_UI_EXTERN uiMenu *uiNewMenu(const char *name);
|
||||
|
||||
_UI_EXTERN char *uiOpenFile(uiWindow *parent);
|
||||
_UI_EXTERN char *uiSaveFile(uiWindow *parent);
|
||||
_UI_EXTERN void uiMsgBox(uiWindow *parent, const char *title, const char *description);
|
||||
_UI_EXTERN void uiMsgBoxError(uiWindow *parent, const char *title, const char *description);
|
||||
|
||||
typedef struct uiArea uiArea;
|
||||
typedef struct uiAreaHandler uiAreaHandler;
|
||||
typedef struct uiAreaDrawParams uiAreaDrawParams;
|
||||
typedef struct uiAreaMouseEvent uiAreaMouseEvent;
|
||||
typedef struct uiAreaKeyEvent uiAreaKeyEvent;
|
||||
|
||||
typedef struct uiDrawContext uiDrawContext;
|
||||
|
||||
struct uiAreaHandler {
|
||||
void (*Draw)(uiAreaHandler *, uiArea *, uiAreaDrawParams *);
|
||||
// TODO document that resizes cause a full redraw for non-scrolling areas; implementation-defined for scrolling areas
|
||||
void (*MouseEvent)(uiAreaHandler *, uiArea *, uiAreaMouseEvent *);
|
||||
// TODO document that on first show if the mouse is already in the uiArea then one gets sent with left=0
|
||||
// TODO what about when the area is hidden and then shown again?
|
||||
void (*MouseCrossed)(uiAreaHandler *, uiArea *, int left);
|
||||
void (*DragBroken)(uiAreaHandler *, uiArea *);
|
||||
int (*KeyEvent)(uiAreaHandler *, uiArea *, uiAreaKeyEvent *);
|
||||
};
|
||||
|
||||
// TODO RTL layouts?
|
||||
// TODO reconcile edge and corner naming
|
||||
_UI_ENUM(uiWindowResizeEdge) {
|
||||
uiWindowResizeEdgeLeft,
|
||||
uiWindowResizeEdgeTop,
|
||||
uiWindowResizeEdgeRight,
|
||||
uiWindowResizeEdgeBottom,
|
||||
uiWindowResizeEdgeTopLeft,
|
||||
uiWindowResizeEdgeTopRight,
|
||||
uiWindowResizeEdgeBottomLeft,
|
||||
uiWindowResizeEdgeBottomRight,
|
||||
// TODO have one for keyboard resizes?
|
||||
// TODO GDK doesn't seem to have any others, including for keyboards...
|
||||
// TODO way to bring up the system menu instead?
|
||||
};
|
||||
|
||||
#define uiArea(this) ((uiArea *) (this))
|
||||
// TODO give a better name
|
||||
// TODO document the types of width and height
|
||||
_UI_EXTERN void uiAreaSetSize(uiArea *a, int width, int height);
|
||||
// TODO uiAreaQueueRedraw()
|
||||
_UI_EXTERN void uiAreaQueueRedrawAll(uiArea *a);
|
||||
_UI_EXTERN void uiAreaScrollTo(uiArea *a, double x, double y, double width, double height);
|
||||
// TODO document these can only be called within Mouse() handlers
|
||||
// TODO should these be allowed on scrolling areas?
|
||||
// TODO decide which mouse events should be accepted; Down is the only one guaranteed to work right now
|
||||
// TODO what happens to events after calling this up to and including the next mouse up?
|
||||
// TODO release capture?
|
||||
_UI_EXTERN void uiAreaBeginUserWindowMove(uiArea *a);
|
||||
_UI_EXTERN void uiAreaBeginUserWindowResize(uiArea *a, uiWindowResizeEdge edge);
|
||||
_UI_EXTERN uiArea *uiNewArea(uiAreaHandler *ah);
|
||||
_UI_EXTERN uiArea *uiNewScrollingArea(uiAreaHandler *ah, int width, int height);
|
||||
|
||||
struct uiAreaDrawParams {
|
||||
uiDrawContext *Context;
|
||||
|
||||
// TODO document that this is only defined for nonscrolling areas
|
||||
double AreaWidth;
|
||||
double AreaHeight;
|
||||
|
||||
double ClipX;
|
||||
double ClipY;
|
||||
double ClipWidth;
|
||||
double ClipHeight;
|
||||
};
|
||||
|
||||
typedef struct uiDrawPath uiDrawPath;
|
||||
typedef struct uiDrawBrush uiDrawBrush;
|
||||
typedef struct uiDrawStrokeParams uiDrawStrokeParams;
|
||||
typedef struct uiDrawMatrix uiDrawMatrix;
|
||||
|
||||
typedef struct uiDrawBrushGradientStop uiDrawBrushGradientStop;
|
||||
|
||||
_UI_ENUM(uiDrawBrushType) {
|
||||
uiDrawBrushTypeSolid,
|
||||
uiDrawBrushTypeLinearGradient,
|
||||
uiDrawBrushTypeRadialGradient,
|
||||
uiDrawBrushTypeImage,
|
||||
};
|
||||
|
||||
_UI_ENUM(uiDrawLineCap) {
|
||||
uiDrawLineCapFlat,
|
||||
uiDrawLineCapRound,
|
||||
uiDrawLineCapSquare,
|
||||
};
|
||||
|
||||
_UI_ENUM(uiDrawLineJoin) {
|
||||
uiDrawLineJoinMiter,
|
||||
uiDrawLineJoinRound,
|
||||
uiDrawLineJoinBevel,
|
||||
};
|
||||
|
||||
// this is the default for botoh cairo and Direct2D (in the latter case, from the C++ helper functions)
|
||||
// Core Graphics doesn't explicitly specify a default, but NSBezierPath allows you to choose one, and this is the initial value
|
||||
// so we're good to use it too!
|
||||
#define uiDrawDefaultMiterLimit 10.0
|
||||
|
||||
_UI_ENUM(uiDrawFillMode) {
|
||||
uiDrawFillModeWinding,
|
||||
uiDrawFillModeAlternate,
|
||||
};
|
||||
|
||||
struct uiDrawMatrix {
|
||||
double M11;
|
||||
double M12;
|
||||
double M21;
|
||||
double M22;
|
||||
double M31;
|
||||
double M32;
|
||||
};
|
||||
|
||||
struct uiDrawBrush {
|
||||
uiDrawBrushType Type;
|
||||
|
||||
// solid brushes
|
||||
double R;
|
||||
double G;
|
||||
double B;
|
||||
double A;
|
||||
|
||||
// gradient brushes
|
||||
double X0; // linear: start X, radial: start X
|
||||
double Y0; // linear: start Y, radial: start Y
|
||||
double X1; // linear: end X, radial: outer circle center X
|
||||
double Y1; // linear: end Y, radial: outer circle center Y
|
||||
double OuterRadius; // radial gradients only
|
||||
uiDrawBrushGradientStop *Stops;
|
||||
size_t NumStops;
|
||||
// TODO extend mode
|
||||
// cairo: none, repeat, reflect, pad; no individual control
|
||||
// Direct2D: repeat, reflect, pad; no individual control
|
||||
// Core Graphics: none, pad; before and after individually
|
||||
// TODO cairo documentation is inconsistent about pad
|
||||
|
||||
// TODO images
|
||||
|
||||
// TODO transforms
|
||||
};
|
||||
|
||||
struct uiDrawBrushGradientStop {
|
||||
double Pos;
|
||||
double R;
|
||||
double G;
|
||||
double B;
|
||||
double A;
|
||||
};
|
||||
|
||||
struct uiDrawStrokeParams {
|
||||
uiDrawLineCap Cap;
|
||||
uiDrawLineJoin Join;
|
||||
// TODO what if this is 0? on windows there will be a crash with dashing
|
||||
double Thickness;
|
||||
double MiterLimit;
|
||||
double *Dashes;
|
||||
// TOOD what if this is 1 on Direct2D?
|
||||
// TODO what if a dash is 0 on Cairo or Quartz?
|
||||
size_t NumDashes;
|
||||
double DashPhase;
|
||||
};
|
||||
|
||||
_UI_EXTERN uiDrawPath *uiDrawNewPath(uiDrawFillMode fillMode);
|
||||
_UI_EXTERN void uiDrawFreePath(uiDrawPath *p);
|
||||
|
||||
_UI_EXTERN void uiDrawPathNewFigure(uiDrawPath *p, double x, double y);
|
||||
_UI_EXTERN void uiDrawPathNewFigureWithArc(uiDrawPath *p, double xCenter, double yCenter, double radius, double startAngle, double sweep, int negative);
|
||||
_UI_EXTERN void uiDrawPathLineTo(uiDrawPath *p, double x, double y);
|
||||
// notes: angles are both relative to 0 and go counterclockwise
|
||||
// TODO is the initial line segment on cairo and OS X a proper join?
|
||||
// TODO what if sweep < 0?
|
||||
_UI_EXTERN void uiDrawPathArcTo(uiDrawPath *p, double xCenter, double yCenter, double radius, double startAngle, double sweep, int negative);
|
||||
_UI_EXTERN void uiDrawPathBezierTo(uiDrawPath *p, double c1x, double c1y, double c2x, double c2y, double endX, double endY);
|
||||
// TODO quadratic bezier
|
||||
_UI_EXTERN void uiDrawPathCloseFigure(uiDrawPath *p);
|
||||
|
||||
// TODO effect of these when a figure is already started
|
||||
_UI_EXTERN void uiDrawPathAddRectangle(uiDrawPath *p, double x, double y, double width, double height);
|
||||
|
||||
_UI_EXTERN void uiDrawPathEnd(uiDrawPath *p);
|
||||
|
||||
_UI_EXTERN void uiDrawStroke(uiDrawContext *c, uiDrawPath *path, uiDrawBrush *b, uiDrawStrokeParams *p);
|
||||
_UI_EXTERN void uiDrawFill(uiDrawContext *c, uiDrawPath *path, uiDrawBrush *b);
|
||||
|
||||
// TODO primitives:
|
||||
// - rounded rectangles
|
||||
// - elliptical arcs
|
||||
// - quadratic bezier curves
|
||||
|
||||
_UI_EXTERN void uiDrawMatrixSetIdentity(uiDrawMatrix *m);
|
||||
_UI_EXTERN void uiDrawMatrixTranslate(uiDrawMatrix *m, double x, double y);
|
||||
_UI_EXTERN void uiDrawMatrixScale(uiDrawMatrix *m, double xCenter, double yCenter, double x, double y);
|
||||
_UI_EXTERN void uiDrawMatrixRotate(uiDrawMatrix *m, double x, double y, double amount);
|
||||
_UI_EXTERN void uiDrawMatrixSkew(uiDrawMatrix *m, double x, double y, double xamount, double yamount);
|
||||
_UI_EXTERN void uiDrawMatrixMultiply(uiDrawMatrix *dest, uiDrawMatrix *src);
|
||||
_UI_EXTERN int uiDrawMatrixInvertible(uiDrawMatrix *m);
|
||||
_UI_EXTERN int uiDrawMatrixInvert(uiDrawMatrix *m);
|
||||
_UI_EXTERN void uiDrawMatrixTransformPoint(uiDrawMatrix *m, double *x, double *y);
|
||||
_UI_EXTERN void uiDrawMatrixTransformSize(uiDrawMatrix *m, double *x, double *y);
|
||||
|
||||
_UI_EXTERN void uiDrawTransform(uiDrawContext *c, uiDrawMatrix *m);
|
||||
|
||||
// TODO add a uiDrawPathStrokeToFill() or something like that
|
||||
_UI_EXTERN void uiDrawClip(uiDrawContext *c, uiDrawPath *path);
|
||||
|
||||
_UI_EXTERN void uiDrawSave(uiDrawContext *c);
|
||||
_UI_EXTERN void uiDrawRestore(uiDrawContext *c);
|
||||
|
||||
// TODO manage the use of Text, Font, and TextFont, and of the uiDrawText prefix in general
|
||||
|
||||
///// TODO reconsider this
|
||||
typedef struct uiDrawFontFamilies uiDrawFontFamilies;
|
||||
|
||||
_UI_EXTERN uiDrawFontFamilies *uiDrawListFontFamilies(void);
|
||||
_UI_EXTERN int uiDrawFontFamiliesNumFamilies(uiDrawFontFamilies *ff);
|
||||
_UI_EXTERN char *uiDrawFontFamiliesFamily(uiDrawFontFamilies *ff, int n);
|
||||
_UI_EXTERN void uiDrawFreeFontFamilies(uiDrawFontFamilies *ff);
|
||||
///// END TODO
|
||||
|
||||
typedef struct uiDrawTextLayout uiDrawTextLayout;
|
||||
typedef struct uiDrawTextFont uiDrawTextFont;
|
||||
typedef struct uiDrawTextFontDescriptor uiDrawTextFontDescriptor;
|
||||
typedef struct uiDrawTextFontMetrics uiDrawTextFontMetrics;
|
||||
|
||||
_UI_ENUM(uiDrawTextWeight) {
|
||||
uiDrawTextWeightThin,
|
||||
uiDrawTextWeightUltraLight,
|
||||
uiDrawTextWeightLight,
|
||||
uiDrawTextWeightBook,
|
||||
uiDrawTextWeightNormal,
|
||||
uiDrawTextWeightMedium,
|
||||
uiDrawTextWeightSemiBold,
|
||||
uiDrawTextWeightBold,
|
||||
uiDrawTextWeightUltraBold,
|
||||
uiDrawTextWeightHeavy,
|
||||
uiDrawTextWeightUltraHeavy,
|
||||
};
|
||||
|
||||
_UI_ENUM(uiDrawTextItalic) {
|
||||
uiDrawTextItalicNormal,
|
||||
uiDrawTextItalicOblique,
|
||||
uiDrawTextItalicItalic,
|
||||
};
|
||||
|
||||
_UI_ENUM(uiDrawTextStretch) {
|
||||
uiDrawTextStretchUltraCondensed,
|
||||
uiDrawTextStretchExtraCondensed,
|
||||
uiDrawTextStretchCondensed,
|
||||
uiDrawTextStretchSemiCondensed,
|
||||
uiDrawTextStretchNormal,
|
||||
uiDrawTextStretchSemiExpanded,
|
||||
uiDrawTextStretchExpanded,
|
||||
uiDrawTextStretchExtraExpanded,
|
||||
uiDrawTextStretchUltraExpanded,
|
||||
};
|
||||
|
||||
struct uiDrawTextFontDescriptor {
|
||||
const char *Family;
|
||||
double Size;
|
||||
uiDrawTextWeight Weight;
|
||||
uiDrawTextItalic Italic;
|
||||
uiDrawTextStretch Stretch;
|
||||
};
|
||||
|
||||
struct uiDrawTextFontMetrics {
|
||||
double Ascent;
|
||||
double Descent;
|
||||
double Leading;
|
||||
// TODO do these two mean the same across all platforms?
|
||||
double UnderlinePos;
|
||||
double UnderlineThickness;
|
||||
};
|
||||
|
||||
_UI_EXTERN uiDrawTextFont *uiDrawLoadClosestFont(const uiDrawTextFontDescriptor *desc);
|
||||
_UI_EXTERN void uiDrawFreeTextFont(uiDrawTextFont *font);
|
||||
_UI_EXTERN uintptr_t uiDrawTextFontHandle(uiDrawTextFont *font);
|
||||
_UI_EXTERN void uiDrawTextFontDescribe(uiDrawTextFont *font, uiDrawTextFontDescriptor *desc);
|
||||
// TODO make copy with given attributes methods?
|
||||
// TODO yuck this name
|
||||
_UI_EXTERN void uiDrawTextFontGetMetrics(uiDrawTextFont *font, uiDrawTextFontMetrics *metrics);
|
||||
|
||||
// TODO initial line spacing? and what about leading?
|
||||
_UI_EXTERN uiDrawTextLayout *uiDrawNewTextLayout(const char *text, uiDrawTextFont *defaultFont, double width);
|
||||
_UI_EXTERN void uiDrawFreeTextLayout(uiDrawTextLayout *layout);
|
||||
// TODO get width
|
||||
_UI_EXTERN void uiDrawTextLayoutSetWidth(uiDrawTextLayout *layout, double width);
|
||||
_UI_EXTERN void uiDrawTextLayoutExtents(uiDrawTextLayout *layout, double *width, double *height);
|
||||
|
||||
// and the attributes that you can set on a text layout
|
||||
_UI_EXTERN void uiDrawTextLayoutSetColor(uiDrawTextLayout *layout, int startChar, int endChar, double r, double g, double b, double a);
|
||||
|
||||
_UI_EXTERN void uiDrawText(uiDrawContext *c, double x, double y, uiDrawTextLayout *layout);
|
||||
|
||||
_UI_ENUM(uiModifiers) {
|
||||
uiModifierCtrl = 1 << 0,
|
||||
uiModifierAlt = 1 << 1,
|
||||
uiModifierShift = 1 << 2,
|
||||
uiModifierSuper = 1 << 3,
|
||||
};
|
||||
|
||||
// TODO document drag captures
|
||||
struct uiAreaMouseEvent {
|
||||
// TODO document what these mean for scrolling areas
|
||||
double X;
|
||||
double Y;
|
||||
|
||||
// TODO see draw above
|
||||
double AreaWidth;
|
||||
double AreaHeight;
|
||||
|
||||
int Down;
|
||||
int Up;
|
||||
|
||||
int Count;
|
||||
|
||||
uiModifiers Modifiers;
|
||||
|
||||
uint64_t Held1To64;
|
||||
};
|
||||
|
||||
_UI_ENUM(uiExtKey) {
|
||||
uiExtKeyEscape = 1,
|
||||
uiExtKeyInsert, // equivalent to "Help" on Apple keyboards
|
||||
uiExtKeyDelete,
|
||||
uiExtKeyHome,
|
||||
uiExtKeyEnd,
|
||||
uiExtKeyPageUp,
|
||||
uiExtKeyPageDown,
|
||||
uiExtKeyUp,
|
||||
uiExtKeyDown,
|
||||
uiExtKeyLeft,
|
||||
uiExtKeyRight,
|
||||
uiExtKeyF1, // F1..F12 are guaranteed to be consecutive
|
||||
uiExtKeyF2,
|
||||
uiExtKeyF3,
|
||||
uiExtKeyF4,
|
||||
uiExtKeyF5,
|
||||
uiExtKeyF6,
|
||||
uiExtKeyF7,
|
||||
uiExtKeyF8,
|
||||
uiExtKeyF9,
|
||||
uiExtKeyF10,
|
||||
uiExtKeyF11,
|
||||
uiExtKeyF12,
|
||||
uiExtKeyN0, // numpad keys; independent of Num Lock state
|
||||
uiExtKeyN1, // N0..N9 are guaranteed to be consecutive
|
||||
uiExtKeyN2,
|
||||
uiExtKeyN3,
|
||||
uiExtKeyN4,
|
||||
uiExtKeyN5,
|
||||
uiExtKeyN6,
|
||||
uiExtKeyN7,
|
||||
uiExtKeyN8,
|
||||
uiExtKeyN9,
|
||||
uiExtKeyNDot,
|
||||
uiExtKeyNEnter,
|
||||
uiExtKeyNAdd,
|
||||
uiExtKeyNSubtract,
|
||||
uiExtKeyNMultiply,
|
||||
uiExtKeyNDivide,
|
||||
};
|
||||
|
||||
struct uiAreaKeyEvent {
|
||||
char Key;
|
||||
uiExtKey ExtKey;
|
||||
uiModifiers Modifier;
|
||||
|
||||
uiModifiers Modifiers;
|
||||
|
||||
int Up;
|
||||
};
|
||||
|
||||
typedef struct uiFontButton uiFontButton;
|
||||
#define uiFontButton(this) ((uiFontButton *) (this))
|
||||
// TODO document this returns a new font
|
||||
_UI_EXTERN uiDrawTextFont *uiFontButtonFont(uiFontButton *b);
|
||||
// TOOD SetFont, mechanics
|
||||
_UI_EXTERN void uiFontButtonOnChanged(uiFontButton *b, void (*f)(uiFontButton *, void *), void *data);
|
||||
_UI_EXTERN uiFontButton *uiNewFontButton(void);
|
||||
|
||||
typedef struct uiColorButton uiColorButton;
|
||||
#define uiColorButton(this) ((uiColorButton *) (this))
|
||||
_UI_EXTERN void uiColorButtonColor(uiColorButton *b, double *r, double *g, double *bl, double *a);
|
||||
_UI_EXTERN void uiColorButtonSetColor(uiColorButton *b, double r, double g, double bl, double a);
|
||||
_UI_EXTERN void uiColorButtonOnChanged(uiColorButton *b, void (*f)(uiColorButton *, void *), void *data);
|
||||
_UI_EXTERN uiColorButton *uiNewColorButton(void);
|
||||
|
||||
typedef struct uiForm uiForm;
|
||||
#define uiForm(this) ((uiForm *) (this))
|
||||
_UI_EXTERN void uiFormAppend(uiForm *f, const char *label, uiControl *c, int stretchy);
|
||||
_UI_EXTERN void uiFormDelete(uiForm *f, int index);
|
||||
_UI_EXTERN int uiFormPadded(uiForm *f);
|
||||
_UI_EXTERN void uiFormSetPadded(uiForm *f, int padded);
|
||||
_UI_EXTERN uiForm *uiNewForm(void);
|
||||
|
||||
_UI_ENUM(uiAlign) {
|
||||
uiAlignFill,
|
||||
uiAlignStart,
|
||||
uiAlignCenter,
|
||||
uiAlignEnd,
|
||||
};
|
||||
|
||||
_UI_ENUM(uiAt) {
|
||||
uiAtLeading,
|
||||
uiAtTop,
|
||||
uiAtTrailing,
|
||||
uiAtBottom,
|
||||
};
|
||||
|
||||
typedef struct uiGrid uiGrid;
|
||||
#define uiGrid(this) ((uiGrid *) (this))
|
||||
_UI_EXTERN void uiGridAppend(uiGrid *g, uiControl *c, int left, int top, int xspan, int yspan, int hexpand, uiAlign halign, int vexpand, uiAlign valign);
|
||||
_UI_EXTERN void uiGridInsertAt(uiGrid *g, uiControl *c, uiControl *existing, uiAt at, int xspan, int yspan, int hexpand, uiAlign halign, int vexpand, uiAlign valign);
|
||||
_UI_EXTERN int uiGridPadded(uiGrid *g);
|
||||
_UI_EXTERN void uiGridSetPadded(uiGrid *g, int padded);
|
||||
_UI_EXTERN uiGrid *uiNewGrid(void);
|
||||
|
||||
#ifdef __cplusplus
|
||||
}
|
||||
#endif
|
||||
|
||||
#endif
|
||||
Vendored
+224
@@ -0,0 +1,224 @@
|
||||
// 7 april 2015
|
||||
|
||||
/*
|
||||
This file assumes that you have imported <Cocoa/Cocoa.h> and "ui.h" beforehand. It provides API-specific functions for interfacing with foreign controls on Mac OS X.
|
||||
*/
|
||||
|
||||
#ifndef __LIBUI_UI_DARWIN_H__
|
||||
#define __LIBUI_UI_DARWIN_H__
|
||||
|
||||
#ifdef __cplusplus
|
||||
extern "C" {
|
||||
#endif
|
||||
|
||||
typedef struct uiDarwinControl uiDarwinControl;
|
||||
struct uiDarwinControl {
|
||||
uiControl c;
|
||||
uiControl *parent;
|
||||
BOOL enabled;
|
||||
BOOL visible;
|
||||
void (*SyncEnableState)(uiDarwinControl *, int);
|
||||
void (*SetSuperview)(uiDarwinControl *, NSView *);
|
||||
BOOL (*HugsTrailingEdge)(uiDarwinControl *);
|
||||
BOOL (*HugsBottom)(uiDarwinControl *);
|
||||
void (*ChildEdgeHuggingChanged)(uiDarwinControl *);
|
||||
NSLayoutPriority (*HuggingPriority)(uiDarwinControl *, NSLayoutConstraintOrientation);
|
||||
void (*SetHuggingPriority)(uiDarwinControl *, NSLayoutPriority, NSLayoutConstraintOrientation);
|
||||
void (*ChildVisibilityChanged)(uiDarwinControl *);
|
||||
};
|
||||
#define uiDarwinControl(this) ((uiDarwinControl *) (this))
|
||||
// TODO document
|
||||
_UI_EXTERN void uiDarwinControlSyncEnableState(uiDarwinControl *, int);
|
||||
_UI_EXTERN void uiDarwinControlSetSuperview(uiDarwinControl *, NSView *);
|
||||
_UI_EXTERN BOOL uiDarwinControlHugsTrailingEdge(uiDarwinControl *);
|
||||
_UI_EXTERN BOOL uiDarwinControlHugsBottom(uiDarwinControl *);
|
||||
_UI_EXTERN void uiDarwinControlChildEdgeHuggingChanged(uiDarwinControl *);
|
||||
_UI_EXTERN NSLayoutPriority uiDarwinControlHuggingPriority(uiDarwinControl *, NSLayoutConstraintOrientation);
|
||||
_UI_EXTERN void uiDarwinControlSetHuggingPriority(uiDarwinControl *, NSLayoutPriority, NSLayoutConstraintOrientation);
|
||||
_UI_EXTERN void uiDarwinControlChildVisibilityChanged(uiDarwinControl *);
|
||||
|
||||
#define uiDarwinControlDefaultDestroy(type, handlefield) \
|
||||
static void type ## Destroy(uiControl *c) \
|
||||
{ \
|
||||
[type(c)->handlefield release]; \
|
||||
uiFreeControl(c); \
|
||||
}
|
||||
#define uiDarwinControlDefaultHandle(type, handlefield) \
|
||||
static uintptr_t type ## Handle(uiControl *c) \
|
||||
{ \
|
||||
return (uintptr_t) (type(c)->handlefield); \
|
||||
}
|
||||
#define uiDarwinControlDefaultParent(type, handlefield) \
|
||||
static uiControl *type ## Parent(uiControl *c) \
|
||||
{ \
|
||||
return uiDarwinControl(c)->parent; \
|
||||
}
|
||||
#define uiDarwinControlDefaultSetParent(type, handlefield) \
|
||||
static void type ## SetParent(uiControl *c, uiControl *parent) \
|
||||
{ \
|
||||
uiControlVerifySetParent(c, parent); \
|
||||
uiDarwinControl(c)->parent = parent; \
|
||||
}
|
||||
#define uiDarwinControlDefaultToplevel(type, handlefield) \
|
||||
static int type ## Toplevel(uiControl *c) \
|
||||
{ \
|
||||
return 0; \
|
||||
}
|
||||
#define uiDarwinControlDefaultVisible(type, handlefield) \
|
||||
static int type ## Visible(uiControl *c) \
|
||||
{ \
|
||||
return uiDarwinControl(c)->visible; \
|
||||
}
|
||||
#define uiDarwinControlDefaultShow(type, handlefield) \
|
||||
static void type ## Show(uiControl *c) \
|
||||
{ \
|
||||
uiDarwinControl(c)->visible = YES; \
|
||||
[type(c)->handlefield setHidden:NO]; \
|
||||
uiDarwinNotifyVisibilityChanged(uiDarwinControl(c)); \
|
||||
}
|
||||
#define uiDarwinControlDefaultHide(type, handlefield) \
|
||||
static void type ## Hide(uiControl *c) \
|
||||
{ \
|
||||
uiDarwinControl(c)->visible = NO; \
|
||||
[type(c)->handlefield setHidden:YES]; \
|
||||
uiDarwinNotifyVisibilityChanged(uiDarwinControl(c)); \
|
||||
}
|
||||
#define uiDarwinControlDefaultEnabled(type, handlefield) \
|
||||
static int type ## Enabled(uiControl *c) \
|
||||
{ \
|
||||
return uiDarwinControl(c)->enabled; \
|
||||
}
|
||||
#define uiDarwinControlDefaultEnable(type, handlefield) \
|
||||
static void type ## Enable(uiControl *c) \
|
||||
{ \
|
||||
uiDarwinControl(c)->enabled = YES; \
|
||||
uiDarwinControlSyncEnableState(uiDarwinControl(c), uiControlEnabledToUser(c)); \
|
||||
}
|
||||
#define uiDarwinControlDefaultDisable(type, handlefield) \
|
||||
static void type ## Disable(uiControl *c) \
|
||||
{ \
|
||||
uiDarwinControl(c)->enabled = NO; \
|
||||
uiDarwinControlSyncEnableState(uiDarwinControl(c), uiControlEnabledToUser(c)); \
|
||||
}
|
||||
#define uiDarwinControlDefaultSyncEnableState(type, handlefield) \
|
||||
static void type ## SyncEnableState(uiDarwinControl *c, int enabled) \
|
||||
{ \
|
||||
if (uiDarwinShouldStopSyncEnableState(c, enabled)) \
|
||||
return; \
|
||||
if ([type(c)->handlefield respondsToSelector:@selector(setEnabled:)]) \
|
||||
[((id) (type(c)->handlefield)) setEnabled:enabled]; /* id cast to make compiler happy; thanks mikeash in irc.freenode.net/#macdev */ \
|
||||
}
|
||||
#define uiDarwinControlDefaultSetSuperview(type, handlefield) \
|
||||
static void type ## SetSuperview(uiDarwinControl *c, NSView *superview) \
|
||||
{ \
|
||||
[type(c)->handlefield setTranslatesAutoresizingMaskIntoConstraints:NO]; \
|
||||
if (superview == nil) \
|
||||
[type(c)->handlefield removeFromSuperview]; \
|
||||
else \
|
||||
[superview addSubview:type(c)->handlefield]; \
|
||||
}
|
||||
#define uiDarwinControlDefaultHugsTrailingEdge(type, handlefield) \
|
||||
static BOOL type ## HugsTrailingEdge(uiDarwinControl *c) \
|
||||
{ \
|
||||
return YES; /* always hug by default */ \
|
||||
}
|
||||
#define uiDarwinControlDefaultHugsBottom(type, handlefield) \
|
||||
static BOOL type ## HugsBottom(uiDarwinControl *c) \
|
||||
{ \
|
||||
return YES; /* always hug by default */ \
|
||||
}
|
||||
#define uiDarwinControlDefaultChildEdgeHuggingChanged(type, handlefield) \
|
||||
static void type ## ChildEdgeHuggingChanged(uiDarwinControl *c) \
|
||||
{ \
|
||||
/* do nothing */ \
|
||||
}
|
||||
#define uiDarwinControlDefaultHuggingPriority(type, handlefield) \
|
||||
static NSLayoutPriority type ## HuggingPriority(uiDarwinControl *c, NSLayoutConstraintOrientation orientation) \
|
||||
{ \
|
||||
return [type(c)->handlefield contentHuggingPriorityForOrientation:orientation]; \
|
||||
}
|
||||
#define uiDarwinControlDefaultSetHuggingPriority(type, handlefield) \
|
||||
static void type ## SetHuggingPriority(uiDarwinControl *c, NSLayoutPriority priority, NSLayoutConstraintOrientation orientation) \
|
||||
{ \
|
||||
[type(c)->handlefield setContentHuggingPriority:priority forOrientation:orientation]; \
|
||||
}
|
||||
#define uiDarwinControlDefaultChildVisibilityChanged(type, handlefield) \
|
||||
static void type ## ChildVisibilityChanged(uiDarwinControl *c) \
|
||||
{ \
|
||||
/* do nothing */ \
|
||||
}
|
||||
|
||||
#define uiDarwinControlAllDefaultsExceptDestroy(type, handlefield) \
|
||||
uiDarwinControlDefaultHandle(type, handlefield) \
|
||||
uiDarwinControlDefaultParent(type, handlefield) \
|
||||
uiDarwinControlDefaultSetParent(type, handlefield) \
|
||||
uiDarwinControlDefaultToplevel(type, handlefield) \
|
||||
uiDarwinControlDefaultVisible(type, handlefield) \
|
||||
uiDarwinControlDefaultShow(type, handlefield) \
|
||||
uiDarwinControlDefaultHide(type, handlefield) \
|
||||
uiDarwinControlDefaultEnabled(type, handlefield) \
|
||||
uiDarwinControlDefaultEnable(type, handlefield) \
|
||||
uiDarwinControlDefaultDisable(type, handlefield) \
|
||||
uiDarwinControlDefaultSyncEnableState(type, handlefield) \
|
||||
uiDarwinControlDefaultSetSuperview(type, handlefield) \
|
||||
uiDarwinControlDefaultHugsTrailingEdge(type, handlefield) \
|
||||
uiDarwinControlDefaultHugsBottom(type, handlefield) \
|
||||
uiDarwinControlDefaultChildEdgeHuggingChanged(type, handlefield) \
|
||||
uiDarwinControlDefaultHuggingPriority(type, handlefield) \
|
||||
uiDarwinControlDefaultSetHuggingPriority(type, handlefield) \
|
||||
uiDarwinControlDefaultChildVisibilityChanged(type, handlefield)
|
||||
|
||||
#define uiDarwinControlAllDefaults(type, handlefield) \
|
||||
uiDarwinControlDefaultDestroy(type, handlefield) \
|
||||
uiDarwinControlAllDefaultsExceptDestroy(type, handlefield)
|
||||
|
||||
// TODO document
|
||||
#define uiDarwinNewControl(type, var) \
|
||||
var = type(uiDarwinAllocControl(sizeof (type), type ## Signature, #type)); \
|
||||
uiControl(var)->Destroy = type ## Destroy; \
|
||||
uiControl(var)->Handle = type ## Handle; \
|
||||
uiControl(var)->Parent = type ## Parent; \
|
||||
uiControl(var)->SetParent = type ## SetParent; \
|
||||
uiControl(var)->Toplevel = type ## Toplevel; \
|
||||
uiControl(var)->Visible = type ## Visible; \
|
||||
uiControl(var)->Show = type ## Show; \
|
||||
uiControl(var)->Hide = type ## Hide; \
|
||||
uiControl(var)->Enabled = type ## Enabled; \
|
||||
uiControl(var)->Enable = type ## Enable; \
|
||||
uiControl(var)->Disable = type ## Disable; \
|
||||
uiDarwinControl(var)->SyncEnableState = type ## SyncEnableState; \
|
||||
uiDarwinControl(var)->SetSuperview = type ## SetSuperview; \
|
||||
uiDarwinControl(var)->HugsTrailingEdge = type ## HugsTrailingEdge; \
|
||||
uiDarwinControl(var)->HugsBottom = type ## HugsBottom; \
|
||||
uiDarwinControl(var)->ChildEdgeHuggingChanged = type ## ChildEdgeHuggingChanged; \
|
||||
uiDarwinControl(var)->HuggingPriority = type ## HuggingPriority; \
|
||||
uiDarwinControl(var)->SetHuggingPriority = type ## SetHuggingPriority; \
|
||||
uiDarwinControl(var)->ChildVisibilityChanged = type ## ChildVisibilityChanged; \
|
||||
uiDarwinControl(var)->visible = YES; \
|
||||
uiDarwinControl(var)->enabled = YES;
|
||||
// TODO document
|
||||
_UI_EXTERN uiDarwinControl *uiDarwinAllocControl(size_t n, uint32_t typesig, const char *typenamestr);
|
||||
|
||||
// Use this function as a shorthand for setting control fonts.
|
||||
_UI_EXTERN void uiDarwinSetControlFont(NSControl *c, NSControlSize size);
|
||||
|
||||
// You can use this function from within your control implementations to return text strings that can be freed with uiFreeText().
|
||||
_UI_EXTERN char *uiDarwinNSStringToText(NSString *);
|
||||
|
||||
// TODO document
|
||||
_UI_EXTERN BOOL uiDarwinShouldStopSyncEnableState(uiDarwinControl *, BOOL);
|
||||
|
||||
// TODO document
|
||||
_UI_EXTERN void uiDarwinNotifyEdgeHuggingChanged(uiDarwinControl *);
|
||||
_UI_EXTERN void uiDarwinNotifyVisibilityChanged(uiDarwinControl *c);
|
||||
|
||||
// TODO document
|
||||
// TODO document that values should not be cached
|
||||
_UI_EXTERN CGFloat uiDarwinMarginAmount(void *reserved);
|
||||
_UI_EXTERN CGFloat uiDarwinPaddingAmount(void *reserved);
|
||||
|
||||
#ifdef __cplusplus
|
||||
}
|
||||
#endif
|
||||
|
||||
#endif
|
||||
Vendored
+140
@@ -0,0 +1,140 @@
|
||||
// 7 april 2015
|
||||
|
||||
/*
|
||||
This file assumes that you have included <gtk/gtk.h> and "ui.h" beforehand. It provides API-specific functions for interfacing with foreign controls on Unix systems that use GTK+ to provide their UI (currently all except Mac OS X).
|
||||
*/
|
||||
|
||||
#ifndef __LIBUI_UI_UNIX_H__
|
||||
#define __LIBUI_UI_UNIX_H__
|
||||
|
||||
#ifdef __cplusplus
|
||||
extern "C" {
|
||||
#endif
|
||||
|
||||
typedef struct uiUnixControl uiUnixControl;
|
||||
struct uiUnixControl {
|
||||
uiControl c;
|
||||
uiControl *parent;
|
||||
gboolean addedBefore;
|
||||
void (*SetContainer)(uiUnixControl *, GtkContainer *, gboolean);
|
||||
};
|
||||
#define uiUnixControl(this) ((uiUnixControl *) (this))
|
||||
// TODO document
|
||||
_UI_EXTERN void uiUnixControlSetContainer(uiUnixControl *, GtkContainer *, gboolean);
|
||||
|
||||
#define uiUnixControlDefaultDestroy(type) \
|
||||
static void type ## Destroy(uiControl *c) \
|
||||
{ \
|
||||
/* TODO is this safe on floating refs? */ \
|
||||
g_object_unref(type(c)->widget); \
|
||||
uiFreeControl(c); \
|
||||
}
|
||||
#define uiUnixControlDefaultHandle(type) \
|
||||
static uintptr_t type ## Handle(uiControl *c) \
|
||||
{ \
|
||||
return (uintptr_t) (type(c)->widget); \
|
||||
}
|
||||
#define uiUnixControlDefaultParent(type) \
|
||||
static uiControl *type ## Parent(uiControl *c) \
|
||||
{ \
|
||||
return uiUnixControl(c)->parent; \
|
||||
}
|
||||
#define uiUnixControlDefaultSetParent(type) \
|
||||
static void type ## SetParent(uiControl *c, uiControl *parent) \
|
||||
{ \
|
||||
uiControlVerifySetParent(c, parent); \
|
||||
uiUnixControl(c)->parent = parent; \
|
||||
}
|
||||
#define uiUnixControlDefaultToplevel(type) \
|
||||
static int type ## Toplevel(uiControl *c) \
|
||||
{ \
|
||||
return 0; \
|
||||
}
|
||||
#define uiUnixControlDefaultVisible(type) \
|
||||
static int type ## Visible(uiControl *c) \
|
||||
{ \
|
||||
return gtk_widget_get_visible(type(c)->widget); \
|
||||
}
|
||||
#define uiUnixControlDefaultShow(type) \
|
||||
static void type ## Show(uiControl *c) \
|
||||
{ \
|
||||
gtk_widget_show(type(c)->widget); \
|
||||
}
|
||||
#define uiUnixControlDefaultHide(type) \
|
||||
static void type ## Hide(uiControl *c) \
|
||||
{ \
|
||||
gtk_widget_hide(type(c)->widget); \
|
||||
}
|
||||
#define uiUnixControlDefaultEnabled(type) \
|
||||
static int type ## Enabled(uiControl *c) \
|
||||
{ \
|
||||
return gtk_widget_get_sensitive(type(c)->widget); \
|
||||
}
|
||||
#define uiUnixControlDefaultEnable(type) \
|
||||
static void type ## Enable(uiControl *c) \
|
||||
{ \
|
||||
gtk_widget_set_sensitive(type(c)->widget, TRUE); \
|
||||
}
|
||||
#define uiUnixControlDefaultDisable(type) \
|
||||
static void type ## Disable(uiControl *c) \
|
||||
{ \
|
||||
gtk_widget_set_sensitive(type(c)->widget, FALSE); \
|
||||
}
|
||||
// TODO this whole addedBefore stuff is a MASSIVE HACK.
|
||||
#define uiUnixControlDefaultSetContainer(type) \
|
||||
static void type ## SetContainer(uiUnixControl *c, GtkContainer *container, gboolean remove) \
|
||||
{ \
|
||||
if (!uiUnixControl(c)->addedBefore) { \
|
||||
g_object_ref_sink(type(c)->widget); /* our own reference, which we release in Destroy() */ \
|
||||
gtk_widget_show(type(c)->widget); \
|
||||
uiUnixControl(c)->addedBefore = TRUE; \
|
||||
} \
|
||||
if (remove) \
|
||||
gtk_container_remove(container, type(c)->widget); \
|
||||
else \
|
||||
gtk_container_add(container, type(c)->widget); \
|
||||
}
|
||||
|
||||
#define uiUnixControlAllDefaultsExceptDestroy(type) \
|
||||
uiUnixControlDefaultHandle(type) \
|
||||
uiUnixControlDefaultParent(type) \
|
||||
uiUnixControlDefaultSetParent(type) \
|
||||
uiUnixControlDefaultToplevel(type) \
|
||||
uiUnixControlDefaultVisible(type) \
|
||||
uiUnixControlDefaultShow(type) \
|
||||
uiUnixControlDefaultHide(type) \
|
||||
uiUnixControlDefaultEnabled(type) \
|
||||
uiUnixControlDefaultEnable(type) \
|
||||
uiUnixControlDefaultDisable(type) \
|
||||
uiUnixControlDefaultSetContainer(type)
|
||||
|
||||
#define uiUnixControlAllDefaults(type) \
|
||||
uiUnixControlDefaultDestroy(type) \
|
||||
uiUnixControlAllDefaultsExceptDestroy(type)
|
||||
|
||||
// TODO document
|
||||
#define uiUnixNewControl(type, var) \
|
||||
var = type(uiUnixAllocControl(sizeof (type), type ## Signature, #type)); \
|
||||
uiControl(var)->Destroy = type ## Destroy; \
|
||||
uiControl(var)->Handle = type ## Handle; \
|
||||
uiControl(var)->Parent = type ## Parent; \
|
||||
uiControl(var)->SetParent = type ## SetParent; \
|
||||
uiControl(var)->Toplevel = type ## Toplevel; \
|
||||
uiControl(var)->Visible = type ## Visible; \
|
||||
uiControl(var)->Show = type ## Show; \
|
||||
uiControl(var)->Hide = type ## Hide; \
|
||||
uiControl(var)->Enabled = type ## Enabled; \
|
||||
uiControl(var)->Enable = type ## Enable; \
|
||||
uiControl(var)->Disable = type ## Disable; \
|
||||
uiUnixControl(var)->SetContainer = type ## SetContainer;
|
||||
// TODO document
|
||||
_UI_EXTERN uiUnixControl *uiUnixAllocControl(size_t n, uint32_t typesig, const char *typenamestr);
|
||||
|
||||
// uiUnixStrdupText() takes the given string and produces a copy of it suitable for being freed by uiFreeText().
|
||||
_UI_EXTERN char *uiUnixStrdupText(const char *);
|
||||
|
||||
#ifdef __cplusplus
|
||||
}
|
||||
#endif
|
||||
|
||||
#endif
|
||||
Vendored
+267
@@ -0,0 +1,267 @@
|
||||
// 21 april 2016
|
||||
|
||||
/*
|
||||
This file assumes that you have included <windows.h> and "ui.h" beforehand. It provides API-specific functions for interfacing with foreign controls in Windows.
|
||||
*/
|
||||
|
||||
#ifndef __LIBUI_UI_WINDOWS_H__
|
||||
#define __LIBUI_UI_WINDOWS_H__
|
||||
|
||||
#ifdef __cplusplus
|
||||
extern "C" {
|
||||
#endif
|
||||
|
||||
typedef struct uiWindowsSizing uiWindowsSizing;
|
||||
|
||||
typedef struct uiWindowsControl uiWindowsControl;
|
||||
struct uiWindowsControl {
|
||||
uiControl c;
|
||||
uiControl *parent;
|
||||
// TODO this should be int on both os x and windows
|
||||
BOOL enabled;
|
||||
BOOL visible;
|
||||
void (*SyncEnableState)(uiWindowsControl *, int);
|
||||
void (*SetParentHWND)(uiWindowsControl *, HWND);
|
||||
void (*MinimumSize)(uiWindowsControl *, int *, int *);
|
||||
void (*MinimumSizeChanged)(uiWindowsControl *);
|
||||
void (*LayoutRect)(uiWindowsControl *c, RECT *r);
|
||||
void (*AssignControlIDZOrder)(uiWindowsControl *, LONG_PTR *, HWND *);
|
||||
void (*ChildVisibilityChanged)(uiWindowsControl *);
|
||||
};
|
||||
#define uiWindowsControl(this) ((uiWindowsControl *) (this))
|
||||
// TODO document
|
||||
_UI_EXTERN void uiWindowsControlSyncEnableState(uiWindowsControl *, int);
|
||||
_UI_EXTERN void uiWindowsControlSetParentHWND(uiWindowsControl *, HWND);
|
||||
_UI_EXTERN void uiWindowsControlMinimumSize(uiWindowsControl *, int *, int *);
|
||||
_UI_EXTERN void uiWindowsControlMinimumSizeChanged(uiWindowsControl *);
|
||||
_UI_EXTERN void uiWindowsControlLayoutRect(uiWindowsControl *, RECT *);
|
||||
_UI_EXTERN void uiWindowsControlAssignControlIDZOrder(uiWindowsControl *, LONG_PTR *, HWND *);
|
||||
_UI_EXTERN void uiWindowsControlChildVisibilityChanged(uiWindowsControl *);
|
||||
|
||||
// TODO document
|
||||
#define uiWindowsControlDefaultDestroy(type) \
|
||||
static void type ## Destroy(uiControl *c) \
|
||||
{ \
|
||||
uiWindowsEnsureDestroyWindow(type(c)->hwnd); \
|
||||
uiFreeControl(c); \
|
||||
}
|
||||
#define uiWindowsControlDefaultHandle(type) \
|
||||
static uintptr_t type ## Handle(uiControl *c) \
|
||||
{ \
|
||||
return (uintptr_t) (type(c)->hwnd); \
|
||||
}
|
||||
#define uiWindowsControlDefaultParent(type) \
|
||||
static uiControl *type ## Parent(uiControl *c) \
|
||||
{ \
|
||||
return uiWindowsControl(c)->parent; \
|
||||
}
|
||||
#define uiWindowsControlDefaultSetParent(type) \
|
||||
static void type ## SetParent(uiControl *c, uiControl *parent) \
|
||||
{ \
|
||||
uiControlVerifySetParent(c, parent); \
|
||||
uiWindowsControl(c)->parent = parent; \
|
||||
}
|
||||
#define uiWindowsControlDefaultToplevel(type) \
|
||||
static int type ## Toplevel(uiControl *c) \
|
||||
{ \
|
||||
return 0; \
|
||||
}
|
||||
#define uiWindowsControlDefaultVisible(type) \
|
||||
static int type ## Visible(uiControl *c) \
|
||||
{ \
|
||||
return uiWindowsControl(c)->visible; \
|
||||
}
|
||||
#define uiWindowsControlDefaultShow(type) \
|
||||
static void type ## Show(uiControl *c) \
|
||||
{ \
|
||||
uiWindowsControl(c)->visible = 1; \
|
||||
ShowWindow(type(c)->hwnd, SW_SHOW); \
|
||||
uiWindowsControlNotifyVisibilityChanged(uiWindowsControl(c)); \
|
||||
}
|
||||
#define uiWindowsControlDefaultHide(type) \
|
||||
static void type ## Hide(uiControl *c) \
|
||||
{ \
|
||||
uiWindowsControl(c)->visible = 0; \
|
||||
ShowWindow(type(c)->hwnd, SW_HIDE); \
|
||||
uiWindowsControlNotifyVisibilityChanged(uiWindowsControl(c)); \
|
||||
}
|
||||
#define uiWindowsControlDefaultEnabled(type) \
|
||||
static int type ## Enabled(uiControl *c) \
|
||||
{ \
|
||||
return uiWindowsControl(c)->enabled; \
|
||||
}
|
||||
#define uiWindowsControlDefaultEnable(type) \
|
||||
static void type ## Enable(uiControl *c) \
|
||||
{ \
|
||||
uiWindowsControl(c)->enabled = 1; \
|
||||
uiWindowsControlSyncEnableState(uiWindowsControl(c), uiControlEnabledToUser(c)); \
|
||||
}
|
||||
#define uiWindowsControlDefaultDisable(type) \
|
||||
static void type ## Disable(uiControl *c) \
|
||||
{ \
|
||||
uiWindowsControl(c)->enabled = 0; \
|
||||
uiWindowsControlSyncEnableState(uiWindowsControl(c), uiControlEnabledToUser(c)); \
|
||||
}
|
||||
#define uiWindowsControlDefaultSyncEnableState(type) \
|
||||
static void type ## SyncEnableState(uiWindowsControl *c, int enabled) \
|
||||
{ \
|
||||
if (uiWindowsShouldStopSyncEnableState(c, enabled)) \
|
||||
return; \
|
||||
EnableWindow(type(c)->hwnd, enabled); \
|
||||
}
|
||||
#define uiWindowsControlDefaultSetParentHWND(type) \
|
||||
static void type ## SetParentHWND(uiWindowsControl *c, HWND parent) \
|
||||
{ \
|
||||
uiWindowsEnsureSetParentHWND(type(c)->hwnd, parent); \
|
||||
}
|
||||
// note that there is no uiWindowsControlDefaultMinimumSize(); you MUST define this yourself!
|
||||
#define uiWindowsControlDefaultMinimumSizeChanged(type) \
|
||||
static void type ## MinimumSizeChanged(uiWindowsControl *c) \
|
||||
{ \
|
||||
if (uiWindowsControlTooSmall(c)) { \
|
||||
uiWindowsControlContinueMinimumSizeChanged(c); \
|
||||
return; \
|
||||
} \
|
||||
/* otherwise do nothing; we have no children */ \
|
||||
}
|
||||
#define uiWindowsControlDefaultLayoutRect(type) \
|
||||
static void type ## LayoutRect(uiWindowsControl *c, RECT *r) \
|
||||
{ \
|
||||
/* use the window rect as we include the non-client area in the sizes */ \
|
||||
uiWindowsEnsureGetWindowRect(type(c)->hwnd, r); \
|
||||
}
|
||||
#define uiWindowsControlDefaultAssignControlIDZOrder(type) \
|
||||
static void type ## AssignControlIDZOrder(uiWindowsControl *c, LONG_PTR *controlID, HWND *insertAfter) \
|
||||
{ \
|
||||
uiWindowsEnsureAssignControlIDZOrder(type(c)->hwnd, controlID, insertAfter); \
|
||||
}
|
||||
#define uiWindowsControlDefaultChildVisibilityChanged(type) \
|
||||
static void type ## ChildVisibilityChanged(uiWindowsControl *c) \
|
||||
{ \
|
||||
/* do nothing */ \
|
||||
}
|
||||
|
||||
#define uiWindowsControlAllDefaultsExceptDestroy(type) \
|
||||
uiWindowsControlDefaultHandle(type) \
|
||||
uiWindowsControlDefaultParent(type) \
|
||||
uiWindowsControlDefaultSetParent(type) \
|
||||
uiWindowsControlDefaultToplevel(type) \
|
||||
uiWindowsControlDefaultVisible(type) \
|
||||
uiWindowsControlDefaultShow(type) \
|
||||
uiWindowsControlDefaultHide(type) \
|
||||
uiWindowsControlDefaultEnabled(type) \
|
||||
uiWindowsControlDefaultEnable(type) \
|
||||
uiWindowsControlDefaultDisable(type) \
|
||||
uiWindowsControlDefaultSyncEnableState(type) \
|
||||
uiWindowsControlDefaultSetParentHWND(type) \
|
||||
uiWindowsControlDefaultMinimumSizeChanged(type) \
|
||||
uiWindowsControlDefaultLayoutRect(type) \
|
||||
uiWindowsControlDefaultAssignControlIDZOrder(type) \
|
||||
uiWindowsControlDefaultChildVisibilityChanged(type)
|
||||
|
||||
#define uiWindowsControlAllDefaults(type) \
|
||||
uiWindowsControlDefaultDestroy(type) \
|
||||
uiWindowsControlAllDefaultsExceptDestroy(type)
|
||||
|
||||
// TODO document
|
||||
#define uiWindowsNewControl(type, var) \
|
||||
var = type(uiWindowsAllocControl(sizeof (type), type ## Signature, #type)); \
|
||||
uiControl(var)->Destroy = type ## Destroy; \
|
||||
uiControl(var)->Handle = type ## Handle; \
|
||||
uiControl(var)->Parent = type ## Parent; \
|
||||
uiControl(var)->SetParent = type ## SetParent; \
|
||||
uiControl(var)->Toplevel = type ## Toplevel; \
|
||||
uiControl(var)->Visible = type ## Visible; \
|
||||
uiControl(var)->Show = type ## Show; \
|
||||
uiControl(var)->Hide = type ## Hide; \
|
||||
uiControl(var)->Enabled = type ## Enabled; \
|
||||
uiControl(var)->Enable = type ## Enable; \
|
||||
uiControl(var)->Disable = type ## Disable; \
|
||||
uiWindowsControl(var)->SyncEnableState = type ## SyncEnableState; \
|
||||
uiWindowsControl(var)->SetParentHWND = type ## SetParentHWND; \
|
||||
uiWindowsControl(var)->MinimumSize = type ## MinimumSize; \
|
||||
uiWindowsControl(var)->MinimumSizeChanged = type ## MinimumSizeChanged; \
|
||||
uiWindowsControl(var)->LayoutRect = type ## LayoutRect; \
|
||||
uiWindowsControl(var)->AssignControlIDZOrder = type ## AssignControlIDZOrder; \
|
||||
uiWindowsControl(var)->ChildVisibilityChanged = type ## ChildVisibilityChanged; \
|
||||
uiWindowsControl(var)->visible = 1; \
|
||||
uiWindowsControl(var)->enabled = 1;
|
||||
// TODO document
|
||||
_UI_EXTERN uiWindowsControl *uiWindowsAllocControl(size_t n, uint32_t typesig, const char *typenamestr);
|
||||
|
||||
// TODO document
|
||||
_UI_EXTERN HWND uiWindowsEnsureCreateControlHWND(DWORD dwExStyle, LPCWSTR lpClassName, LPCWSTR lpWindowName, DWORD dwStyle, HINSTANCE hInstance, LPVOID lpParam, BOOL useStandardControlFont);
|
||||
|
||||
// TODO document
|
||||
_UI_EXTERN void uiWindowsEnsureDestroyWindow(HWND hwnd);
|
||||
|
||||
// TODO document
|
||||
// TODO document that this should only be used in SetParentHWND() implementations
|
||||
_UI_EXTERN void uiWindowsEnsureSetParentHWND(HWND hwnd, HWND parent);
|
||||
|
||||
// TODO document
|
||||
_UI_EXTERN void uiWindowsEnsureAssignControlIDZOrder(HWND hwnd, LONG_PTR *controlID, HWND *insertAfter);
|
||||
|
||||
// TODO document
|
||||
_UI_EXTERN void uiWindowsEnsureGetClientRect(HWND hwnd, RECT *r);
|
||||
_UI_EXTERN void uiWindowsEnsureGetWindowRect(HWND hwnd, RECT *r);
|
||||
|
||||
// TODO document
|
||||
_UI_EXTERN char *uiWindowsWindowText(HWND hwnd);
|
||||
_UI_EXTERN void uiWindowsSetWindowText(HWND hwnd, const char *text);
|
||||
|
||||
// TODO document
|
||||
_UI_EXTERN int uiWindowsWindowTextWidth(HWND hwnd);
|
||||
|
||||
// TODO document
|
||||
// TODO point out this should only be used in a resize cycle
|
||||
_UI_EXTERN void uiWindowsEnsureMoveWindowDuringResize(HWND hwnd, int x, int y, int width, int height);
|
||||
|
||||
// TODO document
|
||||
_UI_EXTERN void uiWindowsRegisterWM_COMMANDHandler(HWND hwnd, BOOL (*handler)(uiControl *, HWND, WORD, LRESULT *), uiControl *c);
|
||||
_UI_EXTERN void uiWindowsUnregisterWM_COMMANDHandler(HWND hwnd);
|
||||
|
||||
// TODO document
|
||||
_UI_EXTERN void uiWindowsRegisterWM_NOTIFYHandler(HWND hwnd, BOOL (*handler)(uiControl *, HWND, NMHDR *, LRESULT *), uiControl *c);
|
||||
_UI_EXTERN void uiWindowsUnregisterWM_NOTIFYHandler(HWND hwnd);
|
||||
|
||||
// TODO document
|
||||
_UI_EXTERN void uiWindowsRegisterWM_HSCROLLHandler(HWND hwnd, BOOL (*handler)(uiControl *, HWND, WORD, LRESULT *), uiControl *c);
|
||||
_UI_EXTERN void uiWindowsUnregisterWM_HSCROLLHandler(HWND hwnd);
|
||||
|
||||
// TODO document
|
||||
_UI_EXTERN void uiWindowsRegisterReceiveWM_WININICHANGE(HWND hwnd);
|
||||
_UI_EXTERN void uiWindowsUnregisterReceiveWM_WININICHANGE(HWND hwnd);
|
||||
|
||||
// TODO document
|
||||
typedef struct uiWindowsSizing uiWindowsSizing;
|
||||
struct uiWindowsSizing {
|
||||
int BaseX;
|
||||
int BaseY;
|
||||
LONG InternalLeading;
|
||||
};
|
||||
_UI_EXTERN void uiWindowsGetSizing(HWND hwnd, uiWindowsSizing *sizing);
|
||||
_UI_EXTERN void uiWindowsSizingDlgUnitsToPixels(uiWindowsSizing *sizing, int *x, int *y);
|
||||
_UI_EXTERN void uiWindowsSizingStandardPadding(uiWindowsSizing *sizing, int *x, int *y);
|
||||
|
||||
// TODO document
|
||||
_UI_EXTERN HWND uiWindowsMakeContainer(uiWindowsControl *c, void (*onResize)(uiWindowsControl *));
|
||||
|
||||
// TODO document
|
||||
_UI_EXTERN BOOL uiWindowsControlTooSmall(uiWindowsControl *c);
|
||||
_UI_EXTERN void uiWindowsControlContinueMinimumSizeChanged(uiWindowsControl *c);
|
||||
|
||||
// TODO document
|
||||
_UI_EXTERN void uiWindowsControlAssignSoleControlIDZOrder(uiWindowsControl *);
|
||||
|
||||
// TODO document
|
||||
_UI_EXTERN BOOL uiWindowsShouldStopSyncEnableState(uiWindowsControl *c, int enabled);
|
||||
|
||||
// TODO document
|
||||
_UI_EXTERN void uiWindowsControlNotifyVisibilityChanged(uiWindowsControl *c);
|
||||
|
||||
#ifdef __cplusplus
|
||||
}
|
||||
#endif
|
||||
|
||||
#endif
|
||||
Vendored
+85
@@ -0,0 +1,85 @@
|
||||
# 3 june 2016
|
||||
|
||||
find_package(PkgConfig REQUIRED)
|
||||
pkg_check_modules(GTK REQUIRED gtk+-3.0)
|
||||
|
||||
list(APPEND _LIBUI_SOURCES
|
||||
unix/alloc.c
|
||||
unix/area.c
|
||||
unix/box.c
|
||||
unix/button.c
|
||||
unix/cellrendererbutton.c
|
||||
unix/checkbox.c
|
||||
unix/child.c
|
||||
unix/colorbutton.c
|
||||
unix/combobox.c
|
||||
unix/control.c
|
||||
unix/datetimepicker.c
|
||||
unix/debug.c
|
||||
unix/draw.c
|
||||
unix/drawmatrix.c
|
||||
unix/drawpath.c
|
||||
unix/drawtext.c
|
||||
unix/editablecombo.c
|
||||
unix/entry.c
|
||||
unix/fontbutton.c
|
||||
unix/form.c
|
||||
unix/future.c
|
||||
unix/graphemes.c
|
||||
unix/grid.c
|
||||
unix/group.c
|
||||
unix/image.c
|
||||
unix/label.c
|
||||
unix/main.c
|
||||
unix/menu.c
|
||||
unix/multilineentry.c
|
||||
unix/progressbar.c
|
||||
unix/radiobuttons.c
|
||||
unix/separator.c
|
||||
unix/slider.c
|
||||
unix/spinbox.c
|
||||
unix/stddialogs.c
|
||||
unix/tab.c
|
||||
unix/text.c
|
||||
unix/util.c
|
||||
unix/window.c
|
||||
)
|
||||
set(_LIBUI_SOURCES ${_LIBUI_SOURCES} PARENT_SCOPE)
|
||||
|
||||
list(APPEND _LIBUI_INCLUDEDIRS
|
||||
unix
|
||||
)
|
||||
set(_LIBUI_INCLUDEDIRS _LIBUI_INCLUDEDIRS PARENT_SCOPE)
|
||||
|
||||
set(_LIBUINAME libui PARENT_SCOPE)
|
||||
if(NOT BUILD_SHARED_LIBS)
|
||||
set(_LIBUINAME libui-temporary PARENT_SCOPE)
|
||||
endif()
|
||||
macro(_handle_static)
|
||||
set_target_properties(${_LIBUINAME} PROPERTIES
|
||||
ARCHIVE_OUTPUT_DIRECTORY "${PROJECT_BINARY_DIR}")
|
||||
set(_aname $<TARGET_FILE:${_LIBUINAME}>)
|
||||
set(_oname libui-combined.o)
|
||||
add_custom_command(
|
||||
OUTPUT ${_oname}
|
||||
COMMAND
|
||||
ld -r --whole-archive ${_aname} -o ${_oname}
|
||||
COMMAND
|
||||
objcopy --localize-hidden ${_oname}
|
||||
COMMENT "Removing hidden symbols")
|
||||
add_library(libui STATIC ${_oname})
|
||||
# otherwise cmake won't know which linker to use
|
||||
set_target_properties(libui PROPERTIES
|
||||
LINKER_LANGUAGE C)
|
||||
set(_aname)
|
||||
set(_oname)
|
||||
endmacro()
|
||||
|
||||
# TODO the other variables don't work?
|
||||
set(_LIBUI_CFLAGS
|
||||
${GTK_CFLAGS}
|
||||
PARENT_SCOPE)
|
||||
|
||||
set(_LIBUI_LIBS
|
||||
${GTK_LDFLAGS} m ${CMAKE_DL_LIBS}
|
||||
PARENT_SCOPE)
|
||||
Vendored
+84
@@ -0,0 +1,84 @@
|
||||
// 7 april 2015
|
||||
#include <string.h>
|
||||
#include "uipriv_unix.h"
|
||||
|
||||
static GPtrArray *allocations;
|
||||
|
||||
#define UINT8(p) ((uint8_t *) (p))
|
||||
#define PVOID(p) ((void *) (p))
|
||||
#define EXTRA (sizeof (size_t) + sizeof (const char **))
|
||||
#define DATA(p) PVOID(UINT8(p) + EXTRA)
|
||||
#define BASE(p) PVOID(UINT8(p) - EXTRA)
|
||||
#define SIZE(p) ((size_t *) (p))
|
||||
#define CCHAR(p) ((const char **) (p))
|
||||
#define TYPE(p) CCHAR(UINT8(p) + sizeof (size_t))
|
||||
|
||||
void initAlloc(void)
|
||||
{
|
||||
allocations = g_ptr_array_new();
|
||||
}
|
||||
|
||||
static void uninitComplain(gpointer ptr, gpointer data)
|
||||
{
|
||||
char **str = (char **) data;
|
||||
char *str2;
|
||||
|
||||
if (*str == NULL)
|
||||
*str = g_strdup_printf("");
|
||||
str2 = g_strdup_printf("%s%p %s\n", *str, ptr, *TYPE(ptr));
|
||||
g_free(*str);
|
||||
*str = str2;
|
||||
}
|
||||
|
||||
void uninitAlloc(void)
|
||||
{
|
||||
char *str = NULL;
|
||||
|
||||
if (allocations->len == 0) {
|
||||
g_ptr_array_free(allocations, TRUE);
|
||||
return;
|
||||
}
|
||||
g_ptr_array_foreach(allocations, uninitComplain, &str);
|
||||
userbug("Some data was leaked; either you left a uiControl lying around or there's a bug in libui itself. Leaked data:\n%s", str);
|
||||
g_free(str);
|
||||
}
|
||||
|
||||
void *uiAlloc(size_t size, const char *type)
|
||||
{
|
||||
void *out;
|
||||
|
||||
out = g_malloc0(EXTRA + size);
|
||||
*SIZE(out) = size;
|
||||
*TYPE(out) = type;
|
||||
g_ptr_array_add(allocations, out);
|
||||
return DATA(out);
|
||||
}
|
||||
|
||||
void *uiRealloc(void *p, size_t new, const char *type)
|
||||
{
|
||||
void *out;
|
||||
size_t *s;
|
||||
|
||||
if (p == NULL)
|
||||
return uiAlloc(new, type);
|
||||
p = BASE(p);
|
||||
out = g_realloc(p, EXTRA + new);
|
||||
s = SIZE(out);
|
||||
if (new <= *s)
|
||||
memset(((uint8_t *) DATA(out)) + *s, 0, new - *s);
|
||||
*s = new;
|
||||
if (g_ptr_array_remove(allocations, p) == FALSE)
|
||||
implbug("%p not found in allocations array in uiRealloc()", p);
|
||||
g_ptr_array_add(allocations, out);
|
||||
return DATA(out);
|
||||
}
|
||||
|
||||
void uiFree(void *p)
|
||||
{
|
||||
if (p == NULL)
|
||||
implbug("attempt to uiFree(NULL)");
|
||||
p = BASE(p);
|
||||
g_free(p);
|
||||
if (g_ptr_array_remove(allocations, p) == FALSE)
|
||||
implbug("%p not found in allocations array in uiFree()", p);
|
||||
}
|
||||
Vendored
+633
@@ -0,0 +1,633 @@
|
||||
// 4 september 2015
|
||||
#include "uipriv_unix.h"
|
||||
|
||||
// notes:
|
||||
// - G_DECLARE_DERIVABLE/FINAL_INTERFACE() requires glib 2.44 and that's starting with debian stretch (testing) (GTK+ 3.18) and ubuntu 15.04 (GTK+ 3.14) - debian jessie has 2.42 (GTK+ 3.14)
|
||||
#define areaWidgetType (areaWidget_get_type())
|
||||
#define areaWidget(obj) (G_TYPE_CHECK_INSTANCE_CAST((obj), areaWidgetType, areaWidget))
|
||||
#define isAreaWidget(obj) (G_TYPE_CHECK_INSTANCE_TYPE((obj), areaWidgetType))
|
||||
#define areaWidgetClass(class) (G_TYPE_CHECK_CLASS_CAST((class), areaWidgetType, areaWidgetClass))
|
||||
#define isAreaWidgetClass(class) (G_TYPE_CHECK_CLASS_TYPE((class), areaWidget))
|
||||
#define getAreaWidgetClass(obj) (G_TYPE_INSTANCE_GET_CLASS((obj), areaWidgetType, areaWidgetClass))
|
||||
|
||||
typedef struct areaWidget areaWidget;
|
||||
typedef struct areaWidgetClass areaWidgetClass;
|
||||
|
||||
struct areaWidget {
|
||||
GtkDrawingArea parent_instance;
|
||||
uiArea *a;
|
||||
// construct-only parameters aare not set until after the init() function has returned
|
||||
// we need this particular object available during init(), so put it here instead of in uiArea
|
||||
// keep a pointer in uiArea for convenience, though
|
||||
clickCounter cc;
|
||||
};
|
||||
|
||||
struct areaWidgetClass {
|
||||
GtkDrawingAreaClass parent_class;
|
||||
};
|
||||
|
||||
struct uiArea {
|
||||
uiUnixControl c;
|
||||
GtkWidget *widget; // either swidget or areaWidget depending on whether it is scrolling
|
||||
|
||||
GtkWidget *swidget;
|
||||
GtkContainer *scontainer;
|
||||
GtkScrolledWindow *sw;
|
||||
|
||||
GtkWidget *areaWidget;
|
||||
GtkDrawingArea *drawingArea;
|
||||
areaWidget *area;
|
||||
|
||||
uiAreaHandler *ah;
|
||||
|
||||
gboolean scrolling;
|
||||
int scrollWidth;
|
||||
int scrollHeight;
|
||||
|
||||
// note that this is a pointer; see above
|
||||
clickCounter *cc;
|
||||
|
||||
// for user window drags
|
||||
GdkEventButton *dragevent;
|
||||
};
|
||||
|
||||
G_DEFINE_TYPE(areaWidget, areaWidget, GTK_TYPE_DRAWING_AREA)
|
||||
|
||||
static void areaWidget_init(areaWidget *aw)
|
||||
{
|
||||
// for events
|
||||
gtk_widget_add_events(GTK_WIDGET(aw),
|
||||
GDK_POINTER_MOTION_MASK |
|
||||
GDK_BUTTON_MOTION_MASK |
|
||||
GDK_BUTTON_PRESS_MASK |
|
||||
GDK_BUTTON_RELEASE_MASK |
|
||||
GDK_KEY_PRESS_MASK |
|
||||
GDK_KEY_RELEASE_MASK |
|
||||
GDK_ENTER_NOTIFY_MASK |
|
||||
GDK_LEAVE_NOTIFY_MASK);
|
||||
|
||||
gtk_widget_set_can_focus(GTK_WIDGET(aw), TRUE);
|
||||
|
||||
clickCounterReset(&(aw->cc));
|
||||
}
|
||||
|
||||
static void areaWidget_dispose(GObject *obj)
|
||||
{
|
||||
G_OBJECT_CLASS(areaWidget_parent_class)->dispose(obj);
|
||||
}
|
||||
|
||||
static void areaWidget_finalize(GObject *obj)
|
||||
{
|
||||
G_OBJECT_CLASS(areaWidget_parent_class)->finalize(obj);
|
||||
}
|
||||
|
||||
static void areaWidget_size_allocate(GtkWidget *w, GtkAllocation *allocation)
|
||||
{
|
||||
areaWidget *aw = areaWidget(w);
|
||||
uiArea *a = aw->a;
|
||||
|
||||
// GtkDrawingArea has a size_allocate() implementation; we need to call it
|
||||
// this will call gtk_widget_set_allocation() for us
|
||||
GTK_WIDGET_CLASS(areaWidget_parent_class)->size_allocate(w, allocation);
|
||||
|
||||
if (!a->scrolling)
|
||||
// we must redraw everything on resize because Windows requires it
|
||||
gtk_widget_queue_resize(w);
|
||||
}
|
||||
|
||||
static void loadAreaSize(uiArea *a, double *width, double *height)
|
||||
{
|
||||
GtkAllocation allocation;
|
||||
|
||||
*width = 0;
|
||||
*height = 0;
|
||||
// don't provide size information for scrolling areas
|
||||
if (!a->scrolling) {
|
||||
gtk_widget_get_allocation(a->areaWidget, &allocation);
|
||||
// these are already in drawing space coordinates
|
||||
// for drawing, the size of drawing space has the same value as the widget allocation
|
||||
// thanks to tristan in irc.gimp.net/#gtk+
|
||||
*width = allocation.width;
|
||||
*height = allocation.height;
|
||||
}
|
||||
}
|
||||
|
||||
static gboolean areaWidget_draw(GtkWidget *w, cairo_t *cr)
|
||||
{
|
||||
areaWidget *aw = areaWidget(w);
|
||||
uiArea *a = aw->a;
|
||||
uiAreaDrawParams dp;
|
||||
double clipX0, clipY0, clipX1, clipY1;
|
||||
|
||||
dp.Context = newContext(cr);
|
||||
|
||||
loadAreaSize(a, &(dp.AreaWidth), &(dp.AreaHeight));
|
||||
|
||||
cairo_clip_extents(cr, &clipX0, &clipY0, &clipX1, &clipY1);
|
||||
dp.ClipX = clipX0;
|
||||
dp.ClipY = clipY0;
|
||||
dp.ClipWidth = clipX1 - clipX0;
|
||||
dp.ClipHeight = clipY1 - clipY0;
|
||||
|
||||
// no need to save or restore the graphics state to reset transformations; GTK+ does that for us
|
||||
(*(a->ah->Draw))(a->ah, a, &dp);
|
||||
|
||||
freeContext(dp.Context);
|
||||
return FALSE;
|
||||
}
|
||||
|
||||
// to do this properly for scrolling areas, we need to
|
||||
// - return the same value for min and nat
|
||||
// - call gtk_widget_queue_resize() when the size changes
|
||||
// thanks to Company in irc.gimp.net/#gtk+
|
||||
static void areaWidget_get_preferred_height(GtkWidget *w, gint *min, gint *nat)
|
||||
{
|
||||
areaWidget *aw = areaWidget(w);
|
||||
uiArea *a = aw->a;
|
||||
|
||||
// always chain up just in case
|
||||
GTK_WIDGET_CLASS(areaWidget_parent_class)->get_preferred_height(w, min, nat);
|
||||
if (a->scrolling) {
|
||||
*min = a->scrollHeight;
|
||||
*nat = a->scrollHeight;
|
||||
}
|
||||
}
|
||||
|
||||
static void areaWidget_get_preferred_width(GtkWidget *w, gint *min, gint *nat)
|
||||
{
|
||||
areaWidget *aw = areaWidget(w);
|
||||
uiArea *a = aw->a;
|
||||
|
||||
// always chain up just in case
|
||||
GTK_WIDGET_CLASS(areaWidget_parent_class)->get_preferred_width(w, min, nat);
|
||||
if (a->scrolling) {
|
||||
*min = a->scrollWidth;
|
||||
*nat = a->scrollWidth;
|
||||
}
|
||||
}
|
||||
|
||||
static guint translateModifiers(guint state, GdkWindow *window)
|
||||
{
|
||||
GdkModifierType statetype;
|
||||
|
||||
// GDK doesn't initialize the modifier flags fully; we have to explicitly tell it to (thanks to Daniel_S and daniels (two different people) in irc.gimp.net/#gtk+)
|
||||
statetype = state;
|
||||
gdk_keymap_add_virtual_modifiers(
|
||||
gdk_keymap_get_for_display(gdk_window_get_display(window)),
|
||||
&statetype);
|
||||
return statetype;
|
||||
}
|
||||
|
||||
static uiModifiers toModifiers(guint state)
|
||||
{
|
||||
uiModifiers m;
|
||||
|
||||
m = 0;
|
||||
if ((state & GDK_CONTROL_MASK) != 0)
|
||||
m |= uiModifierCtrl;
|
||||
if ((state & GDK_META_MASK) != 0)
|
||||
m |= uiModifierAlt;
|
||||
if ((state & GDK_MOD1_MASK) != 0) // GTK+ itself requires this to be Alt (just read through gtkaccelgroup.c)
|
||||
m |= uiModifierAlt;
|
||||
if ((state & GDK_SHIFT_MASK) != 0)
|
||||
m |= uiModifierShift;
|
||||
if ((state & GDK_SUPER_MASK) != 0)
|
||||
m |= uiModifierSuper;
|
||||
return m;
|
||||
}
|
||||
|
||||
// capture on drag is done automatically on GTK+
|
||||
static void finishMouseEvent(uiArea *a, uiAreaMouseEvent *me, guint mb, gdouble x, gdouble y, guint state, GdkWindow *window)
|
||||
{
|
||||
// on GTK+, mouse buttons 4-7 are for scrolling; if we got here, that's a mistake
|
||||
if (mb >= 4 && mb <= 7)
|
||||
return;
|
||||
// if the button ID >= 8, continue counting from 4, as in the MouseEvent spec
|
||||
if (me->Down >= 8)
|
||||
me->Down -= 4;
|
||||
if (me->Up >= 8)
|
||||
me->Up -= 4;
|
||||
|
||||
state = translateModifiers(state, window);
|
||||
me->Modifiers = toModifiers(state);
|
||||
|
||||
// the mb != # checks exclude the Up/Down button from Held
|
||||
me->Held1To64 = 0;
|
||||
if (mb != 1 && (state & GDK_BUTTON1_MASK) != 0)
|
||||
me->Held1To64 |= 1 << 0;
|
||||
if (mb != 2 && (state & GDK_BUTTON2_MASK) != 0)
|
||||
me->Held1To64 |= 1 << 1;
|
||||
if (mb != 3 && (state & GDK_BUTTON3_MASK) != 0)
|
||||
me->Held1To64 |= 1 << 2;
|
||||
// don't check GDK_BUTTON4_MASK or GDK_BUTTON5_MASK because those are for the scrolling buttons mentioned above
|
||||
// GDK expressly does not support any more buttons in the GdkModifierType; see https://git.gnome.org/browse/gtk+/tree/gdk/x11/gdkdevice-xi2.c#n763 (thanks mclasen in irc.gimp.net/#gtk+)
|
||||
|
||||
// these are already in drawing space coordinates
|
||||
// the size of drawing space has the same value as the widget allocation
|
||||
// thanks to tristan in irc.gimp.net/#gtk+
|
||||
me->X = x;
|
||||
me->Y = y;
|
||||
|
||||
loadAreaSize(a, &(me->AreaWidth), &(me->AreaHeight));
|
||||
|
||||
(*(a->ah->MouseEvent))(a->ah, a, me);
|
||||
}
|
||||
|
||||
static gboolean areaWidget_button_press_event(GtkWidget *w, GdkEventButton *e)
|
||||
{
|
||||
areaWidget *aw = areaWidget(w);
|
||||
uiArea *a = aw->a;
|
||||
gint maxTime, maxDistance;
|
||||
GtkSettings *settings;
|
||||
uiAreaMouseEvent me;
|
||||
|
||||
// clicking doesn't automatically transfer keyboard focus; we must do so manually (thanks tristan in irc.gimp.net/#gtk+)
|
||||
gtk_widget_grab_focus(w);
|
||||
|
||||
// we handle multiple clicks ourselves here, in the same way as we do on Windows
|
||||
if (e->type != GDK_BUTTON_PRESS)
|
||||
// ignore GDK's generated double-clicks and beyond
|
||||
return GDK_EVENT_PROPAGATE;
|
||||
settings = gtk_widget_get_settings(w);
|
||||
g_object_get(settings,
|
||||
"gtk-double-click-time", &maxTime,
|
||||
"gtk-double-click-distance", &maxDistance,
|
||||
NULL);
|
||||
// don't unref settings; it's transfer-none (thanks gregier in irc.gimp.net/#gtk+)
|
||||
// e->time is guint32
|
||||
// e->x and e->y are floating-point; just make them 32-bit integers
|
||||
// maxTime and maxDistance... are gint, which *should* fit, hopefully...
|
||||
me.Count = clickCounterClick(a->cc, me.Down,
|
||||
e->x, e->y,
|
||||
e->time, maxTime,
|
||||
maxDistance, maxDistance);
|
||||
|
||||
me.Down = e->button;
|
||||
me.Up = 0;
|
||||
|
||||
// and set things up for window drags
|
||||
a->dragevent = e;
|
||||
finishMouseEvent(a, &me, e->button, e->x, e->y, e->state, e->window);
|
||||
a->dragevent = NULL;
|
||||
return GDK_EVENT_PROPAGATE;
|
||||
}
|
||||
|
||||
static gboolean areaWidget_button_release_event(GtkWidget *w, GdkEventButton *e)
|
||||
{
|
||||
areaWidget *aw = areaWidget(w);
|
||||
uiArea *a = aw->a;
|
||||
uiAreaMouseEvent me;
|
||||
|
||||
me.Down = 0;
|
||||
me.Up = e->button;
|
||||
me.Count = 0;
|
||||
finishMouseEvent(a, &me, e->button, e->x, e->y, e->state, e->window);
|
||||
return GDK_EVENT_PROPAGATE;
|
||||
}
|
||||
|
||||
static gboolean areaWidget_motion_notify_event(GtkWidget *w, GdkEventMotion *e)
|
||||
{
|
||||
areaWidget *aw = areaWidget(w);
|
||||
uiArea *a = aw->a;
|
||||
uiAreaMouseEvent me;
|
||||
|
||||
me.Down = 0;
|
||||
me.Up = 0;
|
||||
me.Count = 0;
|
||||
finishMouseEvent(a, &me, 0, e->x, e->y, e->state, e->window);
|
||||
return GDK_EVENT_PROPAGATE;
|
||||
}
|
||||
|
||||
// we want switching away from the control to reset the double-click counter, like with WM_ACTIVATE on Windows
|
||||
// according to tristan in irc.gimp.net/#gtk+, doing this on both enter-notify-event and leave-notify-event is correct (and it seems to be true in my own tests; plus the events DO get sent when switching programs with the keyboard (just pointing that out))
|
||||
static gboolean onCrossing(areaWidget *aw, int left)
|
||||
{
|
||||
uiArea *a = aw->a;
|
||||
|
||||
(*(a->ah->MouseCrossed))(a->ah, a, left);
|
||||
clickCounterReset(a->cc);
|
||||
return GDK_EVENT_PROPAGATE;
|
||||
}
|
||||
|
||||
static gboolean areaWidget_enter_notify_event(GtkWidget *w, GdkEventCrossing *e)
|
||||
{
|
||||
return onCrossing(areaWidget(w), 0);
|
||||
}
|
||||
|
||||
static gboolean areaWidget_leave_notify_event(GtkWidget *w, GdkEventCrossing *e)
|
||||
{
|
||||
return onCrossing(areaWidget(w), 1);
|
||||
}
|
||||
|
||||
// note: there is no equivalent to WM_CAPTURECHANGED on GTK+; there literally is no way to break a grab like that (at least not on X11 and Wayland)
|
||||
// even if I invoke the task switcher and switch processes, the mouse grab will still be held until I let go of all buttons
|
||||
// therefore, no DragBroken()
|
||||
|
||||
// we use GDK_KEY_Print as a sentinel because libui will never support the print screen key; that key belongs to the user
|
||||
|
||||
static const struct {
|
||||
guint keyval;
|
||||
uiExtKey extkey;
|
||||
} extKeys[] = {
|
||||
{ GDK_KEY_Escape, uiExtKeyEscape },
|
||||
{ GDK_KEY_Insert, uiExtKeyInsert },
|
||||
{ GDK_KEY_Delete, uiExtKeyDelete },
|
||||
{ GDK_KEY_Home, uiExtKeyHome },
|
||||
{ GDK_KEY_End, uiExtKeyEnd },
|
||||
{ GDK_KEY_Page_Up, uiExtKeyPageUp },
|
||||
{ GDK_KEY_Page_Down, uiExtKeyPageDown },
|
||||
{ GDK_KEY_Up, uiExtKeyUp },
|
||||
{ GDK_KEY_Down, uiExtKeyDown },
|
||||
{ GDK_KEY_Left, uiExtKeyLeft },
|
||||
{ GDK_KEY_Right, uiExtKeyRight },
|
||||
{ GDK_KEY_F1, uiExtKeyF1 },
|
||||
{ GDK_KEY_F2, uiExtKeyF2 },
|
||||
{ GDK_KEY_F3, uiExtKeyF3 },
|
||||
{ GDK_KEY_F4, uiExtKeyF4 },
|
||||
{ GDK_KEY_F5, uiExtKeyF5 },
|
||||
{ GDK_KEY_F6, uiExtKeyF6 },
|
||||
{ GDK_KEY_F7, uiExtKeyF7 },
|
||||
{ GDK_KEY_F8, uiExtKeyF8 },
|
||||
{ GDK_KEY_F9, uiExtKeyF9 },
|
||||
{ GDK_KEY_F10, uiExtKeyF10 },
|
||||
{ GDK_KEY_F11, uiExtKeyF11 },
|
||||
{ GDK_KEY_F12, uiExtKeyF12 },
|
||||
// numpad numeric keys and . are handled in events.c
|
||||
{ GDK_KEY_KP_Enter, uiExtKeyNEnter },
|
||||
{ GDK_KEY_KP_Add, uiExtKeyNAdd },
|
||||
{ GDK_KEY_KP_Subtract, uiExtKeyNSubtract },
|
||||
{ GDK_KEY_KP_Multiply, uiExtKeyNMultiply },
|
||||
{ GDK_KEY_KP_Divide, uiExtKeyNDivide },
|
||||
{ GDK_KEY_Print, 0 },
|
||||
};
|
||||
|
||||
static const struct {
|
||||
guint keyval;
|
||||
uiModifiers mod;
|
||||
} modKeys[] = {
|
||||
{ GDK_KEY_Control_L, uiModifierCtrl },
|
||||
{ GDK_KEY_Control_R, uiModifierCtrl },
|
||||
{ GDK_KEY_Alt_L, uiModifierAlt },
|
||||
{ GDK_KEY_Alt_R, uiModifierAlt },
|
||||
{ GDK_KEY_Meta_L, uiModifierAlt },
|
||||
{ GDK_KEY_Meta_R, uiModifierAlt },
|
||||
{ GDK_KEY_Shift_L, uiModifierShift },
|
||||
{ GDK_KEY_Shift_R, uiModifierShift },
|
||||
{ GDK_KEY_Super_L, uiModifierSuper },
|
||||
{ GDK_KEY_Super_R, uiModifierSuper },
|
||||
{ GDK_KEY_Print, 0 },
|
||||
};
|
||||
|
||||
static int areaKeyEvent(uiArea *a, int up, GdkEventKey *e)
|
||||
{
|
||||
uiAreaKeyEvent ke;
|
||||
guint state;
|
||||
int i;
|
||||
|
||||
ke.Key = 0;
|
||||
ke.ExtKey = 0;
|
||||
ke.Modifier = 0;
|
||||
|
||||
state = translateModifiers(e->state, e->window);
|
||||
ke.Modifiers = toModifiers(state);
|
||||
|
||||
ke.Up = up;
|
||||
|
||||
for (i = 0; extKeys[i].keyval != GDK_KEY_Print; i++)
|
||||
if (extKeys[i].keyval == e->keyval) {
|
||||
ke.ExtKey = extKeys[i].extkey;
|
||||
goto keyFound;
|
||||
}
|
||||
|
||||
for (i = 0; modKeys[i].keyval != GDK_KEY_Print; i++)
|
||||
if (modKeys[i].keyval == e->keyval) {
|
||||
ke.Modifier = modKeys[i].mod;
|
||||
// don't include the modifier in ke.Modifiers
|
||||
ke.Modifiers &= ~ke.Modifier;
|
||||
goto keyFound;
|
||||
}
|
||||
|
||||
if (fromScancode(e->hardware_keycode - 8, &ke))
|
||||
goto keyFound;
|
||||
|
||||
// no supported key found; treat as unhandled
|
||||
return 0;
|
||||
|
||||
keyFound:
|
||||
return (*(a->ah->KeyEvent))(a->ah, a, &ke);
|
||||
}
|
||||
|
||||
static gboolean areaWidget_key_press_event(GtkWidget *w, GdkEventKey *e)
|
||||
{
|
||||
areaWidget *aw = areaWidget(w);
|
||||
uiArea *a = aw->a;
|
||||
|
||||
if (areaKeyEvent(a, 0, e))
|
||||
return GDK_EVENT_STOP;
|
||||
return GDK_EVENT_PROPAGATE;
|
||||
}
|
||||
|
||||
static gboolean areaWidget_key_release_event(GtkWidget *w, GdkEventKey *e)
|
||||
{
|
||||
areaWidget *aw = areaWidget(w);
|
||||
uiArea *a = aw->a;
|
||||
|
||||
if (areaKeyEvent(a, 1, e))
|
||||
return GDK_EVENT_STOP;
|
||||
return GDK_EVENT_PROPAGATE;
|
||||
}
|
||||
|
||||
enum {
|
||||
pArea = 1,
|
||||
nProps,
|
||||
};
|
||||
|
||||
static GParamSpec *pspecArea;
|
||||
|
||||
static void areaWidget_set_property(GObject *obj, guint prop, const GValue *value, GParamSpec *pspec)
|
||||
{
|
||||
areaWidget *aw = areaWidget(obj);
|
||||
|
||||
switch (prop) {
|
||||
case pArea:
|
||||
aw->a = (uiArea *) g_value_get_pointer(value);
|
||||
aw->a->cc = &(aw->cc);
|
||||
return;
|
||||
}
|
||||
G_OBJECT_WARN_INVALID_PROPERTY_ID(obj, prop, pspec);
|
||||
}
|
||||
|
||||
static void areaWidget_get_property(GObject *obj, guint prop, GValue *value, GParamSpec *pspec)
|
||||
{
|
||||
G_OBJECT_WARN_INVALID_PROPERTY_ID(obj, prop, pspec);
|
||||
}
|
||||
|
||||
static void areaWidget_class_init(areaWidgetClass *class)
|
||||
{
|
||||
G_OBJECT_CLASS(class)->dispose = areaWidget_dispose;
|
||||
G_OBJECT_CLASS(class)->finalize = areaWidget_finalize;
|
||||
G_OBJECT_CLASS(class)->set_property = areaWidget_set_property;
|
||||
G_OBJECT_CLASS(class)->get_property = areaWidget_get_property;
|
||||
|
||||
GTK_WIDGET_CLASS(class)->size_allocate = areaWidget_size_allocate;
|
||||
GTK_WIDGET_CLASS(class)->draw = areaWidget_draw;
|
||||
GTK_WIDGET_CLASS(class)->get_preferred_height = areaWidget_get_preferred_height;
|
||||
GTK_WIDGET_CLASS(class)->get_preferred_width = areaWidget_get_preferred_width;
|
||||
GTK_WIDGET_CLASS(class)->button_press_event = areaWidget_button_press_event;
|
||||
GTK_WIDGET_CLASS(class)->button_release_event = areaWidget_button_release_event;
|
||||
GTK_WIDGET_CLASS(class)->motion_notify_event = areaWidget_motion_notify_event;
|
||||
GTK_WIDGET_CLASS(class)->enter_notify_event = areaWidget_enter_notify_event;
|
||||
GTK_WIDGET_CLASS(class)->leave_notify_event = areaWidget_leave_notify_event;
|
||||
GTK_WIDGET_CLASS(class)->key_press_event = areaWidget_key_press_event;
|
||||
GTK_WIDGET_CLASS(class)->key_release_event = areaWidget_key_release_event;
|
||||
|
||||
pspecArea = g_param_spec_pointer("libui-area",
|
||||
"libui-area",
|
||||
"uiArea.",
|
||||
G_PARAM_WRITABLE | G_PARAM_CONSTRUCT_ONLY | G_PARAM_STATIC_STRINGS);
|
||||
g_object_class_install_property(G_OBJECT_CLASS(class), pArea, pspecArea);
|
||||
}
|
||||
|
||||
// control implementation
|
||||
|
||||
uiUnixControlAllDefaults(uiArea)
|
||||
|
||||
void uiAreaSetSize(uiArea *a, int width, int height)
|
||||
{
|
||||
if (!a->scrolling)
|
||||
userbug("You cannot call uiAreaSetSize() on a non-scrolling uiArea. (area: %p)", a);
|
||||
a->scrollWidth = width;
|
||||
a->scrollHeight = height;
|
||||
gtk_widget_queue_resize(a->areaWidget);
|
||||
}
|
||||
|
||||
void uiAreaQueueRedrawAll(uiArea *a)
|
||||
{
|
||||
gtk_widget_queue_draw(a->areaWidget);
|
||||
}
|
||||
|
||||
void uiAreaScrollTo(uiArea *a, double x, double y, double width, double height)
|
||||
{
|
||||
// TODO
|
||||
// TODO adjust adjustments and find source for that
|
||||
}
|
||||
|
||||
void uiAreaBeginUserWindowMove(uiArea *a)
|
||||
{
|
||||
GtkWidget *toplevel;
|
||||
|
||||
if (a->dragevent == NULL)
|
||||
userbug("cannot call uiAreaBeginUserWindowMove() outside of a Mouse() with Down != 0");
|
||||
// TODO don't we have a libui function for this? did I scrap it?
|
||||
// TODO widget or areaWidget?
|
||||
toplevel = gtk_widget_get_toplevel(a->widget);
|
||||
if (toplevel == NULL) {
|
||||
// TODO
|
||||
return;
|
||||
}
|
||||
// the docs say to do this
|
||||
if (!gtk_widget_is_toplevel(toplevel)) {
|
||||
// TODO
|
||||
return;
|
||||
}
|
||||
if (!GTK_IS_WINDOW(toplevel)) {
|
||||
// TODO
|
||||
return;
|
||||
}
|
||||
gtk_window_begin_move_drag(GTK_WINDOW(toplevel),
|
||||
a->dragevent->button,
|
||||
a->dragevent->x_root, // TODO are these correct?
|
||||
a->dragevent->y_root,
|
||||
a->dragevent->time);
|
||||
}
|
||||
|
||||
static const GdkWindowEdge edges[] = {
|
||||
[uiWindowResizeEdgeLeft] = GDK_WINDOW_EDGE_WEST,
|
||||
[uiWindowResizeEdgeTop] = GDK_WINDOW_EDGE_NORTH,
|
||||
[uiWindowResizeEdgeRight] = GDK_WINDOW_EDGE_EAST,
|
||||
[uiWindowResizeEdgeBottom] = GDK_WINDOW_EDGE_SOUTH,
|
||||
[uiWindowResizeEdgeTopLeft] = GDK_WINDOW_EDGE_NORTH_WEST,
|
||||
[uiWindowResizeEdgeTopRight] = GDK_WINDOW_EDGE_NORTH_EAST,
|
||||
[uiWindowResizeEdgeBottomLeft] = GDK_WINDOW_EDGE_SOUTH_WEST,
|
||||
[uiWindowResizeEdgeBottomRight] = GDK_WINDOW_EDGE_SOUTH_EAST,
|
||||
};
|
||||
|
||||
void uiAreaBeginUserWindowResize(uiArea *a, uiWindowResizeEdge edge)
|
||||
{
|
||||
GtkWidget *toplevel;
|
||||
|
||||
if (a->dragevent == NULL)
|
||||
userbug("cannot call uiAreaBeginUserWindowResize() outside of a Mouse() with Down != 0");
|
||||
// TODO don't we have a libui function for this? did I scrap it?
|
||||
// TODO widget or areaWidget?
|
||||
toplevel = gtk_widget_get_toplevel(a->widget);
|
||||
if (toplevel == NULL) {
|
||||
// TODO
|
||||
return;
|
||||
}
|
||||
// the docs say to do this
|
||||
if (!gtk_widget_is_toplevel(toplevel)) {
|
||||
// TODO
|
||||
return;
|
||||
}
|
||||
if (!GTK_IS_WINDOW(toplevel)) {
|
||||
// TODO
|
||||
return;
|
||||
}
|
||||
gtk_window_begin_resize_drag(GTK_WINDOW(toplevel),
|
||||
edges[edge],
|
||||
a->dragevent->button,
|
||||
a->dragevent->x_root, // TODO are these correct?
|
||||
a->dragevent->y_root,
|
||||
a->dragevent->time);
|
||||
}
|
||||
|
||||
uiArea *uiNewArea(uiAreaHandler *ah)
|
||||
{
|
||||
uiArea *a;
|
||||
|
||||
uiUnixNewControl(uiArea, a);
|
||||
|
||||
a->ah = ah;
|
||||
a->scrolling = FALSE;
|
||||
|
||||
a->areaWidget = GTK_WIDGET(g_object_new(areaWidgetType,
|
||||
"libui-area", a,
|
||||
NULL));
|
||||
a->drawingArea = GTK_DRAWING_AREA(a->areaWidget);
|
||||
a->area = areaWidget(a->areaWidget);
|
||||
|
||||
a->widget = a->areaWidget;
|
||||
|
||||
return a;
|
||||
}
|
||||
|
||||
uiArea *uiNewScrollingArea(uiAreaHandler *ah, int width, int height)
|
||||
{
|
||||
uiArea *a;
|
||||
|
||||
uiUnixNewControl(uiArea, a);
|
||||
|
||||
a->ah = ah;
|
||||
a->scrolling = TRUE;
|
||||
a->scrollWidth = width;
|
||||
a->scrollHeight = height;
|
||||
|
||||
a->swidget = gtk_scrolled_window_new(NULL, NULL);
|
||||
a->scontainer = GTK_CONTAINER(a->swidget);
|
||||
a->sw = GTK_SCROLLED_WINDOW(a->swidget);
|
||||
|
||||
a->areaWidget = GTK_WIDGET(g_object_new(areaWidgetType,
|
||||
"libui-area", a,
|
||||
NULL));
|
||||
a->drawingArea = GTK_DRAWING_AREA(a->areaWidget);
|
||||
a->area = areaWidget(a->areaWidget);
|
||||
|
||||
a->widget = a->swidget;
|
||||
|
||||
gtk_container_add(a->scontainer, a->areaWidget);
|
||||
// and make the area visible; only the scrolled window's visibility is controlled by libui
|
||||
gtk_widget_show(a->areaWidget);
|
||||
|
||||
return a;
|
||||
}
|
||||
Loaded 100 of 604 files, more files were not shown because too many files have changed in this diff.
Show more
Reference in new issue
Block a user