mirror of
https://github.com/cappuccino/cappuccino.git
synced 2026-09-09 12:17:13 +00:00
Compare commits
1122
Commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
675f32b10f | ||
|
|
cbb7cc93e9 | ||
|
|
8b608c5cce | ||
|
|
163fb14d0e | ||
|
|
e4f87bd805 | ||
|
|
864c7d96f1 | ||
|
|
e35e87fb05 | ||
|
|
3246bdbc8b | ||
|
|
710bb5fe1c | ||
|
|
53a52da709 | ||
|
|
4afbfc6e57 | ||
|
|
f7aa9efe13 | ||
|
|
1fce0ee551 | ||
|
|
ee695f9000 | ||
|
|
8f99ebb577 | ||
|
|
5bfafaf241 | ||
|
|
64e1a2ce4b | ||
|
|
27883c5beb | ||
|
|
2772725fe3 | ||
|
|
3f58a64827 | ||
|
|
cb37c7d218 | ||
|
|
45d951071f | ||
|
|
2ac60684fa | ||
|
|
af71742110 | ||
|
|
1adf27427a | ||
|
|
082f78eefa | ||
|
|
a8a9ec0023 | ||
|
|
df4f945bc6 | ||
|
|
ad756a5b86 | ||
|
|
2ca99ba669 | ||
|
|
6443575114 | ||
|
|
52a51d81f1 | ||
|
|
7d20b06e52 | ||
|
|
ad9d8a7200 | ||
|
|
44da699343 | ||
|
|
0a542f9be9 | ||
|
|
8f1601ca55 | ||
|
|
ff0ce164da | ||
|
|
3650c38aa9 | ||
|
|
61d97338e2 | ||
|
|
e27bb4de50 | ||
|
|
4462d8bf22 | ||
|
|
f23a8ac928 | ||
|
|
9e1750ee13 | ||
|
|
5cd284cb36 | ||
|
|
5ea58430fe | ||
|
|
acfbda0d91 | ||
|
|
437e5a62f1 | ||
|
|
bbb5aea77d | ||
|
|
b5d04f84e7 | ||
|
|
93b12eb14f | ||
|
|
0aa67c4cf2 | ||
|
|
1c1affac5c | ||
|
|
0919797edc | ||
|
|
94ac8fd837 | ||
|
|
ecc82b5b51 | ||
|
|
add021fd67 | ||
|
|
2daf309d7d | ||
|
|
20be4a40ea | ||
|
|
7de91da87a | ||
|
|
b8b48b533b | ||
|
|
535f981739 | ||
|
|
0404ac0395 | ||
|
|
f961625f03 | ||
|
|
7909bae065 | ||
|
|
7e6e117b17 | ||
|
|
314e64d7b1 | ||
|
|
77eb6b358f | ||
|
|
25f86e2542 | ||
|
|
8bc0461463 | ||
|
|
c9dd1ea71d | ||
|
|
85d3529bc3 | ||
|
|
3aac8bb072 | ||
|
|
e9a7e21aca | ||
|
|
fc0e4da33c | ||
|
|
ef3c1315dd | ||
|
|
5c12890faf | ||
|
|
c4fd5e0999 | ||
|
|
d3c56d635d | ||
|
|
c53cb49034 | ||
|
|
d38463c5d9 | ||
|
|
bc4b424f0e | ||
|
|
6bd4f3b7b4 | ||
|
|
d91d83f115 | ||
|
|
584b6ab219 | ||
|
|
04b155dbfb | ||
|
|
ddc5488db7 | ||
|
|
5c6906d5fd | ||
|
|
56fee5d286 | ||
|
|
c4dcab04b6 | ||
|
|
8dd9a96769 | ||
|
|
6864015af6 | ||
|
|
ebaa60e929 | ||
|
|
ac9962c624 | ||
|
|
ccd1773643 | ||
|
|
ac970ff768 | ||
|
|
53ff45425d | ||
|
|
de435c741f | ||
|
|
1dc9a44aec | ||
|
|
0206379315 | ||
|
|
c39adbbbc5 | ||
|
|
061a15d4f7 | ||
|
|
7c2518f027 | ||
|
|
a07459454b | ||
|
|
752f9d2694 | ||
|
|
0a180f9e20 | ||
|
|
6fff2e1c2f | ||
|
|
4266900d2e | ||
|
|
a2a1d84bc0 | ||
|
|
7648c562f1 | ||
|
|
403fd30926 | ||
|
|
17ccddf541 | ||
|
|
c24366fcec | ||
|
|
9a258a6265 | ||
|
|
4542ccbdc2 | ||
|
|
41d8ad2b86 | ||
|
|
a3a2ee118e | ||
|
|
f285efc277 | ||
|
|
fbaf87ec6c | ||
|
|
a005a468a7 | ||
|
|
c8d01549ba | ||
|
|
23af4505dd | ||
|
|
53cb6a5b3e | ||
|
|
765384ace1 | ||
|
|
d39e3e2f69 | ||
|
|
d75124066e | ||
|
|
f4400876da | ||
|
|
c222c5127f | ||
|
|
90d04956e6 | ||
|
|
5d3b918339 | ||
|
|
eba0091764 | ||
|
|
7999cbbea3 | ||
|
|
cee7987b35 | ||
|
|
d475c08024 | ||
|
|
d78f83ae49 | ||
|
|
df694f01e7 | ||
|
|
f5d9cbb11b | ||
|
|
d802588abe | ||
|
|
927d6d2fc6 | ||
|
|
bab7595078 | ||
|
|
1ea9a6c57f | ||
|
|
f479d415d8 | ||
|
|
f41e7be726 | ||
|
|
c1fe28124f | ||
|
|
01a1e506e4 | ||
|
|
e61aebb55d | ||
|
|
bae5bafc62 | ||
|
|
be6cb266de | ||
|
|
6f72edc2d9 | ||
|
|
29f66bdfed | ||
|
|
c1c6f5682c | ||
|
|
a8a74a0246 | ||
|
|
f9b867c3eb | ||
|
|
6c07f9a048 | ||
|
|
573e0acafa | ||
|
|
530c664967 | ||
|
|
12c29c8387 | ||
|
|
6c7f61d90f | ||
|
|
eb6d8d0bb3 | ||
|
|
ef462973c7 | ||
|
|
aa7e49cfde | ||
|
|
186048676a | ||
|
|
0f40721cff | ||
|
|
3799dd92fd | ||
|
|
9ea1a2b1de | ||
|
|
d8b27d122e | ||
|
|
15c3a6eae4 | ||
|
|
4fcc90d53e | ||
|
|
2619a50976 | ||
|
|
de116c95d4 | ||
|
|
47594c9b07 | ||
|
|
1b9098b564 | ||
|
|
b0306eac87 | ||
|
|
125e813d25 | ||
|
|
8e0d4a70f2 | ||
|
|
0556fc68b0 | ||
|
|
e9e2313159 | ||
|
|
dbb2f03a43 | ||
|
|
ed0dc2d887 | ||
|
|
0a93f2d0b0 | ||
|
|
fd581cd65c | ||
|
|
36702bc971 | ||
|
|
0fc3eb38f1 | ||
|
|
73c8e945e9 | ||
|
|
b8420303ba | ||
|
|
532bfcf937 | ||
|
|
f6d892023e | ||
|
|
7f2977f4af | ||
|
|
e246a2753b | ||
|
|
6d06a26c31 | ||
|
|
7cda21b60f | ||
|
|
1db740039b | ||
|
|
df462774aa | ||
|
|
f55fa789c4 | ||
|
|
488541f26e | ||
|
|
b404b1cf50 | ||
|
|
69370aa806 | ||
|
|
b3ddd85b09 | ||
|
|
033221fd01 | ||
|
|
1201fa38a8 | ||
|
|
32059c376e | ||
|
|
511d393c78 | ||
|
|
237601cf29 | ||
|
|
928f14872e | ||
|
|
c6a9adf96c | ||
|
|
cdd00e0bd4 | ||
|
|
2503d13402 | ||
|
|
2d97970d24 | ||
|
|
d8945e53ba | ||
|
|
f18012d07b | ||
|
|
104f8603b9 | ||
|
|
19bb083030 | ||
|
|
ae575a19a0 | ||
|
|
5f94d0a5c2 | ||
|
|
f2fe1a7c17 | ||
|
|
618e6a1604 | ||
|
|
cef294513a | ||
|
|
8550abadef | ||
|
|
25c8b2d631 | ||
|
|
e7b690205e | ||
|
|
2403f8833a | ||
|
|
9f25743b4d | ||
|
|
f1c76f65cf | ||
|
|
7b39533410 | ||
|
|
1fe9c39dd5 | ||
|
|
2b7be2e3f1 | ||
|
|
1ee9a3f279 | ||
|
|
679acfb239 | ||
|
|
3c7ab4b49b | ||
|
|
3f062e1c0a | ||
|
|
0a497d8c3f | ||
|
|
462351f485 | ||
|
|
c4cbb631a9 | ||
|
|
76f2d15b1b | ||
|
|
5d63865ae5 | ||
|
|
ad7c88fb45 | ||
|
|
1af3877758 | ||
|
|
146ce3b12b | ||
|
|
bf67721ac4 | ||
|
|
436f736a8e | ||
|
|
1446e90dd9 | ||
|
|
54716c03d1 | ||
|
|
fad5e97e0a | ||
|
|
8cf79590c5 | ||
|
|
c6aafac6c8 | ||
|
|
94a5995197 | ||
|
|
74081dc981 | ||
|
|
7a593b13a7 | ||
|
|
25dc38b2b1 | ||
|
|
403b12f7b7 | ||
|
|
cd223be88a | ||
|
|
a6a3fca1e7 | ||
|
|
63815eb5c2 | ||
|
|
4444264830 | ||
|
|
255f91f7ca | ||
|
|
700207d29b | ||
|
|
72d96e9a3b | ||
|
|
a55b6de1b4 | ||
|
|
9faac05666 | ||
|
|
7bf408aba0 | ||
|
|
70ee391b33 | ||
|
|
f5eb1158db | ||
|
|
81916d0f45 | ||
|
|
76d77cc244 | ||
|
|
03dccbe613 | ||
|
|
1ccec65619 | ||
|
|
b78e9062a1 | ||
|
|
dc87354702 | ||
|
|
033e8583db | ||
|
|
d46b51fa9a | ||
|
|
dd1dfdcfeb | ||
|
|
c0233c803d | ||
|
|
1fa8c88f6b | ||
|
|
195b109a36 | ||
|
|
994ac3fc8e | ||
|
|
a8ec5cf5e9 | ||
|
|
b4263aa3ec | ||
|
|
6456809a72 | ||
|
|
024c0a3186 | ||
|
|
560b1d48bc | ||
|
|
bb2d860807 | ||
|
|
6f1c0fa4a8 | ||
|
|
35808e7bde | ||
|
|
1b4eeb35b3 | ||
|
|
cee032bbc9 | ||
|
|
9547b70743 | ||
|
|
5df8939d84 | ||
|
|
0b9c689273 | ||
|
|
ac193cd97a | ||
|
|
7355a60f16 | ||
|
|
fb6f6325b5 | ||
|
|
0f767d9dfc | ||
|
|
d1addcab91 | ||
|
|
7a02cd794b | ||
|
|
258cd9209f | ||
|
|
700f6022d9 | ||
|
|
a0f6b815e8 | ||
|
|
54477b8b8f | ||
|
|
6183f8d4e9 | ||
|
|
d98e247665 | ||
|
|
d0cd976246 | ||
|
|
15f6f6a90a | ||
|
|
f18074bf49 | ||
|
|
3f64b74453 | ||
|
|
1c86256f83 | ||
|
|
98105ad4dc | ||
|
|
6bf2d6c1ab | ||
|
|
cebd51aac1 | ||
|
|
42b8b3aa75 | ||
|
|
c0eaf79d25 | ||
|
|
fb2fe3eb63 | ||
|
|
184d63f7eb | ||
|
|
14ebd58c96 | ||
|
|
3edddb8e91 | ||
|
|
d2340dff1a | ||
|
|
d30c6aec7d | ||
|
|
4854b3e08f | ||
|
|
facc748090 | ||
|
|
5c71612e34 | ||
|
|
f00a5fbaf4 | ||
|
|
34769da9cc | ||
|
|
458e6ed531 | ||
|
|
05814e9351 | ||
|
|
b6c0b1b396 | ||
|
|
1a299d85b6 | ||
|
|
1816274ebe | ||
|
|
b02e4c5308 | ||
|
|
12ed42e163 | ||
|
|
6c9c135062 | ||
|
|
6aea0160df | ||
|
|
30edd9e8dd | ||
|
|
7734402e8e | ||
|
|
ace1332ca5 | ||
|
|
baa313e8f8 | ||
|
|
3d9aa24f3c | ||
|
|
c287d457d6 | ||
|
|
7537b27a58 | ||
|
|
1f560c92fc | ||
|
|
74cd11c763 | ||
|
|
ee7d89331b | ||
|
|
5b51077823 | ||
|
|
d2f94d1945 | ||
|
|
0170a099bb | ||
|
|
2b893d0ea1 | ||
|
|
a08da57cbb | ||
|
|
a313fe5a86 | ||
|
|
544de699f6 | ||
|
|
d9d35b7f50 | ||
|
|
7dbec00f0e | ||
|
|
51b9b40c7e | ||
|
|
b2383a084a | ||
|
|
61848c5e24 | ||
|
|
60b2f7d243 | ||
|
|
d2a5e126ae | ||
|
|
9d7d8ddde9 | ||
|
|
249ebeb4aa | ||
|
|
378c14d790 | ||
|
|
30c1093c2f | ||
|
|
b966c8cb0b | ||
|
|
38cddf029b | ||
|
|
6652a27781 | ||
|
|
08b067cfc5 | ||
|
|
4959b84b4c | ||
|
|
d42562fdcb | ||
|
|
1e0aacd811 | ||
|
|
329469fe84 | ||
|
|
93b0c5a7e5 | ||
|
|
9e456fd315 | ||
|
|
8203c71b03 | ||
|
|
80d1e9a9a7 | ||
|
|
3c27e57b19 | ||
|
|
b46b2f048d | ||
|
|
8d403f579c | ||
|
|
12b274ab43 | ||
|
|
986cccb800 | ||
|
|
5da8728d6c | ||
|
|
66c9a1e90a | ||
|
|
a1925e1d0b | ||
|
|
da0910b1e5 | ||
|
|
8dbbe34dfc | ||
|
|
947d552509 | ||
|
|
037bc4ea2b | ||
|
|
cfbef141d8 | ||
|
|
410ad0dc09 | ||
|
|
3d9b42f0c2 | ||
|
|
eeea21c882 | ||
|
|
d94e5480c9 | ||
|
|
a5c8e87e6d | ||
|
|
08f74201b6 | ||
|
|
3a25c683b6 | ||
|
|
fb43f8bf1a | ||
|
|
bf082600c3 | ||
|
|
7596c66703 | ||
|
|
e1bcf6c47e | ||
|
|
ef1d6c78ac | ||
|
|
f36406af08 | ||
|
|
4b7d215d9d | ||
|
|
e09f12dc0b | ||
|
|
efa7406741 | ||
|
|
5a2181c064 | ||
|
|
dda81b12da | ||
|
|
c4bf67d133 | ||
|
|
f355c8679d | ||
|
|
095864de39 | ||
|
|
0eb196e852 | ||
|
|
62e5d8d841 | ||
|
|
2f82fb842d | ||
|
|
f9fa6abbad | ||
|
|
4b55a69150 | ||
|
|
42e12fae24 | ||
|
|
2f55d5130a | ||
|
|
cd4b47b397 | ||
|
|
126cba1e94 | ||
|
|
e4e894fa54 | ||
|
|
65f701d459 | ||
|
|
c2b7cc7964 | ||
|
|
969d1dff2a | ||
|
|
e0fab808b9 | ||
|
|
77031d7082 | ||
|
|
5a0f7ea706 | ||
|
|
9d041c4b62 | ||
|
|
ec4d29c327 | ||
|
|
5f8d8988e3 | ||
|
|
a31912f710 | ||
|
|
e0d42962e7 | ||
|
|
47688bf016 | ||
|
|
39a695959d | ||
|
|
71d45a67b7 | ||
|
|
8dc1369ba3 | ||
|
|
1206654d98 | ||
|
|
1492763d0c | ||
|
|
285fafc464 | ||
|
|
b15cc59b95 | ||
|
|
55cfe31dfe | ||
|
|
dafa6e0310 | ||
|
|
4befe17cab | ||
|
|
2217b560cb | ||
|
|
39d01a1da8 | ||
|
|
4e14ad7821 | ||
|
|
aee5a2cbe4 | ||
|
|
de2d5aec1a | ||
|
|
bea03de562 | ||
|
|
0aeccbb02c | ||
|
|
4e11152e76 | ||
|
|
5059028e8a | ||
|
|
070e6cf848 | ||
|
|
4187fe7261 | ||
|
|
bdec147874 | ||
|
|
189fc76dad | ||
|
|
c0a68005d4 | ||
|
|
8187e4cdc2 | ||
|
|
cc98fd4167 | ||
|
|
422e014eff | ||
|
|
dd4e044608 | ||
|
|
13fad1ca47 | ||
|
|
70aaa29016 | ||
|
|
7dcec9afcf | ||
|
|
608642fc25 | ||
|
|
dd62c08b09 | ||
|
|
315fc21eca | ||
|
|
8cfd3aeda9 | ||
|
|
6061323c36 | ||
|
|
17da8b04bf | ||
|
|
3a948406d5 | ||
|
|
91c52f8ec8 | ||
|
|
6076bbb554 | ||
|
|
f70d3359c4 | ||
|
|
2dbfbe36c3 | ||
|
|
a25bd03566 | ||
|
|
109da1c2cc | ||
|
|
9a48e25bb1 | ||
|
|
a3ade200a9 | ||
|
|
eb1901f03d | ||
|
|
f8c7cdfc28 | ||
|
|
ea58d84b55 | ||
|
|
1360caaa81 | ||
|
|
315a7b9e6a | ||
|
|
79311d107b | ||
|
|
77435fcbd7 | ||
|
|
4395c07648 | ||
|
|
f9cc77382a | ||
|
|
e4fce08eff | ||
|
|
47196cbc55 | ||
|
|
c0bf301434 | ||
|
|
f6671f8561 | ||
|
|
26aab2961b | ||
|
|
c233abe3a4 | ||
|
|
beb3646bef | ||
|
|
eb8330a420 | ||
|
|
7f3c2176f8 | ||
|
|
1d0fdd68f4 | ||
|
|
b26c78828e | ||
|
|
85648c7f11 | ||
|
|
8588f446aa | ||
|
|
d723c5c1c7 | ||
|
|
7897f2489c | ||
|
|
86e2e0a2c3 | ||
|
|
e2349019a3 | ||
|
|
e485044cfb | ||
|
|
72d80e2857 | ||
|
|
79a4733698 | ||
|
|
c64e1daa99 | ||
|
|
d6b4933b05 | ||
|
|
bcf9cee0f8 | ||
|
|
a2960a7997 | ||
|
|
dac9874895 | ||
|
|
a4d374ecb6 | ||
|
|
3e56c8ed8b | ||
|
|
f4c11d14e7 | ||
|
|
dde805931d | ||
|
|
fedc721432 | ||
|
|
b09cb24804 | ||
|
|
80bfeb1a29 | ||
|
|
63ad51a637 | ||
|
|
5ee6232bfc | ||
|
|
b7d399faf7 | ||
|
|
28691bb8a0 | ||
|
|
fd61f35f78 | ||
|
|
0505b59781 | ||
|
|
a3601e5167 | ||
|
|
99f63ab64d | ||
|
|
bb2a9fe180 | ||
|
|
e9c7c7ee4c | ||
|
|
ff7d4f237a | ||
|
|
8bf9f8057e | ||
|
|
27b0f531d7 | ||
|
|
01e96d3086 | ||
|
|
95e7a7c8eb | ||
|
|
bbc2f4766e | ||
|
|
12dde5154f | ||
|
|
5bc3609ad7 | ||
|
|
5c1cdea3c6 | ||
|
|
3da5b3a278 | ||
|
|
9f6460ddae | ||
|
|
f1dcf85d7b | ||
|
|
7b78edf38f | ||
|
|
4a726a0c75 | ||
|
|
5afa7e9fc3 | ||
|
|
75c102f304 | ||
|
|
68f3c10055 | ||
|
|
e9aeda2a34 | ||
|
|
45c7a110b6 | ||
|
|
e438e6aedb | ||
|
|
8dd888bcbd | ||
|
|
5f12291ac4 | ||
|
|
e206d78a52 | ||
|
|
d94a82b0a7 | ||
|
|
3bee3d1701 | ||
|
|
631352e1ef | ||
|
|
f2d5dee49e | ||
|
|
2d578c7cd9 | ||
|
|
b07ff100d1 | ||
|
|
b0c10b2248 | ||
|
|
7b8249442d | ||
|
|
8221339318 | ||
|
|
b773af96b8 | ||
|
|
dbc7808c70 | ||
|
|
77c5000f16 | ||
|
|
56928b6ca0 | ||
|
|
8178dfe7bd | ||
|
|
0db6c667c6 | ||
|
|
2b0a642827 | ||
|
|
f71c0f8cb8 | ||
|
|
3782372f65 | ||
|
|
9e35e42030 | ||
|
|
7a8195e54c | ||
|
|
b50ee28693 | ||
|
|
e134f910aa | ||
|
|
17eb6e47eb | ||
|
|
82ce626d8a | ||
|
|
e063ff5e54 | ||
|
|
5aa98966bc | ||
|
|
fca2746a82 | ||
|
|
7faffc2de7 | ||
|
|
86a8a9dc85 | ||
|
|
c13a0c1c26 | ||
|
|
6ce3c070b8 | ||
|
|
e1cb11da22 | ||
|
|
c628ea34b7 | ||
|
|
33b7c726f6 | ||
|
|
6f3fd424e1 | ||
|
|
b048ce9c8e | ||
|
|
6a1040d659 | ||
|
|
fc05c28231 | ||
|
|
5d165bc6df | ||
|
|
c139b5eabb | ||
|
|
d6dd0e551d | ||
|
|
a84a7bb7c3 | ||
|
|
7195ae6e46 | ||
|
|
3c70b2f033 | ||
|
|
c46340a830 | ||
|
|
8d703b1459 | ||
|
|
f1145ef169 | ||
|
|
a28f9ceeba | ||
|
|
1927bb4798 | ||
|
|
d4c77f9786 | ||
|
|
021f8d0331 | ||
|
|
e7d25550ee | ||
|
|
8227276437 | ||
|
|
38ef76bcdc | ||
|
|
7faa0f60b4 | ||
|
|
faebbb2b8a | ||
|
|
bc9beb1e87 | ||
|
|
f161f723d7 | ||
|
|
6c94550de3 | ||
|
|
b39a45781a | ||
|
|
9273c9ee10 | ||
|
|
e6285914b0 | ||
|
|
7d2eb83a1b | ||
|
|
7e622f1153 | ||
|
|
9b30a634b1 | ||
|
|
e897d2ef81 | ||
|
|
f5b177dda8 | ||
|
|
5f51b5d565 | ||
|
|
8d7753fb40 | ||
|
|
4c6f5744d8 | ||
|
|
43eb2932ba | ||
|
|
59de0e7a4a | ||
|
|
7b204cf910 | ||
|
|
b587a19a0f | ||
|
|
e58420b031 | ||
|
|
24313d6709 | ||
|
|
7acb30b981 | ||
|
|
7454620aa2 | ||
|
|
abb0799817 | ||
|
|
aebd28dbef | ||
|
|
152db9fb8e | ||
|
|
980cd520cd | ||
|
|
373ec8b5a1 | ||
|
|
bacb78e9e8 | ||
|
|
333e112e37 | ||
|
|
818d5d782f | ||
|
|
8774090086 | ||
|
|
0ca65c6d61 | ||
|
|
c38daac646 | ||
|
|
c31f5b1d59 | ||
|
|
2320a56a63 | ||
|
|
07bd91b456 | ||
|
|
803f7c3a4b | ||
|
|
a1926982c6 | ||
|
|
b3306d50eb | ||
|
|
3e6e5f95ec | ||
|
|
df71fb42ac | ||
|
|
fe81a354a3 | ||
|
|
67ba2e49c3 | ||
|
|
8dec42f295 | ||
|
|
5942553370 | ||
|
|
ae5d31746e | ||
|
|
1c1976abbf | ||
|
|
b3465ddf89 | ||
|
|
f67fafdb8e | ||
|
|
ffcac677e0 | ||
|
|
a9d643aa55 | ||
|
|
fa3647e8e5 | ||
|
|
db839f6d6d | ||
|
|
3a85d5b89f | ||
|
|
1575c2bc10 | ||
|
|
57189183d6 | ||
|
|
01c5cf98df | ||
|
|
9ad2995168 | ||
|
|
51fc6518e1 | ||
|
|
530db4e904 | ||
|
|
cd92ebd69c | ||
|
|
5c58552d91 | ||
|
|
584aebf76b | ||
|
|
43a2480699 | ||
|
|
699503042f | ||
|
|
02e71c23de | ||
|
|
c24c3868bb | ||
|
|
5c60819668 | ||
|
|
480a125a20 | ||
|
|
598a6ebaed | ||
|
|
185ad5e747 | ||
|
|
42d7e4312b | ||
|
|
41a741724f | ||
|
|
087e811586 | ||
|
|
fa07bc143e | ||
|
|
e13f5ed730 | ||
|
|
48108cebc9 | ||
|
|
ecac17a00a | ||
|
|
3ba215887c | ||
|
|
edbc1baf32 | ||
|
|
500fa7f314 | ||
|
|
a10dfa4ab4 | ||
|
|
b208571ed7 | ||
|
|
c1ec27045c | ||
|
|
111b12161c | ||
|
|
d23d18933c | ||
|
|
f0e736962d | ||
|
|
b12d1491d4 | ||
|
|
6d1a094b15 | ||
|
|
88d14792a4 | ||
|
|
7780a98abe | ||
|
|
a1248db3e7 | ||
|
|
6fd2d12d3f | ||
|
|
9fab52c044 | ||
|
|
a32f55d64e | ||
|
|
96d2b001ea | ||
|
|
e38d6c9f9e | ||
|
|
be129d243b | ||
|
|
c85355986b | ||
|
|
310c0e8379 | ||
|
|
f0dbffe4f8 | ||
|
|
598a482538 | ||
|
|
a995f3ab52 | ||
|
|
ec5454de44 | ||
|
|
ec55bddbd4 | ||
|
|
808591df56 | ||
|
|
96ae3da1e0 | ||
|
|
4f13df69c7 | ||
|
|
6d305e4a84 | ||
|
|
d3dab982a1 | ||
|
|
2f6adbae3c | ||
|
|
ccdc1eed5a | ||
|
|
65dee154f7 | ||
|
|
662db009de | ||
|
|
d07d8f6857 | ||
|
|
0327f6b1ea | ||
|
|
16dea2ee09 | ||
|
|
5e3fe139fc | ||
|
|
55d41fca05 | ||
|
|
0b7bb29557 | ||
|
|
6b683bd02d | ||
|
|
8f1a498953 | ||
|
|
293ed9065d | ||
|
|
8908fb7ee0 | ||
|
|
2877178b34 | ||
|
|
d527139f48 | ||
|
|
b3d8180541 | ||
|
|
3411c340ab | ||
|
|
4482089712 | ||
|
|
c8ce2343d7 | ||
|
|
d547d04277 | ||
|
|
00bdbcf857 | ||
|
|
bf8070c42d | ||
|
|
0f30d7744d | ||
|
|
095a978120 | ||
|
|
1b72646772 | ||
|
|
a7b129147f | ||
|
|
ca9039b8d5 | ||
|
|
0e17658269 | ||
|
|
87edda5972 | ||
|
|
ab8c3c2b75 | ||
|
|
44cef002ed | ||
|
|
bc80fe693a | ||
|
|
1898b51dc2 | ||
|
|
a5dcf5958e | ||
|
|
36c417458d | ||
|
|
f1591e593a | ||
|
|
117ac0f5d6 | ||
|
|
e72a42fd2d | ||
|
|
07b6e2b9f1 | ||
|
|
5ab10f4024 | ||
|
|
b32499f6ec | ||
|
|
d9819d8b3d | ||
|
|
a0cbc5822e | ||
|
|
65ffce8db2 | ||
|
|
f241f12ddb | ||
|
|
9545e0145f | ||
|
|
15d3e4023b | ||
|
|
205e4dedd1 | ||
|
|
324d796aca | ||
|
|
5b31e8e9d8 | ||
|
|
0afb62ee88 | ||
|
|
1d3a8bcacd | ||
|
|
dd187aef08 | ||
|
|
78b2c4d094 | ||
|
|
83876532d0 | ||
|
|
edc824d05e | ||
|
|
9a7869dd40 | ||
|
|
1aa247d4da | ||
|
|
93e8ec7f06 | ||
|
|
ba1d1c3bac | ||
|
|
fbaff2b75b | ||
|
|
9774e8807b | ||
|
|
b4dd3a9889 | ||
|
|
4510b69798 | ||
|
|
84a3ee209a | ||
|
|
4b0fe879cb | ||
|
|
b87aeffcd8 | ||
|
|
88aafec6e3 | ||
|
|
2cb6ddf3ca | ||
|
|
799622a932 | ||
|
|
2283652704 | ||
|
|
33fb2daabf | ||
|
|
c79f59ec05 | ||
|
|
845aaad891 | ||
|
|
3c0b061220 | ||
|
|
af7de73f5c | ||
|
|
1a8a9f0047 | ||
|
|
a3b4e8db44 | ||
|
|
f9e8875803 | ||
|
|
e4c11d9a88 | ||
|
|
6d1d8da101 | ||
|
|
77303e668f | ||
|
|
72725b91c7 | ||
|
|
46f50f55c2 | ||
|
|
5e880f6bfa | ||
|
|
80cf88dcc1 | ||
|
|
dddc7f348f | ||
|
|
ce45f2e114 | ||
|
|
d22a7c0a63 | ||
|
|
59162155de | ||
|
|
0f347cd0b4 | ||
|
|
dc50793733 | ||
|
|
b98329f91c | ||
|
|
587d8fbfa9 | ||
|
|
26a2e9ef53 | ||
|
|
e5ca98a1d3 | ||
|
|
5bd5e9000e | ||
|
|
d566642c98 | ||
|
|
f81ddc60ec | ||
|
|
6e0b20c921 | ||
|
|
0c308b38c5 | ||
|
|
8e0fdf68e4 | ||
|
|
9c7a9b5674 | ||
|
|
0a49e31b30 | ||
|
|
9478206202 | ||
|
|
c86e6f5b88 | ||
|
|
87868acecd | ||
|
|
6d877754da | ||
|
|
dd5521a07c | ||
|
|
cca142ce86 | ||
|
|
fa3567cb9f | ||
|
|
7d22230b13 | ||
|
|
1b7f684ab2 | ||
|
|
fef8f4d29c | ||
|
|
0d7e945623 | ||
|
|
13ea8eae77 | ||
|
|
cdb2ef3ac7 | ||
|
|
f216e0198f | ||
|
|
c5c3698f43 | ||
|
|
299d3b9f60 | ||
|
|
eaaebeaebb | ||
|
|
2ef2d2b9c5 | ||
|
|
a856b4027b | ||
|
|
e2f72ecffc | ||
|
|
d888bcd473 | ||
|
|
0bfd5fa9a3 | ||
|
|
9c05ccef69 | ||
|
|
5281b6df77 | ||
|
|
cd5bb6a309 | ||
|
|
94a44d7af4 | ||
|
|
662050b89f | ||
|
|
3f7f3140eb | ||
|
|
dc11d18161 | ||
|
|
cde83e9d7d | ||
|
|
1476fda014 | ||
|
|
7a875f2db4 | ||
|
|
b868f37da5 | ||
|
|
9bef5a62dd | ||
|
|
d1aff30c9e | ||
|
|
a356dafde0 | ||
|
|
09477384fe | ||
|
|
6b923542e8 | ||
|
|
9722cf718c | ||
|
|
702b973c87 | ||
|
|
d66bbd95f0 | ||
|
|
aae498509b | ||
|
|
4987fb5f15 | ||
|
|
b1ed299286 | ||
|
|
679ac3e5b1 | ||
|
|
181ef788a3 | ||
|
|
d2caa0953f | ||
|
|
bd6d5ffbb9 | ||
|
|
a4e41a57d7 | ||
|
|
e8bb3864e3 | ||
|
|
3cd1066a8d | ||
|
|
6494b7ea89 | ||
|
|
2f5d13ac10 | ||
|
|
c8e6dca263 | ||
|
|
9dec40eca7 | ||
|
|
4896043599 | ||
|
|
5f0ddeedd0 | ||
|
|
e679e760ee | ||
|
|
1b0aa2a9b1 | ||
|
|
a49c230bb2 | ||
|
|
b65749cf70 | ||
|
|
0cf54a6a03 | ||
|
|
4d77277f2d | ||
|
|
8b81506ed3 | ||
|
|
24c6509a61 | ||
|
|
3c32c4160d | ||
|
|
1f912d86e1 | ||
|
|
9c93ab0b6d | ||
|
|
6646a20a5d | ||
|
|
92f471c476 | ||
|
|
4c7ce97650 | ||
|
|
4d1fc994c0 | ||
|
|
c4310c554e | ||
|
|
4ebcf40619 | ||
|
|
a884b94ac0 | ||
|
|
194e705982 | ||
|
|
c77ec3bf20 | ||
|
|
0a1857249b | ||
|
|
03bd5ad426 | ||
|
|
82d0f62d73 | ||
|
|
041fce743e | ||
|
|
ad4904a0ec | ||
|
|
42a1bca64d | ||
|
|
492a5bbb38 | ||
|
|
c40ef22c20 | ||
|
|
783aac561a | ||
|
|
02245c150c | ||
|
|
54a93775e4 | ||
|
|
ca778aac70 | ||
|
|
c75f1685c5 | ||
|
|
db5b103ab3 | ||
|
|
b2f1c9e832 | ||
|
|
cff4eebb74 | ||
|
|
a3aab5aeca | ||
|
|
63e5d8f7ea | ||
|
|
e4df7b10f6 | ||
|
|
f39a1e7d03 | ||
|
|
a473f6aa16 | ||
|
|
6907c24690 | ||
|
|
ef3d8a9277 | ||
|
|
1a183b7f17 | ||
|
|
ae6bcc2987 | ||
|
|
ed61e5c7f7 | ||
|
|
3581ac5ebd | ||
|
|
1f369b47d7 | ||
|
|
51ec699649 | ||
|
|
ed6be510fb | ||
|
|
55f05ba839 | ||
|
|
17c9382a21 | ||
|
|
50a53181dc | ||
|
|
36f6fec99f | ||
|
|
e9ae8063db | ||
|
|
9844550d6c | ||
|
|
82fcf08d84 | ||
|
|
b951624ed3 | ||
|
|
1b21f2d1c7 | ||
|
|
43ea0effa2 | ||
|
|
6df9b95dea | ||
|
|
cd7b9d31c9 | ||
|
|
cbb4565a48 | ||
|
|
fc2f5c3615 | ||
|
|
3a5ac9df10 | ||
|
|
e8497bb0cc | ||
|
|
fa6a5f75af | ||
|
|
7b9eb4c87c | ||
|
|
c3ab510b57 | ||
|
|
e9efe5bbb7 | ||
|
|
a7c234b272 | ||
|
|
80c8dcacb3 | ||
|
|
0ad2dea722 | ||
|
|
dec3c45f6c | ||
|
|
5736d2c9cc | ||
|
|
dc622a9bd5 | ||
|
|
3ddd1d5f21 | ||
|
|
ea15f0690c | ||
|
|
8751ef7439 | ||
|
|
8c393e3144 | ||
|
|
a3ef2a43ac | ||
|
|
d2b3029f67 | ||
|
|
3e800ec7c8 | ||
|
|
47d963b585 | ||
|
|
397ae3407e | ||
|
|
3f9e2d5d39 | ||
|
|
31449552d1 | ||
|
|
5f2fee5a99 | ||
|
|
bd71e779c3 | ||
|
|
18d83b8c50 | ||
|
|
49c7e7ce2d | ||
|
|
1af7d01d96 | ||
|
|
66c6938df0 | ||
|
|
bb9fb04788 | ||
|
|
f5cf2b2b55 | ||
|
|
43169ced69 | ||
|
|
708844857d | ||
|
|
ce777348b3 | ||
|
|
c052325487 | ||
|
|
ce69410f49 | ||
|
|
5f0be0cc7f | ||
|
|
dca6347ea9 | ||
|
|
94c4db9072 | ||
|
|
7f63be2cd2 | ||
|
|
1236dea4c5 | ||
|
|
c5c46df613 | ||
|
|
a96269150a | ||
|
|
75e347e8e3 | ||
|
|
d641cc5057 | ||
|
|
cf64145ff3 | ||
|
|
195eca6b09 | ||
|
|
b0ef077e48 | ||
|
|
e40a698d1a | ||
|
|
69c34b1b27 | ||
|
|
4700c2583d | ||
|
|
6cf79a3c73 | ||
|
|
2c110f7796 | ||
|
|
aebea0b57f | ||
|
|
8684455ea5 | ||
|
|
7cbb103376 | ||
|
|
9cf8ec5dd7 | ||
|
|
f5187b1533 | ||
|
|
6230a65a79 | ||
|
|
f13c96839e | ||
|
|
776f68c9dd | ||
|
|
714ed89a4c | ||
|
|
0b2b29b7cf | ||
|
|
a41c0a0519 | ||
|
|
026e28114e | ||
|
|
0840719289 | ||
|
|
aad875e113 | ||
|
|
6b059d94ba | ||
|
|
39eeaea7e7 | ||
|
|
81a8e91180 | ||
|
|
4fc6f6f59b | ||
|
|
dd2f573f27 | ||
|
|
ff8ddc431b | ||
|
|
d703945aa3 | ||
|
|
f4aba5f9ea | ||
|
|
393113c3e3 | ||
|
|
f2db18820a | ||
|
|
e04d300e18 | ||
|
|
685f836f41 | ||
|
|
d4f5ad641a | ||
|
|
007d437ee8 | ||
|
|
00e380e8d8 | ||
|
|
56749d9494 | ||
|
|
cf10d37c90 | ||
|
|
f9de34e36d | ||
|
|
aa889be140 | ||
|
|
6da6838bde | ||
|
|
07a0be303f | ||
|
|
ddbe0af290 | ||
|
|
e71f0f44a4 | ||
|
|
39f3bc7100 | ||
|
|
211a145e58 | ||
|
|
d2339b915f | ||
|
|
24c2651745 | ||
|
|
36e95b93b4 | ||
|
|
e7a44e0b15 | ||
|
|
fbdb21ac72 | ||
|
|
c9522cf6ae | ||
|
|
7bf9d334ad | ||
|
|
422ff93dcd | ||
|
|
eee1ea7a09 | ||
|
|
b90e482add | ||
|
|
875b51720b | ||
|
|
5272d4f9e3 | ||
|
|
c980f4f6e3 | ||
|
|
89525330c4 | ||
|
|
806db24c0e | ||
|
|
ba7d6df5a4 | ||
|
|
112301e0e0 | ||
|
|
c07065e457 | ||
|
|
b9e1afae17 | ||
|
|
549b4a9ebf | ||
|
|
fa9a3fb7c4 | ||
|
|
ccb15f278a | ||
|
|
f0fb4b8842 | ||
|
|
dacaf293b2 | ||
|
|
627d0e4e6f | ||
|
|
f32b9e26e0 | ||
|
|
db2b1a0a35 | ||
|
|
f734a60c62 | ||
|
|
9aa815c1ed | ||
|
|
e0772500c9 | ||
|
|
718f79775e | ||
|
|
4261c1768a | ||
|
|
01603185c3 | ||
|
|
f1adf24f3b | ||
|
|
856765a775 | ||
|
|
075da34cdb | ||
|
|
5ce543002d | ||
|
|
700b1c50af | ||
|
|
9e1c363a0a | ||
|
|
bbe3065f19 | ||
|
|
1716a0ebca | ||
|
|
ed66339e94 | ||
|
|
441774e61c | ||
|
|
c6fd14afd2 | ||
|
|
27ed9ca7fd | ||
|
|
50fda906bd | ||
|
|
6c7c60037b | ||
|
|
91a429ea77 | ||
|
|
2dbe01e90a | ||
|
|
3d7dd41223 | ||
|
|
91495c8595 | ||
|
|
c3e997168a | ||
|
|
c3e6f4f7b6 | ||
|
|
e600ec95ed | ||
|
|
cbd5853389 | ||
|
|
4e3a086993 | ||
|
|
5f23dfb034 | ||
|
|
99f59490a3 | ||
|
|
4a44b35ea5 | ||
|
|
47aafe9042 | ||
|
|
53d94a1d3d | ||
|
|
5dd3c7c8e8 | ||
|
|
0c2a13f27f | ||
|
|
da0d3133df | ||
|
|
7abd9a3db9 | ||
|
|
98b9f57e0e | ||
|
|
e064ccd585 | ||
|
|
b5a63ed3fc | ||
|
|
147edddb90 | ||
|
|
4b8a91fb8c | ||
|
|
4fb677ed23 | ||
|
|
4f0bd7f461 | ||
|
|
01eecdb698 | ||
|
|
5ca41a6fff | ||
|
|
e4f71420fc | ||
|
|
d5456ed1db | ||
|
|
e3335c5843 | ||
|
|
067e6bf93d | ||
|
|
13dc31dc1f | ||
|
|
7f6bf21270 | ||
|
|
d647258856 | ||
|
|
1eb7ed5e9c | ||
|
|
e78f1b05a7 | ||
|
|
e45140b7ea | ||
|
|
41747a7a04 | ||
|
|
e448430d8a | ||
|
|
394b1a1602 | ||
|
|
85abd5a8c3 | ||
|
|
b9e0eb5b76 | ||
|
|
a8eae73b66 | ||
|
|
9edd8d6f66 | ||
|
|
e5d883f97e |
@@ -17,3 +17,4 @@ Tests/Manual/**/*.xcodeproj
|
||||
*.sublime-project
|
||||
*.sublime-workspace
|
||||
*.tm_properties
|
||||
*.idea
|
||||
@@ -27,6 +27,7 @@
|
||||
@import "CPAccordionView.j"
|
||||
@import "CPAlert.j"
|
||||
@import "CPAnimation.j"
|
||||
@import "CPAnimationContext.j"
|
||||
@import "CPAppearance.j"
|
||||
@import "CPApplication.j"
|
||||
@import "CPArrayController.j"
|
||||
@@ -100,6 +101,7 @@
|
||||
@import "CPTabView.j"
|
||||
@import "CPText.j"
|
||||
@import "CPTextField.j"
|
||||
@import "CPTextView.j"
|
||||
@import "CPTokenField.j"
|
||||
@import "CPToolbar.j"
|
||||
@import "CPToolbarItem.j"
|
||||
@@ -107,6 +109,7 @@
|
||||
@import "CPTreeNode.j"
|
||||
@import "CPUserDefaultsController.j"
|
||||
@import "CPView.j"
|
||||
@import "CPViewAnimator.j"
|
||||
@import "CPViewAnimation.j"
|
||||
@import "CPViewController.j"
|
||||
@import "CPVisualEffectView.j"
|
||||
|
||||
@@ -216,7 +216,7 @@ var CPApplicationDelegate_applicationShouldTerminate_ = 1 << 0,
|
||||
}
|
||||
|
||||
if ([_delegate respondsToSelector:@selector(applicationShouldTerminate:)])
|
||||
_implementedDelegateMethods |= CPApplicationDelegate_applicationShouldTerminate_
|
||||
_implementedDelegateMethods |= CPApplicationDelegate_applicationShouldTerminate_;
|
||||
|
||||
if ([_delegate respondsToSelector:@selector(applicationShouldTerminateMessage:)])
|
||||
_implementedDelegateMethods |= CPApplicationDelegate_applicationShouldTerminateMessage_
|
||||
|
||||
@@ -563,7 +563,7 @@
|
||||
*/
|
||||
- (BOOL)setSelectionIndexes:(CPIndexSet)indexes
|
||||
{
|
||||
[self _selectionWillChange]
|
||||
[self _selectionWillChange];
|
||||
var r = [self __setSelectionIndexes:indexes avoidEmpty:NO];
|
||||
[self _selectionDidChange];
|
||||
return r;
|
||||
|
||||
+21
-2
@@ -598,7 +598,8 @@ var CPBoxTypeKey = @"CPBoxTypeKey",
|
||||
CPBoxBorderTypeKey = @"CPBoxBorderTypeKey",
|
||||
CPBoxTitle = @"CPBoxTitle",
|
||||
CPBoxTitlePosition = @"CPBoxTitlePosition",
|
||||
CPBoxTitleView = @"CPBoxTitleView";
|
||||
CPBoxTitleView = @"CPBoxTitleView",
|
||||
CPBoxContentView = @"CPBoxContentView";
|
||||
|
||||
@implementation CPBox (CPCoding)
|
||||
|
||||
@@ -615,8 +616,25 @@ var CPBoxTypeKey = @"CPBoxTypeKey",
|
||||
_titlePosition = [aCoder decodeIntForKey:CPBoxTitlePosition];
|
||||
_titleView = [aCoder decodeObjectForKey:CPBoxTitleView] || [CPTextField labelWithTitle:_title];
|
||||
|
||||
_contentView = [self subviews][0];
|
||||
if (_boxType != CPBoxSeparator)
|
||||
{
|
||||
// FIXME: we have a problem with CIB decoding here.
|
||||
// We should be able to simply add : _contentView = [self subviews][0]
|
||||
// but first box subview seems to be malformed (badly decoded).
|
||||
// For example, when deployed, this view doesn't have its _trackingAreas array initialized.
|
||||
// As a (temporary) workaround, we encode/decode the _contentView property. We then transfer the subview hierarchy
|
||||
// and replace the first (and only) box subview with this _contentView
|
||||
|
||||
_contentView = [aCoder decodeObjectForKey:CPBoxContentView] || [[CPView alloc] initWithFrame:[self bounds]];
|
||||
var malformedContentView = [self subviews][0];
|
||||
[_contentView setSubviews:[malformedContentView subviews]];
|
||||
[self replaceSubview:malformedContentView with:_contentView];
|
||||
}
|
||||
else
|
||||
{
|
||||
_titlePosition = CPNoTitle;
|
||||
}
|
||||
|
||||
[self setAutoresizesSubviews:YES];
|
||||
[_contentView setAutoresizingMask:CPViewWidthSizable | CPViewHeightSizable];
|
||||
|
||||
@@ -635,6 +653,7 @@ var CPBoxTypeKey = @"CPBoxTypeKey",
|
||||
[aCoder encodeObject:_title forKey:CPBoxTitle];
|
||||
[aCoder encodeInt:_titlePosition forKey:CPBoxTitlePosition];
|
||||
[aCoder encodeObject:_titleView forKey:CPBoxTitleView];
|
||||
[aCoder encodeObject:_contentView forKey:CPBoxContentView];
|
||||
}
|
||||
|
||||
@end
|
||||
|
||||
+1
-1
@@ -378,7 +378,7 @@ CPButtonImageOffset = 3.0;
|
||||
|
||||
- (void)setImage:(CPImage)anImage
|
||||
{
|
||||
[self setValue:anImage forThemeAttribute:@"image"];
|
||||
[self setValue:anImage forThemeAttribute:@"image" inState:CPThemeStateNormal];
|
||||
}
|
||||
|
||||
- (CPImage)image
|
||||
|
||||
+43
-24
@@ -31,6 +31,7 @@
|
||||
@import "CPDragServer_Constants.j"
|
||||
@import "CPPasteboard.j"
|
||||
@import "CPView.j"
|
||||
@import "CPKeyValueBinding.j"
|
||||
|
||||
@class _CPCollectionViewDropIndicator
|
||||
|
||||
@@ -55,7 +56,7 @@ var CPCollectionViewDelegate_collectionView_acceptDrop_index_dropOperation_
|
||||
- (CPData)collectionView:(CPCollectionView)collectionView dataForItemsAtIndexes:(CPIndexSet)indices forType:(CPString)aType;
|
||||
- (CPDragOperation)collectionView:(CPCollectionView)collectionView validateDrop:(id)draggingInfo proposedIndex:(CPInteger)proposedDropIndex dropOperation:(CPCollectionViewDropOperation)proposedDropOperation;
|
||||
- (CPMenu)collectionView:(CPCollectionView)collectionView menuForItemAtIndex:(CPInteger)anIndex;
|
||||
- (CPView)collectionView:(CPCollectionView)collectionView dragginViewForItemsAtIndexes:(CPIndexSet)indexes withEvent:(CPEvent)event offset:(CGPoint)dragImageOffset;
|
||||
- (CPView)collectionView:(CPCollectionView)collectionView draggingViewForItemsAtIndexes:(CPIndexSet)indexes withEvent:(CPEvent)event offset:(CGPoint)dragImageOffset;
|
||||
- (void)collectionView:(CPCollectionView)collectionView didDoubleClickOnItemAtIndex:(int)index;
|
||||
|
||||
@end
|
||||
@@ -132,7 +133,6 @@ var HORIZONTAL_MARGIN = 2;
|
||||
CGSize _storedFrameSize;
|
||||
|
||||
BOOL _uniformSubviewsResizing @accessors(property=uniformSubviewsResizing);
|
||||
BOOL _lockResizing;
|
||||
|
||||
CPInteger _currentDropIndex;
|
||||
CPDragOperation _currentDragOperation;
|
||||
@@ -140,6 +140,14 @@ var HORIZONTAL_MARGIN = 2;
|
||||
_CPCollectionViewDropIndicator _dropView;
|
||||
}
|
||||
|
||||
+ (Class)_binderClassForBinding:(CPString)aBinding
|
||||
{
|
||||
if (aBinding == CPContentBinding)
|
||||
return [_CPCollectionViewContentBinder class];
|
||||
|
||||
return [super _binderClassForBinding:aBinding];
|
||||
}
|
||||
|
||||
- (id)initWithFrame:(CGRect)aFrame
|
||||
{
|
||||
self = [super initWithFrame:aFrame];
|
||||
@@ -182,7 +190,7 @@ var HORIZONTAL_MARGIN = 2;
|
||||
|
||||
_needsMinMaxItemSizeUpdate = YES;
|
||||
_uniformSubviewsResizing = NO;
|
||||
_lockResizing = NO;
|
||||
_inLiveResize = NO;
|
||||
|
||||
_currentDropIndex = -1;
|
||||
_currentDragOperation = CPDragOperationNone;
|
||||
@@ -384,14 +392,7 @@ var HORIZONTAL_MARGIN = 2;
|
||||
_isSelectable = isSelectable;
|
||||
|
||||
if (!_isSelectable)
|
||||
{
|
||||
var index = CPNotFound,
|
||||
itemCount = [_items count];
|
||||
|
||||
// Be wary of invalid selection ranges since setContent: does not clear selection indexes.
|
||||
while ((index = [_selectionIndexes indexGreaterThanIndex:index]) != CPNotFound && index < itemCount)
|
||||
[_items[index] setSelected:NO];
|
||||
}
|
||||
[self _applySelectionToItems:NO];
|
||||
}
|
||||
|
||||
/*!
|
||||
@@ -445,22 +446,15 @@ var HORIZONTAL_MARGIN = 2;
|
||||
{
|
||||
if (!anIndexSet)
|
||||
anIndexSet = [CPIndexSet indexSet];
|
||||
|
||||
if (!_isSelectable || [_selectionIndexes isEqual:anIndexSet])
|
||||
return;
|
||||
|
||||
var index = CPNotFound,
|
||||
itemCount = [_items count];
|
||||
|
||||
// Be wary of invalid selection ranges since setContent: does not clear selection indexes.
|
||||
while ((index = [_selectionIndexes indexGreaterThanIndex:index]) !== CPNotFound && index < itemCount)
|
||||
[_items[index] setSelected:NO];
|
||||
[self _applySelectionToItems:NO];
|
||||
|
||||
_selectionIndexes = anIndexSet;
|
||||
|
||||
var index = CPNotFound;
|
||||
|
||||
while ((index = [_selectionIndexes indexGreaterThanIndex:index]) !== CPNotFound)
|
||||
[_items[index] setSelected:YES];
|
||||
[self _applySelectionToItems:YES];
|
||||
|
||||
var binderClass = [[self class] _binderClassForBinding:@"selectionIndexes"];
|
||||
[[binderClass getBinding:@"selectionIndexes" forObject:self] reverseSetValueFor:@"selectionIndexes"];
|
||||
@@ -525,14 +519,14 @@ var HORIZONTAL_MARGIN = 2;
|
||||
|
||||
- (void)resizeWithOldSuperviewSize:(CGSize)oldBoundsSize
|
||||
{
|
||||
if (_lockResizing)
|
||||
if (_inLiveResize)
|
||||
return;
|
||||
|
||||
_lockResizing = YES;
|
||||
_inLiveResize = YES;
|
||||
|
||||
[self tile];
|
||||
|
||||
_lockResizing = NO;
|
||||
_inLiveResize = NO;
|
||||
}
|
||||
|
||||
- (void)tile
|
||||
@@ -972,6 +966,20 @@ var HORIZONTAL_MARGIN = 2;
|
||||
return frame;
|
||||
}
|
||||
|
||||
- (void)_applySelectionToItems:(BOOL)select
|
||||
{
|
||||
var numberOfItems = [_items count];
|
||||
|
||||
[_selectionIndexes enumerateIndexesUsingBlock:function(idx, stop)
|
||||
{
|
||||
if (idx < numberOfItems)
|
||||
[[_items objectAtIndex:idx] setSelected:select];
|
||||
else {
|
||||
stop(YES);
|
||||
}
|
||||
}];
|
||||
}
|
||||
|
||||
@end
|
||||
|
||||
@implementation CPCollectionView (DragAndDrop)
|
||||
@@ -1586,3 +1594,14 @@ var CPCollectionViewMinItemSizeKey = @"CPCollectionViewMinItemSizeK
|
||||
}
|
||||
|
||||
@end
|
||||
|
||||
@implementation _CPCollectionViewContentBinder : CPBinder
|
||||
{
|
||||
}
|
||||
|
||||
- (void)setValue:(id)aValue forBinding:(CPString)aBinding
|
||||
{
|
||||
[_source setContent:aValue];
|
||||
}
|
||||
|
||||
@end
|
||||
|
||||
+241
-5
@@ -100,8 +100,11 @@ var cachedBlackColor,
|
||||
+ (CPDictionary)themeAttributes
|
||||
{
|
||||
return @{
|
||||
@"alternate-selected-control-color": [CPNull null],
|
||||
@"secondary-selected-control-color" : [CPNull null]
|
||||
@"alternate-selected-control-color": [CPNull null],
|
||||
@"secondary-selected-control-color": [CPNull null],
|
||||
@"selected-text-background-color": [CPNull null],
|
||||
@"selected-text-inactive-background-color": [CPNull null],
|
||||
@"css-based": NO
|
||||
};
|
||||
}
|
||||
|
||||
@@ -498,6 +501,16 @@ var cachedBlackColor,
|
||||
return [[CPColor alloc] _initWithCSSString: aString];
|
||||
}
|
||||
|
||||
+ (CPColor)selectedTextBackgroundColor
|
||||
{
|
||||
return [[self _cachedThemeColor] valueForThemeAttribute:@"selected-text-background-color"] || [CPColor colorWithHexString:"99CCFF"];
|
||||
}
|
||||
|
||||
+ (CPColor)_selectedTextBackgroundColorUnfocussed
|
||||
{
|
||||
return [[self _cachedThemeColor] valueForThemeAttribute:@"selected-text-inactive-background-color"] || [CPColor colorWithHexString:"CCCCCC"];
|
||||
}
|
||||
|
||||
/* @ignore */
|
||||
- (id)_initWithCSSString:(CPString)aString
|
||||
{
|
||||
@@ -864,9 +877,221 @@ url("data:image/png;base64,BASE64ENCODEDDATA") // if there is a pattern image
|
||||
|
||||
@end
|
||||
|
||||
#pragma mark -
|
||||
#pragma mark CSS Theming
|
||||
|
||||
// The code below adds support for CSS theming with 100% compatibility with current theming system.
|
||||
// The idea is to extend CPColor (and CPImage) with CSS components and adapt low level UI components to
|
||||
// support this new kind of CPColor/CPImage. See CPImageView, CPView and _CPImageAndTextView.
|
||||
//
|
||||
// To create a CPColor that uses CSS, simply use the new class method :
|
||||
// + (CPColor)colorWithCSSDictionary:(CPDictionary)aDictionary beforeDictionary:(CPDictionary)beforeDictionary afterDictionary:(CPDictionary)afterDictionary
|
||||
// where beforeDictionary & afterDictionary are related to ::before & ::after pseudo-elements.
|
||||
// If you don't need them, just use the simplified class method :
|
||||
// + (CPColor)colorWithCSSDictionary:(CPDictionary)aDictionary
|
||||
//
|
||||
// Example :
|
||||
// buttonCssColor = [CPColor colorWithCSSDictionary:@{
|
||||
// @"background-color": A3ColorBackgroundWhite,
|
||||
// @"border-color": A3ColorActiveBorder,
|
||||
// @"border-style": @"solid",
|
||||
// @"border-width": @"1px",
|
||||
// @"border-radius": @"3px",
|
||||
// @"box-sizing": @"border-box"
|
||||
// }
|
||||
// beforeDictionary:@{
|
||||
// @"background-color": @"rgb(225,225,225)",
|
||||
// @"bottom": @"3px",
|
||||
// @"content": @"''",
|
||||
// @"position": @"absolute",
|
||||
// @"right": @"21px",
|
||||
// @"top": @"3px",
|
||||
// @"width": @"1px"
|
||||
// }
|
||||
// afterDictionary:@{
|
||||
// @"content": @"''",
|
||||
// @"bottom": @"3px",
|
||||
// @"right": @"6px",
|
||||
// @"top": @"1px",
|
||||
// @"position": @"absolute",
|
||||
// @"height": @"14px",
|
||||
// @"width": @"9px",
|
||||
// @"margin": @"2px 0px 2px 0px",
|
||||
// @"background-image": @"url(%%packed.png)",
|
||||
// @"background-position": @"0px -64px",
|
||||
// @"background-repeat": @"no-repeat",
|
||||
// @"background-size": @"100px 400px"
|
||||
// }];
|
||||
//
|
||||
// Remark : Please note the special URL of the background image used in this example : url(%%packed.png)
|
||||
// During theme loading, "%%" will be replaced by the path to the theme blend resources folder.
|
||||
// Typically, a CSS theme will use some (rare) images all packed together in a single image resource (see packed.png in Aristo3 theme)
|
||||
//
|
||||
// Also, please note that if you don't use one of the CSS components, you can either set it to nil (best solution) or to an empty dictionary, like :
|
||||
// aCssColor = [CPColor colorWithCSSDictionary:@{} beforeDictionary:nil afterDictionary:@{ ... }];
|
||||
//
|
||||
// You can use -(BOOL)isCSSBased to determine how to cope with it in your code.
|
||||
// -(BOOL)hasCSSDictionary, -(BOOL)hasCSSBeforeDictionary and -(BOOL)hasCSSAfterDictionary are convience methods you can use.
|
||||
//
|
||||
// Remark : -(void)restorePreviousCSSState and -(DOMElement)applyCSSColorForView are meant to be used by low level UI widgets (like CPView) to implement
|
||||
// CSS theme support.
|
||||
|
||||
@implementation CPColor (CSSTheming)
|
||||
{
|
||||
CPDictionary _cssDictionary @accessors(property=cssDictionary);
|
||||
CPDictionary _cssBeforeDictionary @accessors(property=cssBeforeDictionary);
|
||||
CPDictionary _cssAfterDictionary @accessors(property=cssAfterDictionary);
|
||||
}
|
||||
|
||||
+ (CPColor)colorWithCSSDictionary:(CPDictionary)aDictionary
|
||||
{
|
||||
return [[CPColor alloc] _initWithCSSDictionary:aDictionary beforeDictionary:nil afterDictionary:nil];
|
||||
}
|
||||
|
||||
+ (CPColor)colorWithCSSDictionary:(CPDictionary)aDictionary beforeDictionary:(CPDictionary)beforeDictionary afterDictionary:(CPDictionary)afterDictionary
|
||||
{
|
||||
return [[CPColor alloc] _initWithCSSDictionary:aDictionary beforeDictionary:beforeDictionary afterDictionary:afterDictionary];
|
||||
}
|
||||
|
||||
- (id)_initWithCSSDictionary:(CPDictionary)aDictionary beforeDictionary:(CPDictionary)beforeDictionary afterDictionary:(CPDictionary)afterDictionary
|
||||
{
|
||||
self = [super init];
|
||||
|
||||
if (self)
|
||||
{
|
||||
_cssDictionary = aDictionary;
|
||||
_cssBeforeDictionary = beforeDictionary;
|
||||
_cssAfterDictionary = afterDictionary;
|
||||
_components = [0.0, 0.0, 0.0, 1.0];
|
||||
|
||||
_theme = [CPTheme defaultTheme];
|
||||
_themeState = CPThemeStateNormal;
|
||||
[self _loadThemeAttributes];
|
||||
}
|
||||
|
||||
return self;
|
||||
}
|
||||
|
||||
- (BOOL)isCSSBased
|
||||
{
|
||||
return !!(_cssDictionary || _cssBeforeDictionary || _cssAfterDictionary);
|
||||
}
|
||||
|
||||
- (BOOL)hasCSSDictionary
|
||||
{
|
||||
return ([_cssDictionary count] > 0);
|
||||
}
|
||||
|
||||
- (BOOL)hasCSSBeforeDictionary
|
||||
{
|
||||
return ([_cssBeforeDictionary count] > 0);
|
||||
}
|
||||
|
||||
- (BOOL)hasCSSAfterDictionary
|
||||
{
|
||||
return ([_cssAfterDictionary count] > 0);
|
||||
}
|
||||
|
||||
- (void)restorePreviousCSSState:(CPArrayRef)aPreviousStateRef forDOMElement:(DOMElement)aDOMElement
|
||||
{
|
||||
#if PLATFORM(DOM)
|
||||
var aPreviousState = @deref(aPreviousStateRef);
|
||||
|
||||
for (var i = 0, count = aPreviousState.length; i < count; i++)
|
||||
aDOMElement.style[aPreviousState[i][0]] = aPreviousState[i][1];
|
||||
|
||||
@deref(aPreviousStateRef) = @[];
|
||||
#endif
|
||||
}
|
||||
|
||||
- (DOMElement)applyCSSColorForView:(CPView)aView onDOMElement:(DOMElement)aDOMElement styleNode:(DOMElement)aStyleNode previousState:(CPArrayRef)aPreviousStateRef
|
||||
{
|
||||
#if PLATFORM(DOM)
|
||||
var aPreviousState = @deref(aPreviousStateRef);
|
||||
|
||||
[_cssDictionary enumerateKeysAndObjectsUsingBlock:function(aKey, anObject, stop)
|
||||
{
|
||||
[aPreviousState addObject:@[aKey, aDOMElement.style[aKey]]];
|
||||
aDOMElement.style[aKey] = anObject;
|
||||
}];
|
||||
|
||||
if ([self hasCSSBeforeDictionary] || [self hasCSSAfterDictionary])
|
||||
{
|
||||
// We need to create a unique class name
|
||||
|
||||
var styleClassName = @".CP" + [aView UID],
|
||||
styleContent = @"";
|
||||
|
||||
if ([self hasCSSBeforeDictionary])
|
||||
{
|
||||
styleContent += styleClassName + @"::before { ";
|
||||
|
||||
[_cssBeforeDictionary enumerateKeysAndObjectsUsingBlock:function(aKey, anObject, stop)
|
||||
{
|
||||
styleContent += aKey + ": " + anObject + "; ";
|
||||
}];
|
||||
|
||||
styleContent += "} ";
|
||||
}
|
||||
|
||||
if ([self hasCSSAfterDictionary])
|
||||
{
|
||||
styleContent += styleClassName + @"::after { ";
|
||||
|
||||
[_cssAfterDictionary enumerateKeysAndObjectsUsingBlock:function(aKey, anObject, stop)
|
||||
{
|
||||
styleContent += aKey + ": " + anObject + "; ";
|
||||
}];
|
||||
|
||||
styleContent += "} ";
|
||||
}
|
||||
|
||||
var styleDescription = document.createTextNode(styleContent);
|
||||
|
||||
if (!aStyleNode)
|
||||
{
|
||||
aStyleNode = document.createElement("style");
|
||||
|
||||
aView._DOMElement.insertBefore(aStyleNode, aView._DOMElement.firstChild);
|
||||
|
||||
aStyleNode.appendChild(styleDescription);
|
||||
}
|
||||
else
|
||||
{
|
||||
aStyleNode.replaceChild(styleDescription, aStyleNode.firstChild);
|
||||
}
|
||||
|
||||
[aView setDOMClassName:@"CP"+[aView UID]];
|
||||
}
|
||||
else
|
||||
{
|
||||
// no before/after so remove aStyleNode if existing
|
||||
|
||||
if (aStyleNode)
|
||||
{
|
||||
aView._DOMElement.removeChild(aStyleNode);
|
||||
aStyleNode = nil;
|
||||
}
|
||||
}
|
||||
|
||||
// Return actualised values
|
||||
|
||||
@deref(aPreviousStateRef) = aPreviousState;
|
||||
|
||||
return aStyleNode;
|
||||
#endif
|
||||
}
|
||||
|
||||
@end
|
||||
|
||||
#pragma mark -
|
||||
|
||||
/// @cond IGNORE
|
||||
var CPColorComponentsKey = @"CPColorComponentsKey",
|
||||
CPColorPatternImageKey = @"CPColorPatternImageKey";
|
||||
var CPColorComponentsKey = @"CPColorComponentsKey",
|
||||
CPColorPatternImageKey = @"CPColorPatternImageKey",
|
||||
CPColorCssDictionaryKey = @"CPColorCssDictionaryKey",
|
||||
CPColorCssBeforeDictionaryKey = @"CPColorCssBeforeDictionaryKey",
|
||||
CPColorCssAfterDictionaryKey = @"CPColorCssAfterDictionaryKey";
|
||||
/// @endcond
|
||||
|
||||
@implementation CPColor (CPCoding)
|
||||
@@ -879,6 +1104,10 @@ var CPColorComponentsKey = @"CPColorComponentsKey",
|
||||
{
|
||||
if ([aCoder containsValueForKey:CPColorPatternImageKey])
|
||||
self = [self _initWithPatternImage:[aCoder decodeObjectForKey:CPColorPatternImageKey]];
|
||||
else if ([aCoder containsValueForKey:CPColorCssDictionaryKey])
|
||||
self = [self _initWithCSSDictionary:[aCoder decodeObjectForKey:CPColorCssDictionaryKey]
|
||||
beforeDictionary:[aCoder decodeObjectForKey:CPColorCssBeforeDictionaryKey]
|
||||
afterDictionary:[aCoder decodeObjectForKey:CPColorCssAfterDictionaryKey]];
|
||||
else
|
||||
self = [self _initWithRGBA:[aCoder decodeObjectForKey:CPColorComponentsKey]];
|
||||
|
||||
@@ -895,6 +1124,12 @@ var CPColorComponentsKey = @"CPColorComponentsKey",
|
||||
{
|
||||
if (_patternImage)
|
||||
[aCoder encodeObject:_patternImage forKey:CPColorPatternImageKey];
|
||||
else if (_cssDictionary)
|
||||
{
|
||||
[aCoder encodeObject:_cssDictionary forKey:CPColorCssDictionaryKey];
|
||||
[aCoder encodeObject:_cssBeforeDictionary forKey:CPColorCssBeforeDictionaryKey];
|
||||
[aCoder encodeObject:_cssAfterDictionary forKey:CPColorCssAfterDictionaryKey];
|
||||
}
|
||||
else
|
||||
[aCoder encodeObject:_components forKey:CPColorComponentsKey];
|
||||
|
||||
@@ -1235,7 +1470,8 @@ var patternColorsFromPattern = function(pattern, attributes, imageFactory)
|
||||
bottomHeight,
|
||||
centerWidthHeight,
|
||||
centerIsNil,
|
||||
numParts;
|
||||
numParts,
|
||||
isVertical;
|
||||
|
||||
// positions are mandatory
|
||||
if (pattern.indexOf("{position}") < 0)
|
||||
|
||||
@@ -483,13 +483,12 @@ var CPColorPanelSwatchesCookie = "CPColorPanelSwatchesCookie";
|
||||
];
|
||||
}
|
||||
|
||||
var cookieValue = eval(cookieValue),
|
||||
result = [];
|
||||
var cookieValue = eval(cookieValue);
|
||||
|
||||
for (var i = 0; i < cookieValue.length; i++)
|
||||
result.push([CPColor colorWithHexString:cookieValue[i]]);
|
||||
|
||||
return result;
|
||||
return [cookieValue arrayByApplyingBlock:function(value)
|
||||
{
|
||||
return [CPColor colorWithHexString:value];
|
||||
}];
|
||||
}
|
||||
|
||||
- (CPArray)saveColorList
|
||||
|
||||
@@ -125,7 +125,8 @@
|
||||
_brightnessSlider = [[CPSlider alloc] initWithFrame:CGRectMake(0, (aFrame.size.height - 34), aFrame.size.width, 15)];
|
||||
|
||||
[_brightnessSlider setValue:15.0 forThemeAttribute:@"track-width"];
|
||||
[_brightnessSlider setValue:[CPColor colorWithPatternImage:[[CPImage alloc] initWithContentsOfFile:[[CPBundle bundleForClass:[CPColorPicker class]] pathForResource:@"brightness_bar.png"]]] forThemeAttribute:@"track-color"];
|
||||
var brightnessImage = [[CPImage alloc] initWithContentsOfFile:[[CPBundle bundleForClass:[CPColorPicker class]] pathForResource:@"brightness_bar.png"] size:CGSizeMake(272, 20)];
|
||||
[_brightnessSlider setValue:[CPColor colorWithPatternImage:brightnessImage] forThemeAttribute:@"track-color"];
|
||||
|
||||
[_brightnessSlider setMinValue:0.0];
|
||||
[_brightnessSlider setMaxValue:100.0];
|
||||
@@ -246,7 +247,7 @@
|
||||
_blackWheelImage = new Image();
|
||||
_blackWheelImage.src = path;
|
||||
_blackWheelImage.style.opacity = "0";
|
||||
_blackWheelImage.style.filter = "alpha(opacity=0)"
|
||||
_blackWheelImage.style.filter = "alpha(opacity=0)";
|
||||
_blackWheelImage.style.position = "absolute";
|
||||
|
||||
_DOMElement.appendChild(_wheelImage);
|
||||
|
||||
@@ -255,6 +255,8 @@ var _CPColorWellDidBecomeExclusiveNotification = @"_CPColorWellDidBecomeExclusiv
|
||||
|
||||
var colorPanel = [CPColorPanel sharedColorPanel];
|
||||
|
||||
[colorPanel setPlatformWindow:[[self window] platformWindow]];
|
||||
|
||||
[colorPanel setColor:_color];
|
||||
[colorPanel orderFront:self];
|
||||
}
|
||||
|
||||
+2
-4
@@ -1142,11 +1142,9 @@ var CPComboBoxTextSubview = @"text",
|
||||
// Directly nuke _items, [_items removeAll] will trigger an extra call to setContent
|
||||
_items = [];
|
||||
|
||||
var values = [];
|
||||
|
||||
[anArray enumerateObjectsUsingBlock:function(object)
|
||||
var values = [anArray arrayByApplyingBlock:function(object)
|
||||
{
|
||||
values.push([object description]);
|
||||
return [object description];
|
||||
}];
|
||||
|
||||
[self addItemsWithObjectValues:values];
|
||||
|
||||
+49
-16
@@ -26,11 +26,12 @@
|
||||
|
||||
// Browser Engines
|
||||
CPUnknownBrowserEngine = 0;
|
||||
CPGeckoBrowserEngine = 1;
|
||||
CPInternetExplorerBrowserEngine = 2;
|
||||
CPKHTMLBrowserEngine = 3;
|
||||
CPOperaBrowserEngine = 4;
|
||||
CPWebKitBrowserEngine = 5;
|
||||
CPGeckoBrowserEngine = 1 << 0;
|
||||
CPInternetExplorerBrowserEngine = 1 << 1;
|
||||
CPKHTMLBrowserEngine = 1 << 2;
|
||||
CPOperaBrowserEngine = 1 << 3;
|
||||
CPWebKitBrowserEngine = 1 << 4; // Safari + Chrome
|
||||
CPBlinkBrowserEngine = 1 << 5; // Recent Chrome
|
||||
|
||||
// Operating Systems
|
||||
CPMacOperatingSystem = 0;
|
||||
@@ -80,18 +81,27 @@ CPFileAPIFeature = 31;
|
||||
|
||||
CPAltEnterTextAreaFeature = 32;
|
||||
|
||||
|
||||
CPCSSAnimationFeature = 33;
|
||||
|
||||
/*
|
||||
When an absolutely positioned div (CPView) with an absolutely positioned canvas in it (CPView with drawRect:) moves things on top of the canvas (subviews) don't redraw correctly. E.g. if you have a bunch of text fields in a CPBox in a sheet which animates in, some of the text fields might not be visible because the CPBox has a canvas at the bottom and the box moved form offscreen to onscreen. This bug is probably very related: https://bugs.webkit.org/show_bug.cgi?id=67203
|
||||
*/
|
||||
*/
|
||||
CPCanvasParentDrawErrorsOnMovementBug = 1 << 0;
|
||||
|
||||
// The paste event is only sent if an input or textarea has focus.
|
||||
CPJavaScriptPasteRequiresEditableTarget = 1 << 1;
|
||||
CPJavaScriptPasteRequiresEditableTarget = 1 << 1;
|
||||
// Redirecting the focus of the browser on keydown to an input for Cmd-V or Ctrl-V makes the paste fail.
|
||||
CPJavaScriptPasteCantRefocus = 1 << 2;
|
||||
|
||||
/*
|
||||
Safari calculates incorrect text size unless you set the canvas font even if it is already set
|
||||
You can see the bug after disabling the workaround and opening any panel while typing.
|
||||
You can use the font panel in the manual test for CPTextView.
|
||||
Look out for a displaced cursor, i.e. after typing letters of small width, such as the 'i'.
|
||||
https://bugs.webkit.org/show_bug.cgi?id=150224
|
||||
*/
|
||||
CPTextSizingAlwaysNeedsSetFontBug = 1 << 3;
|
||||
|
||||
|
||||
var USER_AGENT = "",
|
||||
PLATFORM_ENGINE = CPUnknownBrowserEngine,
|
||||
@@ -110,15 +120,15 @@ if (typeof window !== "undefined" && typeof window.navigator !== "undefined")
|
||||
// Opera
|
||||
if (typeof window !== "undefined" && window.opera)
|
||||
{
|
||||
PLATFORM_ENGINE = CPOperaBrowserEngine;
|
||||
PLATFORM_ENGINE |= CPOperaBrowserEngine;
|
||||
|
||||
PLATFORM_FEATURES[CPJavaScriptCanvasDrawFeature] = YES;
|
||||
}
|
||||
|
||||
// Internet Explorer
|
||||
else if (typeof window !== "undefined" && window.attachEvent) // Must follow Opera check.
|
||||
else if (typeof window !== "undefined" && (window.attachEvent || (!(window.ActiveXObject) && "ActiveXObject" in window))) // Must follow Opera check.
|
||||
{
|
||||
PLATFORM_ENGINE = CPInternetExplorerBrowserEngine;
|
||||
PLATFORM_ENGINE |= CPInternetExplorerBrowserEngine;
|
||||
|
||||
// Features we can only be sure of with IE (no known independent tests)
|
||||
PLATFORM_FEATURES[CPVMLFeature] = YES;
|
||||
@@ -136,10 +146,10 @@ else if (typeof window !== "undefined" && window.attachEvent) // Must follow Ope
|
||||
PLATFORM_FEATURES[CPJavaScriptClipboardAccessFeature] = YES;
|
||||
}
|
||||
|
||||
// WebKit
|
||||
// Safari + Chrome (WebKit and Blink)
|
||||
else if (USER_AGENT.indexOf("AppleWebKit/") != -1)
|
||||
{
|
||||
PLATFORM_ENGINE = CPWebKitBrowserEngine;
|
||||
PLATFORM_ENGINE |= CPWebKitBrowserEngine;
|
||||
|
||||
// Features we can only be sure of with WebKit (no known independent tests)
|
||||
PLATFORM_FEATURES[CPCSSRGBAFeature] = YES;
|
||||
@@ -179,7 +189,10 @@ else if (USER_AGENT.indexOf("AppleWebKit/") != -1)
|
||||
PLATFORM_BUGS |= CPJavaScriptPasteRequiresEditableTarget;
|
||||
// https://bugs.webkit.org/show_bug.cgi?id=39689
|
||||
PLATFORM_BUGS |= CPJavaScriptPasteCantRefocus;
|
||||
PLATFORM_BUGS |= CPTextSizingAlwaysNeedsSetFontBug;
|
||||
}
|
||||
else if ((window.chrome || (window.Intl && Intl.v8BreakIterator)) && 'CSS' in window)
|
||||
PLATFORM_ENGINE |= CPBlinkBrowserEngine;
|
||||
|
||||
// Assume this bug was introduced around Safari 5.1/Chrome 16. This could probably be tighter.
|
||||
if (majorVersion > 533)
|
||||
@@ -189,13 +202,13 @@ else if (USER_AGENT.indexOf("AppleWebKit/") != -1)
|
||||
// KHTML
|
||||
else if (USER_AGENT.indexOf("KHTML") != -1) // Must follow WebKit check.
|
||||
{
|
||||
PLATFORM_ENGINE = CPKHTMLBrowserEngine;
|
||||
PLATFORM_ENGINE |= CPKHTMLBrowserEngine;
|
||||
}
|
||||
|
||||
// Gecko
|
||||
else if (USER_AGENT.indexOf("Gecko") !== -1) // Must follow KHTML check.
|
||||
{
|
||||
PLATFORM_ENGINE = CPGeckoBrowserEngine;
|
||||
PLATFORM_ENGINE |= CPGeckoBrowserEngine;
|
||||
|
||||
PLATFORM_FEATURES[CPJavaScriptCanvasDrawFeature] = YES;
|
||||
|
||||
@@ -286,7 +299,7 @@ function CPPlatformHasBug(aBug)
|
||||
|
||||
function CPBrowserIsEngine(anEngine)
|
||||
{
|
||||
return PLATFORM_ENGINE === anEngine;
|
||||
return PLATFORM_ENGINE & anEngine;
|
||||
}
|
||||
|
||||
function CPBrowserIsOperatingSystem(anOperatingSystem)
|
||||
@@ -322,6 +335,14 @@ else
|
||||
CPRedoKeyEquivalentModifierMask = CPControlKeyMask;
|
||||
}
|
||||
|
||||
/*!
|
||||
Sets a feature with the given value.
|
||||
*/
|
||||
function CPSetPlatformFeature(aFeature, aBool)
|
||||
{
|
||||
PLATFORM_FEATURES[aFeature] = aBool;
|
||||
}
|
||||
|
||||
/*!
|
||||
Return the properly prefixed JS property for the given name. E.g. in a webkit browser,
|
||||
CPBrowserStyleProperty('transition') -> WebkitTransition
|
||||
@@ -369,6 +390,18 @@ function CPBrowserStyleProperty(aProperty)
|
||||
r = candidates[PLATFORM_STYLE_JS_PROPERTIES['transform']] || nil;
|
||||
break;
|
||||
|
||||
case 'animationend':
|
||||
var candidates = {
|
||||
'WebkitAnimation' : 'webkitAnimationEnd',
|
||||
'MozAnimation' : 'animationend',
|
||||
'OAnimation' : 'oAnimationEnd',
|
||||
'msAnimation' : 'MSAnimationEnd',
|
||||
'animation' : 'animationend'
|
||||
};
|
||||
|
||||
r = candidates[PLATFORM_STYLE_JS_PROPERTIES['animation']] || nil;
|
||||
break;
|
||||
|
||||
default:
|
||||
var prefixes = ["Webkit", "Moz", "O", "ms"],
|
||||
strippedProperty = aProperty.split('-').join(' '),
|
||||
|
||||
+14
-7
@@ -25,7 +25,7 @@
|
||||
|
||||
@import "CPFont.j"
|
||||
@import "CPShadow.j"
|
||||
@import "CPView.j"
|
||||
@import "CPText.j"
|
||||
@import "CPKeyValueBinding.j"
|
||||
@import "CPTrackingArea.j"
|
||||
|
||||
@@ -169,7 +169,7 @@ var CPControlBlackColor = [CPColor blackColor];
|
||||
}
|
||||
|
||||
/*!
|
||||
Reverse set the binding iff the CPContinuouslyUpdatesValueBindingOption is set.
|
||||
Reverse set the binding if the CPContinuouslyUpdatesValueBindingOption is set.
|
||||
*/
|
||||
- (void)_continuouslyReverseSetBinding
|
||||
{
|
||||
@@ -1057,14 +1057,21 @@ var CPControlBlackColor = [CPColor blackColor];
|
||||
@end
|
||||
|
||||
@implementation CPControl (CPTrackingArea)
|
||||
{
|
||||
CPTrackingArea _controlTrackingArea;
|
||||
}
|
||||
|
||||
- (void)updateTrackingAreas
|
||||
{
|
||||
[self removeAllTrackingAreas];
|
||||
[self addTrackingArea:[[CPTrackingArea alloc] initWithRect:CGRectMakeZero()
|
||||
options:CPTrackingMouseEnteredAndExited | CPTrackingActiveInKeyWindow | CPTrackingInVisibleRect
|
||||
owner:self
|
||||
userInfo:nil]];
|
||||
if (_controlTrackingArea)
|
||||
[self removeTrackingArea:_controlTrackingArea];
|
||||
|
||||
_controlTrackingArea = [[CPTrackingArea alloc] initWithRect:CGRectMakeZero()
|
||||
options:CPTrackingMouseEnteredAndExited | CPTrackingActiveInKeyWindow | CPTrackingInVisibleRect
|
||||
owner:self
|
||||
userInfo:nil];
|
||||
[self addTrackingArea:_controlTrackingArea];
|
||||
[super updateTrackingAreas];
|
||||
}
|
||||
|
||||
@end
|
||||
|
||||
@@ -352,8 +352,8 @@ CPEraDatePickerElementFlag = 0x0100;
|
||||
if (_implementedCDatePickerDelegateMethods & CPDatePicker_validateProposedDateValue_timeInterval)
|
||||
{
|
||||
// constrain timeInterval also
|
||||
var aStartDateRef = function(x){if (typeof x == 'undefined') return aDateValue; aDateValue = x;}
|
||||
var aTimeIntervalRef = function(x){if (typeof x == 'undefined') return aTimeInterval; aTimeInterval = x;}
|
||||
var aStartDateRef = function(x){if (typeof x == 'undefined') return aDateValue; aDateValue = x;};
|
||||
var aTimeIntervalRef = function(x){if (typeof x == 'undefined') return aTimeInterval; aTimeInterval = x;};
|
||||
|
||||
[_delegate datePicker:self validateProposedDateValue:aStartDateRef timeInterval:aTimeIntervalRef];
|
||||
}
|
||||
@@ -694,7 +694,7 @@ var CPDatePickerModeKey = @"CPDatePickerModeKey",
|
||||
[aCoder encodeInt:_datePickerStyle forKey:CPDatePickerStyleKey];
|
||||
[aCoder encodeInt:_datePickerElements forKey:CPDatePickerElementsKey];
|
||||
[aCoder encodeObject:_minDate forKey:CPMinDateKey];
|
||||
[aCoder encodeObject:_maxDate forKey:CPMaxDateKey]
|
||||
[aCoder encodeObject:_maxDate forKey:CPMaxDateKey];
|
||||
[aCoder encodeObject:_dateValue forKey:CPDateValueKey];;
|
||||
[aCoder encodeObject:_textFont forKey:CPTextFontKey];
|
||||
[aCoder encodeObject:_locale forKey:CPLocaleKey];
|
||||
|
||||
@@ -85,7 +85,7 @@ var CPShortWeekDayNameArrayEn = [@"Mo", @"Tu", @"We", @"Th", @"Fr", @"Sa", @"Su"
|
||||
{
|
||||
if (self = [super initWithFrame:aFrame])
|
||||
{
|
||||
_datePicker = aDatePicker
|
||||
_datePicker = aDatePicker;
|
||||
[self _init];
|
||||
}
|
||||
|
||||
@@ -435,7 +435,7 @@ var CPShortWeekDayNameArrayEn = [@"Mo", @"Tu", @"We", @"Th", @"Fr", @"Sa", @"Su"
|
||||
break;
|
||||
|
||||
default:
|
||||
return CPShortWeekDayNameArrayEn
|
||||
return CPShortWeekDayNameArrayEn;
|
||||
break;
|
||||
}
|
||||
}
|
||||
@@ -464,7 +464,7 @@ var CPShortWeekDayNameArrayEn = [@"Mo", @"Tu", @"We", @"Th", @"Fr", @"Sa", @"Su"
|
||||
break;
|
||||
|
||||
default:
|
||||
return CPShortMonthNameArrayEn
|
||||
return CPShortMonthNameArrayEn;
|
||||
break;
|
||||
}
|
||||
|
||||
@@ -526,11 +526,11 @@ var CPShortWeekDayNameArrayEn = [@"Mo", @"Tu", @"We", @"Th", @"Fr", @"Sa", @"Su"
|
||||
var dayLabel = _dayLabels[i];
|
||||
|
||||
[dayLabel setStringValue:dayNames[i]];
|
||||
[dayLabel sizeToFit]
|
||||
[dayLabel sizeToFit];
|
||||
[dayLabel setFrameOrigin:CGPointMake(sizeTileWidth * (i + 1) - sizeTileWidth / 2 - [dayLabel frameSize].width / 2, 23)];
|
||||
|
||||
if (i == 0)
|
||||
firstDayTileX = sizeTileWidth * (i + 1) - sizeTileWidth / 2 - [dayLabel frameSize].width / 2
|
||||
firstDayTileX = sizeTileWidth * (i + 1) - sizeTileWidth / 2 - [dayLabel frameSize].width / 2;
|
||||
}
|
||||
|
||||
// Title
|
||||
@@ -769,7 +769,7 @@ var CPShortWeekDayNameArrayEn = [@"Mo", @"Tu", @"We", @"Th", @"Fr", @"Sa", @"Su"
|
||||
tileDate = [[tile date] copy],
|
||||
selected = NO;
|
||||
|
||||
[tileDate _resetToMidnight]
|
||||
[tileDate _resetToMidnight];
|
||||
|
||||
if (aStartDate)
|
||||
selected = tileDate >= aStartDate && tileDate <= endDate;
|
||||
@@ -845,7 +845,7 @@ var CPShortWeekDayNameArrayEn = [@"Mo", @"Tu", @"We", @"Th", @"Fr", @"Sa", @"Su"
|
||||
|
||||
// Very usefull to avoid to have a line of two pixels instead one
|
||||
if (!isBorderPair)
|
||||
y += 0.5
|
||||
y += 0.5;
|
||||
|
||||
CGContextMoveToPoint(context, 0, y);
|
||||
CGContextAddLineToPoint(context, [self bounds].size.width, y);
|
||||
@@ -857,7 +857,7 @@ var CPShortWeekDayNameArrayEn = [@"Mo", @"Tu", @"We", @"Th", @"Fr", @"Sa", @"Su"
|
||||
|
||||
// Very usefull to avoid to have a line of two pixels instead one
|
||||
if (!isBorderPair)
|
||||
x += 0.5
|
||||
x += 0.5;
|
||||
|
||||
CGContextMoveToPoint(context, x, 0);
|
||||
CGContextAddLineToPoint(context, x, [self bounds].size.height);
|
||||
@@ -869,7 +869,7 @@ var CPShortWeekDayNameArrayEn = [@"Mo", @"Tu", @"We", @"Th", @"Fr", @"Sa", @"Su"
|
||||
|
||||
// Very usefull to avoid to have a line of two pixels instead one
|
||||
if (!isBorderPair)
|
||||
y += 0.5
|
||||
y += 0.5;
|
||||
|
||||
CGContextMoveToPoint(context, 0, y);
|
||||
CGContextAddLineToPoint(context, [self bounds].size.width, y);
|
||||
@@ -897,7 +897,7 @@ var CPShortWeekDayNameArrayEn = [@"Mo", @"Tu", @"We", @"Th", @"Fr", @"Sa", @"Su"
|
||||
_clickDate = [dateTile copy];
|
||||
_dragDate = nil;
|
||||
_indexDayTile = -1;
|
||||
_eventDragged = nil
|
||||
_eventDragged = nil;
|
||||
|
||||
_datePicker._invokedByUserEvent = YES;
|
||||
|
||||
|
||||
@@ -142,7 +142,7 @@ var RADIANS = Math.PI / 180;
|
||||
if ([_datePicker _isAmericanFormat])
|
||||
{
|
||||
if (dateValue.getHours() > 11)
|
||||
[_PMAMTextField setStringValue:@"PM"]
|
||||
[_PMAMTextField setStringValue:@"PM"];
|
||||
else
|
||||
[_PMAMTextField setStringValue:@"AM"];
|
||||
|
||||
|
||||
@@ -80,7 +80,7 @@ var CPZeroKeyCode = 48,
|
||||
{
|
||||
if (self = [super initWithFrame:aFrame])
|
||||
{
|
||||
_datePicker = aDatePicker
|
||||
_datePicker = aDatePicker;
|
||||
[self _init];
|
||||
}
|
||||
return self;
|
||||
|
||||
+18
-8
@@ -28,7 +28,6 @@
|
||||
|
||||
@import "CPCompatibility.j"
|
||||
@import "CGGeometry.j"
|
||||
@import "CPText.j"
|
||||
@import "CPTrackingArea.j"
|
||||
|
||||
@class CPTextField
|
||||
@@ -36,6 +35,9 @@
|
||||
@class CPGraphicsContext
|
||||
|
||||
@global CPApp
|
||||
@global CPNewlineCharacter
|
||||
@global CPCarriageReturnCharacter
|
||||
@global CPEnterCharacter
|
||||
|
||||
@typedef DOMEvent
|
||||
@typedef CPEventType
|
||||
@@ -64,7 +66,7 @@ var _CPEventPeriodicEventPeriod = 0,
|
||||
CPWindow _window;
|
||||
Number _windowNumber;
|
||||
CPString _characters;
|
||||
CPString _charactersIgnoringModifiers
|
||||
CPString _charactersIgnoringModifiers;
|
||||
BOOL _isARepeat;
|
||||
unsigned _keyCode;
|
||||
DOMEvent _DOMEvent;
|
||||
@@ -83,7 +85,7 @@ var _CPEventPeriodicEventPeriod = 0,
|
||||
BOOL _suppressCappuccinoCut;
|
||||
BOOL _suppressCappuccinoPaste;
|
||||
#endif
|
||||
|
||||
|
||||
CPTrackingArea _trackingArea;
|
||||
}
|
||||
|
||||
@@ -146,7 +148,7 @@ var _CPEventPeriodicEventPeriod = 0,
|
||||
|
||||
/*!
|
||||
Creates a new mouse tracking event.
|
||||
|
||||
|
||||
@param anEventType the event type
|
||||
@param aPoint the location of the cursor in the window specified by \c aWindowNumber
|
||||
@param modifierFlags a bitwise combination of the modifiers specified in the CPEvent globals
|
||||
@@ -232,7 +234,7 @@ var _CPEventPeriodicEventPeriod = 0,
|
||||
{
|
||||
if ((anEventType != CPMouseEntered) && (anEventType != CPMouseExited) && (anEventType != CPCursorUpdate))
|
||||
[CPException raise:CPInternalInconsistencyException reason:"Invalid event type"];
|
||||
|
||||
|
||||
if (self = [self _initWithType:anEventType])
|
||||
{
|
||||
_location = CGPointCreateCopy(aPoint);
|
||||
@@ -243,7 +245,7 @@ var _CPEventPeriodicEventPeriod = 0,
|
||||
_trackingArea = aTrackingArea;
|
||||
_window = [CPApp windowWithWindowNumber:aWindowNumber];
|
||||
}
|
||||
|
||||
|
||||
return self;
|
||||
}
|
||||
|
||||
@@ -336,6 +338,14 @@ var _CPEventPeriodicEventPeriod = 0,
|
||||
return _type;
|
||||
}
|
||||
|
||||
/*!
|
||||
Returns the subtype of the event.
|
||||
*/
|
||||
- (CPEventType)subtype
|
||||
{
|
||||
return _subtype;
|
||||
}
|
||||
|
||||
/*!
|
||||
Returns the event's associated window.
|
||||
*/
|
||||
@@ -632,8 +642,8 @@ var _CPEventPeriodicEventPeriod = 0,
|
||||
- (CPTrackingArea)trackingArea
|
||||
{
|
||||
if ((_type !== CPMouseEntered) && (_type !== CPMouseExited) && (_type !== CPCursorUpdate))
|
||||
[CPException raise:CPInternalInconsistencyException format:@"You can't call trackingArea for events of type %#x", _type]
|
||||
|
||||
[CPException raise:CPInternalInconsistencyException format:@"You can't call trackingArea for events of type %#x", _type];
|
||||
|
||||
return _trackingArea;
|
||||
}
|
||||
|
||||
|
||||
+39
-1
@@ -24,6 +24,7 @@
|
||||
@import <Foundation/CPBundle.j>
|
||||
|
||||
@import "CPView.j"
|
||||
@import "CPFontDescriptor.j"
|
||||
|
||||
CPFontDefaultSystemFontFace = @"Arial, sans-serif";
|
||||
CPFontDefaultSystemFontSize = 12;
|
||||
@@ -162,7 +163,7 @@ following:
|
||||
if (normalizedFaces === _CPFontSystemFontFace)
|
||||
return;
|
||||
|
||||
[self _invalidateSystemFontCache]
|
||||
[self _invalidateSystemFontCache];
|
||||
_CPFontSystemFontFace = aFace;
|
||||
}
|
||||
|
||||
@@ -433,6 +434,43 @@ following:
|
||||
|
||||
@end
|
||||
|
||||
@implementation CPFont(DescriptorAdditions)
|
||||
|
||||
- (id)_initWithFontDescriptor:(CPFontDescriptor)fontDescriptor
|
||||
{
|
||||
var aName = [fontDescriptor objectForKey: CPFontNameAttribute] ,
|
||||
aSize = [fontDescriptor pointSize],
|
||||
isBold = [fontDescriptor symbolicTraits] & CPFontBoldTrait,
|
||||
isItalic = [fontDescriptor symbolicTraits] & CPFontItalicTrait;
|
||||
|
||||
return [self _initWithName:aName size:aSize bold:isBold italic:isItalic system:NO];
|
||||
}
|
||||
|
||||
+ (CPFont)fontWithDescriptor:(CPFontDescriptor)fontDescriptor size:(float)aSize
|
||||
{
|
||||
var aName = [fontDescriptor objectForKey: CPFontNameAttribute],
|
||||
isBold = [fontDescriptor symbolicTraits] & CPFontBoldTrait,
|
||||
isItalic = [fontDescriptor symbolicTraits] & CPFontItalicTrait;
|
||||
|
||||
return [self _fontWithName:aName size:aSize || [fontDescriptor pointSize] bold:isBold italic:isItalic];
|
||||
}
|
||||
|
||||
- (CPFontDescriptor)fontDescriptor
|
||||
{
|
||||
var traits = 0;
|
||||
|
||||
if ([self isBold])
|
||||
traits |= CPFontBoldTrait;
|
||||
|
||||
if ([self isItalic])
|
||||
traits |= CPFontItalicTrait;
|
||||
|
||||
return [[CPFontDescriptor fontDescriptorWithName:_name size:_size] fontDescriptorWithSymbolicTraits:traits];
|
||||
}
|
||||
|
||||
@end
|
||||
|
||||
|
||||
var CPFontNameKey = @"CPFontNameKey",
|
||||
CPFontSizeKey = @"CPFontSizeKey",
|
||||
CPFontIsBoldKey = @"CPFontIsBoldKey",
|
||||
|
||||
+208
-1
@@ -22,9 +22,12 @@
|
||||
|
||||
@import <Foundation/CPObject.j>
|
||||
|
||||
@import "CPControl.j"
|
||||
@import "CPFont.j"
|
||||
@import "CPFontDescriptor.j"
|
||||
|
||||
@global CPApp
|
||||
@class CPFontPanel
|
||||
|
||||
CPItalicFontMask = 1 << 0;
|
||||
CPBoldFontMask = 1 << 1;
|
||||
@@ -41,7 +44,20 @@ CPUnitalicFontMask = 1 << 24;
|
||||
|
||||
|
||||
var CPSharedFontManager = nil,
|
||||
CPFontManagerFactory = Nil;
|
||||
CPFontManagerFactory = nil,
|
||||
CPFontPanelFactory = nil;
|
||||
|
||||
/*
|
||||
modifyFont: sender's tag
|
||||
*/
|
||||
CPNoFontChangeAction = 0;
|
||||
CPViaPanelFontAction = 1;
|
||||
CPAddTraitFontAction = 2;
|
||||
CPSizeUpFontAction = 3;
|
||||
CPSizeDownFontAction = 4;
|
||||
CPHeavierFontAction = 5;
|
||||
CPLighterFontAction = 6;
|
||||
CPRemoveTraitFontAction = 7;
|
||||
|
||||
/*!
|
||||
@ingroup appkit
|
||||
@@ -59,6 +75,8 @@ var CPSharedFontManager = nil,
|
||||
BOOL _multiple @accessors(getter=isMultiple, setter=setMultiple:);
|
||||
|
||||
CPDictionary _activeChange;
|
||||
|
||||
unsigned _fontAction;
|
||||
}
|
||||
|
||||
// Getting the Shared Font Manager
|
||||
@@ -83,6 +101,15 @@ var CPSharedFontManager = nil,
|
||||
{
|
||||
CPFontManagerFactory = aClass;
|
||||
}
|
||||
/*!
|
||||
Sets the class that will be used to create the application's
|
||||
Font panel.
|
||||
*/
|
||||
+ (void)setFontPanelFactory:(Class)aClass
|
||||
{
|
||||
CPFontPanelFactory = aClass;
|
||||
}
|
||||
|
||||
|
||||
- (id)init
|
||||
{
|
||||
@@ -210,6 +237,7 @@ var CPSharedFontManager = nil,
|
||||
{
|
||||
var tag = [sender tag];
|
||||
_activeChange = tag === nil ? @{} : @{ @"addTraits": tag };
|
||||
_fontAction = CPAddTraitFontAction;
|
||||
|
||||
[self sendAction];
|
||||
}
|
||||
@@ -219,6 +247,185 @@ var CPSharedFontManager = nil,
|
||||
return [CPApp sendAction:_action to:_target from:self];
|
||||
}
|
||||
|
||||
|
||||
/*!
|
||||
This method open the font panel, create it if necessary.
|
||||
@param sender The object that sent the message.
|
||||
*/
|
||||
- (CPFontPanel)fontPanel:(BOOL)createIt
|
||||
{
|
||||
var panel = nil,
|
||||
panelExists = [CPFontPanelFactory sharedFontPanelExists];
|
||||
|
||||
if ((panelExists) || (!panelExists && createIt))
|
||||
panel = [CPFontPanelFactory sharedFontPanel];
|
||||
|
||||
return panel;
|
||||
}
|
||||
|
||||
/*!
|
||||
Convert a font to have the specified Font traits. The font is unchanged expect for the specified Font traits.
|
||||
Using CPUnboldFontMask or CPUnitalicFontMask will respectively remove Bold and Italic traits.
|
||||
@param aFont The font to convert.
|
||||
@param fontTrait The new font traits mask.
|
||||
@result The converted font or \c aFont if the conversion failed.
|
||||
*/
|
||||
- (CPFont)convertFont:(CPFont)aFont toHaveTrait:(CPFontTraitMask)fontTrait
|
||||
{
|
||||
var attributes = [[[aFont fontDescriptor] fontAttributes] copy],
|
||||
symbolicTrait = [[aFont fontDescriptor] symbolicTraits];
|
||||
|
||||
if (fontTrait & CPBoldFontMask)
|
||||
symbolicTrait |= CPFontBoldTrait;
|
||||
|
||||
if (fontTrait & CPItalicFontMask)
|
||||
symbolicTrait |= CPFontItalicTrait;
|
||||
|
||||
if (fontTrait & CPUnboldFontMask) /* FIXME: this only change CPFontSymbolicTrait what about CPFontWeightTrait */
|
||||
symbolicTrait &= ~CPFontBoldTrait;
|
||||
|
||||
if (fontTrait & CPUnitalicFontMask)
|
||||
symbolicTrait &= ~CPFontItalicTrait;
|
||||
|
||||
if (fontTrait & CPExpandedFontMask)
|
||||
symbolicTrait |= CPFontExpandedTrait;
|
||||
|
||||
if (fontTrait & CPSmallCapsFontMask)
|
||||
symbolicTrait |= CPFontSmallCapsTrait;
|
||||
|
||||
if (![attributes containsKey:CPFontTraitsAttribute])
|
||||
[attributes setObject:[CPDictionary dictionaryWithObject:[CPNumber numberWithUnsignedInt:symbolicTrait]
|
||||
forKey:CPFontSymbolicTrait]
|
||||
forKey:CPFontTraitsAttribute];
|
||||
else
|
||||
[[attributes objectForKey:CPFontTraitsAttribute] setObject:[CPNumber numberWithUnsignedInt:symbolicTrait]
|
||||
forKey:CPFontSymbolicTrait];
|
||||
|
||||
return [[aFont class] fontWithDescriptor:[CPFontDescriptor fontDescriptorWithFontAttributes:attributes] size:0.0];
|
||||
}
|
||||
|
||||
/*!
|
||||
Convert a font to not have the specified Font traits. The font is unchanged expect for the specified Font traits.
|
||||
@param aFont The font to convert.
|
||||
@param fontTrait The font traits mask to remove.
|
||||
@result The converted font or \c aFont if the conversion failed.
|
||||
*/
|
||||
- (CPFont)convertFont:(CPFont)aFont toNotHaveTrait:(CPFontTraitMask)fontTrait
|
||||
{
|
||||
var attributes = [[[aFont fontDescriptor] fontAttributes] copy],
|
||||
symbolicTrait = [[aFont fontDescriptor] symbolicTraits];
|
||||
|
||||
if ((fontTrait & CPBoldFontMask) || (fontTrait & CPUnboldFontMask)) /* FIXME: see convertFont:toHaveTrait: about CPFontWeightTrait */
|
||||
symbolicTrait &= ~CPFontBoldTrait;
|
||||
|
||||
if ((fontTrait & CPItalicFontMask) || (fontTrait & CPUnitalicFontMask))
|
||||
symbolicTrait &= ~CPFontItalicTrait;
|
||||
|
||||
if (fontTrait & CPExpandedFontMask)
|
||||
symbolicTrait &= ~CPFontExpandedTrait;
|
||||
|
||||
if (fontTrait & CPSmallCapsFontMask)
|
||||
symbolicTrait &= ~CPFontSmallCapsTrait;
|
||||
|
||||
if (![attributes containsKey:CPFontTraitsAttribute])
|
||||
[attributes setObject:[CPDictionary dictionaryWithObject:[CPNumber numberWithUnsignedInt:symbolicTrait]
|
||||
forKey:CPFontSymbolicTrait]
|
||||
forKey:CPFontTraitsAttribute];
|
||||
else
|
||||
[[attributes objectForKey:CPFontTraitsAttribute] setObject:[CPNumber numberWithUnsignedInt:symbolicTrait]
|
||||
forKey:CPFontSymbolicTrait];
|
||||
|
||||
return [[aFont class] fontWithDescriptor:[CPFontDescriptor fontDescriptorWithFontAttributes:attributes] size:0.0];
|
||||
}
|
||||
|
||||
/*!
|
||||
Convert a font to have specified size. The font is unchanged expect for the specified size.
|
||||
@param aFont The font to convert.
|
||||
@param aSize The new font size.
|
||||
@result The converted font or \c aFont if the conversion failed.
|
||||
*/
|
||||
- (CPFont)convertFont:(CPFont)aFont toSize:(float)aSize
|
||||
{
|
||||
var descriptor = [aFont fontDescriptor];
|
||||
|
||||
return [[aFont class] fontWithDescriptor: descriptor size:aSize]
|
||||
}
|
||||
|
||||
- (void)orderFrontFontPanel:(id)sender
|
||||
{
|
||||
[[self fontPanel:YES] orderFront:sender];
|
||||
}
|
||||
|
||||
- (void)modifyFont:(id)sender
|
||||
{
|
||||
_fontAction = [sender tag];
|
||||
[self sendAction];
|
||||
|
||||
if (_selectedFont)
|
||||
[self setSelectedFont:[self convertFont:_selectedFont] isMultiple:NO];
|
||||
}
|
||||
|
||||
/*!
|
||||
This method causes the receiver to send its action message.
|
||||
@param sender The object that sent the message. (a Font panel)
|
||||
*/
|
||||
- (void)modifyFontViaPanel:(id)sender
|
||||
{
|
||||
_fontAction = CPViaPanelFontAction;
|
||||
if (_selectedFont)
|
||||
[self setSelectedFont:[self convertFont:_selectedFont] isMultiple:NO];
|
||||
|
||||
[self sendAction];
|
||||
}
|
||||
|
||||
/*!
|
||||
Convert a font according to current font changes, provided by the object that initiated the font change.
|
||||
@param aFont The font to convert.
|
||||
@result The converted font or \c aFont if the conversion failed.
|
||||
*/
|
||||
- (CPFont)convertFont:(CPFont)aFont
|
||||
{
|
||||
var newFont = nil;
|
||||
switch (_fontAction)
|
||||
{
|
||||
case CPNoFontChangeAction:
|
||||
newFont = aFont;
|
||||
break;
|
||||
|
||||
case CPViaPanelFontAction:
|
||||
newFont = [[self fontPanel:NO] panelConvertFont:aFont];
|
||||
break;
|
||||
|
||||
case CPAddTraitFontAction:
|
||||
newFont = aFont;
|
||||
if (!_activeChange)
|
||||
break;
|
||||
|
||||
var addTraits = [_activeChange valueForKey:@"addTraits"];
|
||||
|
||||
if (addTraits)
|
||||
newFont = [self convertFont:aFont toHaveTrait:addTraits];
|
||||
break;
|
||||
|
||||
case CPSizeUpFontAction:
|
||||
newFont = [self convertFont:aFont toSize:[aFont size] + 1.0]; /* any limit ? */
|
||||
break;
|
||||
|
||||
case CPSizeDownFontAction:
|
||||
if ([aFont size] > 1)
|
||||
newFont = [self convertFont:aFont toSize:[aFont size] - 1.0];
|
||||
/* else CPBeep() :-p */
|
||||
break;
|
||||
|
||||
default:
|
||||
CPLog.trace(@"-[" + [self className] + " " + _cmd + "] unsupported font action: " + _fontAction + " aFont unchanged");
|
||||
newFont = aFont;
|
||||
break;
|
||||
}
|
||||
|
||||
return newFont;
|
||||
}
|
||||
|
||||
@end
|
||||
|
||||
var _CPFontDetectSpan,
|
||||
|
||||
+6
-5
@@ -70,11 +70,12 @@ CPGradientDrawsAfterEndingLocation = kCGGradientDrawsAfterEndLocation;
|
||||
{
|
||||
if (self = [super init])
|
||||
{
|
||||
var cgColors = [],
|
||||
count = [someColors count],
|
||||
colorSpace = [aColorSpace CGColorSpace] || CGColorSpaceCreateDeviceRGB;
|
||||
for (var i = 0; i < count; i++)
|
||||
cgColors.push(CGColorCreate(colorSpace, [someColors[i] components]));
|
||||
var colorSpace = [aColorSpace CGColorSpace] || CGColorSpaceCreateDeviceRGB,
|
||||
cgColors = [someColors arrayByApplyingBlock:function(color)
|
||||
{
|
||||
return CGColorCreate(colorSpace, [color components])
|
||||
}];
|
||||
|
||||
_gradient = CGGradientCreateWithColors(colorSpace, cgColors, someLocations);
|
||||
}
|
||||
|
||||
|
||||
+4
-4
@@ -43,10 +43,10 @@ function CPDrawTiledRects(
|
||||
if (sides.length != grays.length)
|
||||
[CPException raise:CPInvalidArgumentException reason:@"sides (length: " + sides.length + ") and grays (length: " + grays.length + ") must have the same length."];
|
||||
|
||||
var colors = [];
|
||||
|
||||
for (var i = 0; i < grays.length; ++i)
|
||||
colors.push([CPColor colorWithCalibratedWhite:grays[i] alpha:1.0]);
|
||||
var colors = [grays arrayByApplyingBlock:function(gray)
|
||||
{
|
||||
return [CPColor colorWithCalibratedWhite:gray alpha:1.0];
|
||||
}];
|
||||
|
||||
return CPDrawColorTiledRects(boundsRect, clipRect, sides, colors);
|
||||
}
|
||||
|
||||
+219
-2
@@ -246,7 +246,7 @@ function CPAppKitImage(aFilename, aSize)
|
||||
var canvas = document.createElement("canvas"),
|
||||
ctx = canvas.getContext("2d");
|
||||
|
||||
canvas.width = _image.width,
|
||||
canvas.width = _image.width;
|
||||
canvas.height = _image.height;
|
||||
|
||||
ctx.drawImage(_image, 0, 0);
|
||||
@@ -509,6 +509,212 @@ function CPAppKitImage(aFilename, aSize)
|
||||
|
||||
@end
|
||||
|
||||
#pragma mark -
|
||||
#pragma mark CSS Theming
|
||||
|
||||
// The code below adds support for CSS theming with 100% compatibility with current theming system.
|
||||
// The idea is to extend CPImage (and CPColor) with CSS components and adapt low level UI components to
|
||||
// support this new kind of CPColor/CPImage. See CPImageView, CPView and _CPImageAndTextView.
|
||||
//
|
||||
// To create a CPImage that uses CSS, simply use the new class method :
|
||||
// + (CPImage)imageWithCSSDictionary:(CPDictionary)aDictionary beforeDictionary:(CPDictionary)beforeDictionary afterDictionary:(CPDictionary)afterDictionary size:(CGSize)aSize
|
||||
// where beforeDictionary & afterDictionary are related to ::before & ::after pseudo-elements.
|
||||
// If you don't need them, just use the simplified class method :
|
||||
// + (CPImage)imageWithCSSDictionary:(CPDictionary)aDictionary size:(CGSize)aSize
|
||||
//
|
||||
// Examples :
|
||||
// regularImageNormal = [CPImage imageWithCSSDictionary:@{
|
||||
// @"border-color": A3ColorActiveBorder,
|
||||
// @"border-style": @"solid",
|
||||
// @"border-width": @"1px",
|
||||
// @"border-radius": @"50%",
|
||||
// @"box-sizing": @"border-box",
|
||||
// @"background-color": A3ColorBackgroundWhite,
|
||||
// @"transition-duration": @"0.35s",
|
||||
// @"transition-property": @"all",
|
||||
// @"transition-timing-function": @"ease"
|
||||
// }
|
||||
// size:CGSizeMake(16,16)];
|
||||
//
|
||||
// imageSearch = [CPImage imageWithCSSDictionary:@{
|
||||
// @"background-image": @"url(%%packed.png)",
|
||||
// @"background-position": @"-16px -32px",
|
||||
// @"background-repeat": @"no-repeat",
|
||||
// @"background-size": @"100px 400px"
|
||||
// }
|
||||
// size:CGSizeMake(16,16)];
|
||||
//
|
||||
// Remark : Please note the special URL of the background image used in this example : url(%%packed.png)
|
||||
// During theme loading, "%%" will be replaced by the path to the theme blend resources folder.
|
||||
// Typically, a CSS theme will use some (rare) images all packed together in a single image resource (see packed.png in Aristo3 theme)
|
||||
//
|
||||
// Also, please note that if you don't use one of the CSS components, you can either set it to nil (best solution) or to an empty dictionary, like :
|
||||
// aCssImage = [CPImage imageWithCSSDictionary:@{} beforeDictionary:nil afterDictionary:@{ ... }];
|
||||
//
|
||||
// You can use -(BOOL)isCSSBased to determine how to cope with it in your code.
|
||||
// -(BOOL)hasCSSDictionary, -(BOOL)hasCSSBeforeDictionary and -(BOOL)hasCSSAfterDictionary are convience methods you can use.
|
||||
//
|
||||
// Remark : -(DOMElement)applyCSSImageForView is meant to be used by low level UI widgets (like CPImageView and _CPImageAndTextView) to implement CSS theme support.
|
||||
//
|
||||
// In some circumstances, you may have to clear a CSS image. You can do this easily by replacing your current image with the special dummy empty CSS image :
|
||||
// [CPImage dummyCSSImageOfSize:CGSizeMake(someWidth, someHeight)]
|
||||
|
||||
@implementation CPImage (CSSTheming)
|
||||
{
|
||||
CPDictionary _cssDictionary @accessors(property=cssDictionary);
|
||||
CPDictionary _cssBeforeDictionary @accessors(property=cssBeforeDictionary);
|
||||
CPDictionary _cssAfterDictionary @accessors(property=cssAfterDictionary);
|
||||
}
|
||||
|
||||
+ (CPImage)imageWithCSSDictionary:(CPDictionary)aDictionary size:(CGSize)aSize
|
||||
{
|
||||
return [[CPImage alloc] initWithCSSDictionary:aDictionary beforeDictionary:nil afterDictionary:nil size:aSize];
|
||||
}
|
||||
|
||||
+ (CPImage)imageWithCSSDictionary:(CPDictionary)aDictionary beforeDictionary:(CPDictionary)beforeDictionary afterDictionary:(CPDictionary)afterDictionary size:(CGSize)aSize
|
||||
{
|
||||
return [[CPImage alloc] initWithCSSDictionary:aDictionary beforeDictionary:beforeDictionary afterDictionary:afterDictionary size:aSize];
|
||||
}
|
||||
|
||||
+ (CPImage)dummyCSSImageOfSize:(CGSize)aSize
|
||||
{
|
||||
// This is used to clear a previous CSS image
|
||||
return [[CPImage alloc] initWithCSSDictionary:@{} beforeDictionary:nil afterDictionary:nil size:aSize];
|
||||
}
|
||||
|
||||
- (id)initWithCSSDictionary:(CPDictionary)aDictionary beforeDictionary:(CPDictionary)beforeDictionary afterDictionary:(CPDictionary)afterDictionary size:(CGSize)aSize
|
||||
{
|
||||
self = [super init];
|
||||
|
||||
if (self)
|
||||
{
|
||||
_size = CGSizeMakeCopy(aSize);
|
||||
_filename = @"CSS image";
|
||||
_loadStatus = CPImageLoadStatusCompleted;
|
||||
_cssDictionary = aDictionary;
|
||||
_cssBeforeDictionary = beforeDictionary;
|
||||
_cssAfterDictionary = afterDictionary;
|
||||
}
|
||||
|
||||
return self;
|
||||
}
|
||||
|
||||
- (BOOL)isCSSBased
|
||||
{
|
||||
return !!(_cssDictionary || _cssBeforeDictionary || _cssAfterDictionary);
|
||||
}
|
||||
|
||||
- (BOOL)hasCSSDictionary
|
||||
{
|
||||
return ([_cssDictionary count] > 0);
|
||||
}
|
||||
|
||||
- (BOOL)hasCSSBeforeDictionary
|
||||
{
|
||||
return ([_cssBeforeDictionary count] > 0);
|
||||
}
|
||||
|
||||
- (BOOL)hasCSSAfterDictionary
|
||||
{
|
||||
return ([_cssAfterDictionary count] > 0);
|
||||
}
|
||||
|
||||
- (DOMElement)applyCSSImageForView:(CPView)aView onDOMElement:(DOMElement)aDOMElement styleNode:(DOMElement)aStyleNode previousState:(CPArrayRef)aPreviousStateRef
|
||||
{
|
||||
#if PLATFORM(DOM)
|
||||
// First, restore previous CSS styling before applying the new one
|
||||
|
||||
var aPreviousState = @deref(aPreviousStateRef);
|
||||
|
||||
for (var i = 0, count = aPreviousState.length; i < count; i++)
|
||||
aDOMElement.style[aPreviousState[i][0]] = aPreviousState[i][1];
|
||||
|
||||
aPreviousState = @[];
|
||||
|
||||
// Then apply new CSS styling
|
||||
|
||||
[_cssDictionary enumerateKeysAndObjectsUsingBlock:function(aKey, anObject, stop)
|
||||
{
|
||||
[aPreviousState addObject:@[aKey, aDOMElement.style[aKey]]];
|
||||
aDOMElement.style[aKey] = anObject;
|
||||
}];
|
||||
|
||||
if (_cssBeforeDictionary || _cssAfterDictionary)
|
||||
{
|
||||
// We need to create a unique class name
|
||||
|
||||
var styleClassName = @".CP" + [aView UID],
|
||||
styleContent = @"";
|
||||
|
||||
if (_cssBeforeDictionary)
|
||||
{
|
||||
styleContent += styleClassName + @"::before { ";
|
||||
|
||||
[_cssBeforeDictionary enumerateKeysAndObjectsUsingBlock:function(aKey, anObject, stop)
|
||||
{
|
||||
styleContent += aKey + ": " + anObject + "; ";
|
||||
}];
|
||||
|
||||
styleContent += "} ";
|
||||
}
|
||||
|
||||
if (_cssAfterDictionary)
|
||||
{
|
||||
styleContent += styleClassName + @"::after { ";
|
||||
|
||||
[_cssAfterDictionary enumerateKeysAndObjectsUsingBlock:function(aKey, anObject, stop)
|
||||
{
|
||||
styleContent += aKey + ": " + anObject + "; ";
|
||||
}];
|
||||
|
||||
styleContent += "} ";
|
||||
}
|
||||
|
||||
var styleDescription = document.createTextNode(styleContent);
|
||||
|
||||
if (!aStyleNode)
|
||||
{
|
||||
aStyleNode = document.createElement("style");
|
||||
|
||||
aView._DOMElement.insertBefore(aStyleNode, aView._DOMElement.firstChild);
|
||||
|
||||
aStyleNode.appendChild(styleDescription);
|
||||
}
|
||||
else
|
||||
{
|
||||
aStyleNode.replaceChild(styleDescription, aStyleNode.firstChild);
|
||||
}
|
||||
|
||||
[aView setDOMClassName:@"CP"+[aView UID]];
|
||||
}
|
||||
else
|
||||
{
|
||||
// no before/after so remove aStyleNode if existing
|
||||
|
||||
if (aStyleNode)
|
||||
{
|
||||
aView._DOMElement.removeChild(aStyleNode);
|
||||
aStyleNode = nil;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
// Return actualised values
|
||||
|
||||
@deref(aPreviousStateRef) = aPreviousState;
|
||||
|
||||
return aStyleNode;
|
||||
#endif
|
||||
}
|
||||
|
||||
@end
|
||||
|
||||
var CPImageCSSDictionaryKey = @"CPImageCSSDictionaryKey",
|
||||
CPImageCSSBeforeDictionaryKey = @"CPImageCSSBeforeDictionaryKey",
|
||||
CPImageCSSAfterDictionaryKey = @"CPImageCSSAfterDictionaryKey";
|
||||
|
||||
#pragma mark -
|
||||
|
||||
@implementation CPImage (CPCoding)
|
||||
|
||||
/*!
|
||||
@@ -518,7 +724,10 @@ function CPAppKitImage(aFilename, aSize)
|
||||
*/
|
||||
- (id)initWithCoder:(CPCoder)aCoder
|
||||
{
|
||||
return [self initWithContentsOfFile:[aCoder decodeObjectForKey:@"CPFilename"] size:[aCoder decodeSizeForKey:@"CPSize"]];
|
||||
if ([aCoder containsValueForKey:CPImageCSSDictionaryKey])
|
||||
return [self initWithCSSDictionary:[aCoder decodeObjectForKey:CPImageCSSDictionaryKey] beforeDictionary:[aCoder decodeObjectForKey:CPImageCSSBeforeDictionaryKey] afterDictionary:[aCoder decodeObjectForKey:CPImageCSSAfterDictionaryKey] size:[aCoder decodeSizeForKey:@"CPSize"]];
|
||||
else
|
||||
return [self initWithContentsOfFile:[aCoder decodeObjectForKey:@"CPFilename"] size:[aCoder decodeSizeForKey:@"CPSize"]];
|
||||
}
|
||||
|
||||
/*!
|
||||
@@ -529,6 +738,14 @@ function CPAppKitImage(aFilename, aSize)
|
||||
{
|
||||
[aCoder encodeObject:_filename forKey:@"CPFilename"];
|
||||
[aCoder encodeSize:_size forKey:@"CPSize"];
|
||||
|
||||
// CSS Styling
|
||||
if ([self isCSSBased])
|
||||
{
|
||||
[aCoder encodeObject:_cssDictionary forKey:CPImageCSSDictionaryKey];
|
||||
[aCoder encodeObject:_cssBeforeDictionary forKey:CPImageCSSBeforeDictionaryKey];
|
||||
[aCoder encodeObject:_cssAfterDictionary forKey:CPImageCSSAfterDictionaryKey];
|
||||
}
|
||||
}
|
||||
|
||||
@end
|
||||
|
||||
+33
-6
@@ -98,10 +98,32 @@ var CPImageViewEmptyPlaceholderImage = nil;
|
||||
- (void)_createDOMImageElement
|
||||
{
|
||||
#if PLATFORM(DOM)
|
||||
if (_DOMImageElement)
|
||||
return;
|
||||
var image = [self objectValue],
|
||||
isCSSBasedImage = [image isCSSBased],
|
||||
isIMGImageElement = _DOMImageElement && (_DOMImageElement.nodeName == "IMG");
|
||||
|
||||
_DOMImageElement = document.createElement("img");
|
||||
// First, check if we need to destroy a current DOM image element. This is the case if :
|
||||
// - we have one but not the right one (that is a DIV but needing an IMG, and vice versa)
|
||||
|
||||
if (_DOMImageElement)
|
||||
{
|
||||
if ((isIMGImageElement && isCSSBasedImage) || (!isIMGImageElement && !isCSSBasedImage))
|
||||
{
|
||||
// OK, destroy it
|
||||
|
||||
_DOMElement.removeChild(_DOMImageElement);
|
||||
|
||||
_DOMImageElement = nil;
|
||||
|
||||
// CSS styling cleaning
|
||||
_cssStylePreviousState = @[];
|
||||
_cssStyleNode = nil;
|
||||
}
|
||||
else
|
||||
return;
|
||||
}
|
||||
|
||||
_DOMImageElement = document.createElement(isCSSBasedImage ? "div" : "img");
|
||||
_DOMImageElement.style.position = "absolute";
|
||||
_DOMImageElement.style.left = "0px";
|
||||
_DOMImageElement.style.top = "0px";
|
||||
@@ -150,10 +172,15 @@ var CPImageViewEmptyPlaceholderImage = nil;
|
||||
var newImage = [self objectValue];
|
||||
|
||||
#if PLATFORM(DOM)
|
||||
if (!_DOMImageElement)
|
||||
[self _createDOMImageElement];
|
||||
[self _createDOMImageElement];
|
||||
|
||||
_DOMImageElement.src = newImage ? [newImage filename] : [CPImageViewEmptyPlaceholderImage filename];
|
||||
if ([newImage isCSSBased])
|
||||
_cssStyleNode = [newImage applyCSSImageForView:self
|
||||
onDOMElement:_DOMImageElement
|
||||
styleNode:_cssStyleNode
|
||||
previousState:@ref(_cssStylePreviousState)];
|
||||
else
|
||||
_DOMImageElement.src = newImage ? [newImage filename] : [CPImageViewEmptyPlaceholderImage filename];
|
||||
#endif
|
||||
|
||||
var size = [newImage size];
|
||||
|
||||
@@ -857,7 +857,7 @@ var CPBindingOperationAnd = 0,
|
||||
- (id)initWithName:(CPString)aName
|
||||
{
|
||||
if (self = [super init])
|
||||
_name = aName
|
||||
_name = aName;
|
||||
|
||||
return self;
|
||||
}
|
||||
@@ -918,6 +918,7 @@ CPTextColorBinding = @"textColor";
|
||||
CPTitleBinding = @"title";
|
||||
CPToolTipBinding = @"toolTip";
|
||||
CPValueBinding = @"value";
|
||||
CPAttributedStringBinding = @"attributedString";
|
||||
CPValueURLBinding = @"valueURL";
|
||||
CPValuePathBinding = @"valuePath";
|
||||
CPDataBinding = @"data";
|
||||
|
||||
@@ -292,7 +292,7 @@ CPRatingLevelIndicatorStyle = 3;
|
||||
[self setNeedsLayout];
|
||||
}
|
||||
|
||||
- (void)setWarningValue:(double)warningValue;
|
||||
- (void)setWarningValue:(double)warningValue
|
||||
{
|
||||
if (_warningValue === warningValue)
|
||||
return;
|
||||
@@ -301,7 +301,7 @@ CPRatingLevelIndicatorStyle = 3;
|
||||
[self setNeedsLayout];
|
||||
}
|
||||
|
||||
- (void)setCriticalValue:(double)criticalValue;
|
||||
- (void)setCriticalValue:(double)criticalValue
|
||||
{
|
||||
if (_criticalValue === criticalValue)
|
||||
return;
|
||||
|
||||
@@ -505,7 +505,7 @@ CPOffState
|
||||
if (_submenu)
|
||||
{
|
||||
[_submenu setSupermenu:_menu];
|
||||
[_submenu setTitle:[self title]]
|
||||
[_submenu setTitle:[self title]];
|
||||
|
||||
[self setTarget:_menu];
|
||||
[self setAction:@selector(submenuAction:)];
|
||||
@@ -843,7 +843,7 @@ CPControlKeyMask
|
||||
[item setTarget:_target];
|
||||
[item setAction:_action];
|
||||
[item setEnabled:_isEnabled];
|
||||
[item setHidden:_isHidden]
|
||||
[item setHidden:_isHidden];
|
||||
[item setTag:_tag];
|
||||
[item setState:_state];
|
||||
[item setImage:_image];
|
||||
|
||||
@@ -55,6 +55,7 @@
|
||||
@"menu-item-selection-color": [CPNull null],
|
||||
@"menu-item-text-shadow-color": [CPNull null],
|
||||
@"menu-item-text-color": [CPNull null],
|
||||
@"menu-item-disabled-text-color": [CPColor lightGrayColor],
|
||||
@"menu-item-default-off-state-image": [CPNull null],
|
||||
@"menu-item-default-off-state-highlighted-image": [CPNull null],
|
||||
@"menu-item-default-on-state-image": [CPNull null],
|
||||
@@ -124,7 +125,7 @@
|
||||
- (CPColor)textColor
|
||||
{
|
||||
if (![_menuItem isEnabled])
|
||||
return [CPColor lightGrayColor];
|
||||
return [self valueForThemeAttribute:@"menu-item-disabled-text-color"];
|
||||
|
||||
if (_highlighted)
|
||||
return [CPColor whiteColor];
|
||||
|
||||
+105
-3
@@ -27,6 +27,9 @@
|
||||
@import "CPController.j"
|
||||
@import "CPKeyValueBinding.j"
|
||||
|
||||
@class _CPManagedProxy
|
||||
@class CPPredicate;
|
||||
|
||||
/*!
|
||||
@class
|
||||
|
||||
@@ -47,6 +50,9 @@
|
||||
|
||||
BOOL _isEditable;
|
||||
BOOL _automaticallyPreparesContent;
|
||||
BOOL _usesLazyFetching @accessors(getter=usesLazyFetching, setter=setUsesLazyFetching:);
|
||||
BOOL _isUsingManagedProxy;
|
||||
_CPManagedProxy _managedProxy;
|
||||
|
||||
CPCountedSet _observedKeys;
|
||||
}
|
||||
@@ -179,6 +185,52 @@
|
||||
return _automaticallyPreparesContent;
|
||||
}
|
||||
|
||||
/*!
|
||||
Sets the entity name the controller handles.
|
||||
|
||||
@param CPString newEntityName - The new entity name.
|
||||
*/
|
||||
- (void)setEntityName:(CPString)newEntityName
|
||||
{
|
||||
if (!_managedProxy)
|
||||
{
|
||||
_managedProxy = [[_CPManagedProxy alloc] init];
|
||||
_isUsingManagedProxy = YES;
|
||||
}
|
||||
|
||||
[_managedProxy setEntityName:newEntityName];
|
||||
}
|
||||
|
||||
/*!
|
||||
Returns the entity name.
|
||||
|
||||
@return CPString - The name of the entity.
|
||||
*/
|
||||
- (CPString)entityName
|
||||
{
|
||||
return [_managedProxy entityName];
|
||||
}
|
||||
|
||||
/*!
|
||||
Sets the predicate used to fetch content.
|
||||
|
||||
@param CPPredicate newPredicate - The fetch predicate.
|
||||
*/
|
||||
- (void)setFetchPredicate:(CPPredicate)newPredicate
|
||||
{
|
||||
[_managedProxy setFetchPredicate:newPredicate];
|
||||
}
|
||||
|
||||
/*!
|
||||
Returns the fetch predicate.
|
||||
|
||||
@return CPPredicate - The predicate used to fetch content.
|
||||
*/
|
||||
- (CPPredicate)fetchPredicate
|
||||
{
|
||||
return [_managedProxy fetchPredicate];
|
||||
}
|
||||
|
||||
/*!
|
||||
Overridden by a subclass that require control over the creation of new objects.
|
||||
*/
|
||||
@@ -189,6 +241,7 @@
|
||||
|
||||
/*!
|
||||
Sets the object class when creating new objects.
|
||||
|
||||
@param Class - the class of new objects that will be created.
|
||||
*/
|
||||
- (void)setObjectClass:(Class)aClass
|
||||
@@ -363,7 +416,10 @@
|
||||
var CPObjectControllerContentKey = @"CPObjectControllerContentKey",
|
||||
CPObjectControllerObjectClassNameKey = @"CPObjectControllerObjectClassNameKey",
|
||||
CPObjectControllerIsEditableKey = @"CPObjectControllerIsEditableKey",
|
||||
CPObjectControllerAutomaticallyPreparesContentKey = @"CPObjectControllerAutomaticallyPreparesContentKey";
|
||||
CPObjectControllerAutomaticallyPreparesContentKey = @"CPObjectControllerAutomaticallyPreparesContentKey",
|
||||
CPObjectControllerUsesLazyFetchingKey = @"CPObjectControllerUsesLazyFetchingKey",
|
||||
CPObjectControllerIsUsingManagedProxyKey = @"CPObjectControllerIsUsingManagedProxyKey",
|
||||
CPObjectControllerManagedProxyKey = @"CPObjectControllerManagedProxyKey";
|
||||
|
||||
@implementation CPObjectController (CPCoding)
|
||||
|
||||
@@ -374,12 +430,18 @@ var CPObjectControllerContentKey = @"CPObjectControllerCo
|
||||
if (self)
|
||||
{
|
||||
var objectClassName = [aCoder decodeObjectForKey:CPObjectControllerObjectClassNameKey],
|
||||
objectClass = CPClassFromString(objectClassName);
|
||||
objectClass = CPClassFromString(objectClassName),
|
||||
content = [aCoder decodeObjectForKey:CPObjectControllerContentKey];
|
||||
|
||||
[self setObjectClass:objectClass || [CPMutableDictionary class]];
|
||||
[self setEditable:[aCoder decodeBoolForKey:CPObjectControllerIsEditableKey]];
|
||||
[self setAutomaticallyPreparesContent:[aCoder decodeBoolForKey:CPObjectControllerAutomaticallyPreparesContentKey]];
|
||||
[self setContent:[aCoder decodeObjectForKey:CPObjectControllerContentKey]];
|
||||
[self setUsesLazyFetching:[aCoder decodeBoolForKey:CPObjectControllerUsesLazyFetchingKey]];
|
||||
_isUsingManagedProxy = [aCoder decodeBoolForKey:CPObjectControllerIsUsingManagedProxyKey];
|
||||
_managedProxy = [aCoder decodeObjectForKey:CPObjectControllerManagedProxyKey];
|
||||
|
||||
if (content != nil)
|
||||
[self setContent:content];
|
||||
|
||||
_observedKeys = [[CPCountedSet alloc] init];
|
||||
}
|
||||
@@ -398,6 +460,11 @@ var CPObjectControllerContentKey = @"CPObjectControllerCo
|
||||
|
||||
[aCoder encodeBool:[self isEditable] forKey:CPObjectControllerIsEditableKey];
|
||||
[aCoder encodeBool:[self automaticallyPreparesContent] forKey:CPObjectControllerAutomaticallyPreparesContentKey];
|
||||
[aCoder encodeBool:[self usesLazyFetching] forKey:CPObjectControllerUsesLazyFetchingKey];
|
||||
[aCoder encodeBool:_isUsingManagedProxy forKey:CPObjectControllerIsUsingManagedProxyKey];
|
||||
|
||||
if (_managedProxy)
|
||||
[aCoder encodeObject:_managedProxy forKey:CPObjectControllerManagedProxyKey];
|
||||
}
|
||||
|
||||
- (void)awakeFromCib
|
||||
@@ -825,3 +892,38 @@ var CPObjectControllerContentKey = @"CPObjectControllerCo
|
||||
}
|
||||
|
||||
@end
|
||||
|
||||
|
||||
@implementation _CPManagedProxy : CPObject
|
||||
{
|
||||
CPString _entityName @accessors(getter=entityName, setter=setEntityName:);
|
||||
CPPredicate _fetchPredicate @accessors(getter=fetchPredicate, setter=setFetchPredicate:);
|
||||
}
|
||||
|
||||
@end
|
||||
|
||||
var CPManagedProxyEntityNameKey = @"CPManagedProxyEntityNameKey",
|
||||
CPManagedProxyFetchPredicateKey = @"CPManagedProxyFetchPredicateKey";
|
||||
|
||||
@implementation _CPManagedProxy (CPCoding)
|
||||
|
||||
- (id)initWithCoder:(CPCoder)aCoder
|
||||
{
|
||||
self = [super init];
|
||||
|
||||
if (self)
|
||||
{
|
||||
[self setEntityName:[aCoder decodeObjectForKey:CPManagedProxyEntityNameKey]];
|
||||
[self setFetchPredicate:[aCoder decodeObjectForKey:CPManagedProxyFetchPredicateKey]];
|
||||
}
|
||||
|
||||
return self;
|
||||
}
|
||||
|
||||
- (void)encodeWithCoder:(CPCoder)aCoder
|
||||
{
|
||||
[aCoder encodeObject:[self entityName] forKey:CPManagedProxyEntityNameKey];
|
||||
[aCoder encodeObject:[self fetchPredicate] forKey:CPManagedProxyFetchPredicateKey];
|
||||
}
|
||||
|
||||
@end
|
||||
@@ -1923,7 +1923,7 @@ var CPOutlineViewCoalesceSelectionNotificationStateOff = 0,
|
||||
|
||||
while (index !== CPNotFound)
|
||||
{
|
||||
[items addObject:[_outlineView itemAtRow:index]]
|
||||
[items addObject:[_outlineView itemAtRow:index]];
|
||||
index = [theIndexes indexGreaterThanIndex:index];
|
||||
}
|
||||
|
||||
@@ -1957,7 +1957,7 @@ var CPOutlineViewCoalesceSelectionNotificationStateOff = 0,
|
||||
- (void)_parentItemForDropOperation:(CPTableViewDropOperation)theDropOperation row:(CPInteger)theRow offset:(CGPoint)theOffset
|
||||
{
|
||||
if (theDropOperation === CPTableViewDropAbove)
|
||||
return [_outlineView _parentItemForUpperRow:theRow - 1 andLowerRow:theRow atMouseOffset:theOffset]
|
||||
return [_outlineView _parentItemForUpperRow:theRow - 1 andLowerRow:theRow atMouseOffset:theOffset];
|
||||
|
||||
return [_outlineView itemAtRow:theRow];
|
||||
}
|
||||
|
||||
+3
-69
@@ -44,6 +44,8 @@ CPStringPboardType = @"CPStringPboardType";
|
||||
CPURLPboardType = @"CPURLPboardType";
|
||||
CPImagesPboardType = @"CPImagesPboardType";
|
||||
CPVideosPboardType = @"CPVideosPboardType";
|
||||
CPRTFPboardType = @"CPRTFPboardType";
|
||||
_CPSmartPboardType = @"_CPSmartPboardType";
|
||||
|
||||
UTF8PboardType = @"public.utf8-plain-text";
|
||||
|
||||
@@ -51,8 +53,7 @@ UTF8PboardType = @"public.utf8-plain-text";
|
||||
CPImagePboardType = @"CPImagePboardType";
|
||||
|
||||
|
||||
var CPPasteboards = nil,
|
||||
supportsNativePasteboard = NO;
|
||||
var CPPasteboards = nil;
|
||||
|
||||
/*!
|
||||
@ingroup appkit
|
||||
@@ -68,8 +69,6 @@ var CPPasteboards = nil,
|
||||
|
||||
unsigned _changeCount;
|
||||
CPString _stateUID;
|
||||
|
||||
CPWebScriptObject _nativePasteboard;
|
||||
}
|
||||
|
||||
/*
|
||||
@@ -83,9 +82,6 @@ var CPPasteboards = nil,
|
||||
[self setVersion:1.0];
|
||||
|
||||
CPPasteboards = @{};
|
||||
|
||||
if (typeof window.cpPasteboardWithName !== "undefined")
|
||||
supportsNativePasteboard = YES;
|
||||
}
|
||||
|
||||
/*!
|
||||
@@ -128,12 +124,6 @@ var CPPasteboards = nil,
|
||||
_provided = @{};
|
||||
|
||||
_changeCount = 0;
|
||||
|
||||
if (supportsNativePasteboard)
|
||||
{
|
||||
_nativePasteboard = window.cpPasteboardWithName(aName);
|
||||
[self _synchronizePasteboard];
|
||||
}
|
||||
}
|
||||
|
||||
return self;
|
||||
@@ -163,15 +153,6 @@ var CPPasteboards = nil,
|
||||
[_owners setObject:anOwner forKey:type];
|
||||
}
|
||||
|
||||
if (_nativePasteboard)
|
||||
{
|
||||
var nativeTypes = [types copy];
|
||||
if ([types containsObject:CPStringPboardType])
|
||||
nativeTypes.push(UTF8PboardType);
|
||||
|
||||
_nativePasteboard.addTypes_(nativeTypes);
|
||||
}
|
||||
|
||||
return ++_changeCount;
|
||||
}
|
||||
|
||||
@@ -182,12 +163,6 @@ var CPPasteboards = nil,
|
||||
@return the pasteboard's change count
|
||||
*/
|
||||
- (unsigned)declareTypes:(CPArray)types owner:(id)anOwner
|
||||
{
|
||||
[self _declareTypes:types owner:anOwner updateNativePasteboard:YES];
|
||||
}
|
||||
|
||||
/*! @ignore */
|
||||
- (unsigned)_declareTypes:(CPArray)types owner:(id)anOwner updateNativePasteboard:(BOOL)shouldUpdate
|
||||
{
|
||||
[_types setArray:types];
|
||||
|
||||
@@ -201,16 +176,6 @@ var CPPasteboards = nil,
|
||||
[_owners setObject:anOwner forKey:_types[count]];
|
||||
}
|
||||
|
||||
if (_nativePasteboard && shouldUpdate)
|
||||
{
|
||||
var nativeTypes = [types copy];
|
||||
if ([types containsObject:CPStringPboardType])
|
||||
nativeTypes.push(UTF8PboardType);
|
||||
|
||||
_nativePasteboard.declareTypes_(nativeTypes);
|
||||
_changeCount = _nativePasteboard.changeCount();
|
||||
}
|
||||
|
||||
return ++_changeCount;
|
||||
}
|
||||
|
||||
@@ -273,7 +238,6 @@ var CPPasteboards = nil,
|
||||
*/
|
||||
- (CPArray)types
|
||||
{
|
||||
[self _synchronizePasteboard];
|
||||
return _types;
|
||||
}
|
||||
|
||||
@@ -312,36 +276,6 @@ var CPPasteboards = nil,
|
||||
return nil;
|
||||
}
|
||||
|
||||
- (void)_synchronizePasteboard
|
||||
{
|
||||
if (_nativePasteboard && _nativePasteboard.changeCount() > _changeCount)
|
||||
{
|
||||
var nativeTypes = [_nativePasteboard.types() copy];
|
||||
if ([nativeTypes containsObject:UTF8PboardType])
|
||||
nativeTypes.push(CPStringPboardType);
|
||||
|
||||
[self _declareTypes:nativeTypes owner:self updateNativePasteboard:NO];
|
||||
|
||||
_changeCount = _nativePasteboard.changeCount();
|
||||
}
|
||||
}
|
||||
|
||||
/*! @ignore
|
||||
method provided for integration with native pasteboard
|
||||
*/
|
||||
- (void)pasteboard:(CPPasteboard)aPasteboard provideDataForType:(CPString)aType
|
||||
{
|
||||
if (aType === CPStringPboardType)
|
||||
{
|
||||
var string = _nativePasteboard.stringForType_(UTF8PboardType);
|
||||
|
||||
[self setString:string forType:CPStringPboardType];
|
||||
[self setString:string forType:UTF8PboardType];
|
||||
}
|
||||
else
|
||||
[self setString:_nativePasteboard.stringForType_(aType) forType:aType];
|
||||
}
|
||||
|
||||
/*!
|
||||
Returns the property list for the specified data type
|
||||
@param aType the requested data type
|
||||
|
||||
+27
-12
@@ -361,15 +361,10 @@ CPPopUpButtonStatePullsDown = CPThemeState("pulls-down");
|
||||
*/
|
||||
- (CPArray)itemTitles
|
||||
{
|
||||
var titles = [],
|
||||
items = [self itemArray],
|
||||
index = 0,
|
||||
count = [items count];
|
||||
|
||||
for (; index < count; ++index)
|
||||
titles.push([items[index] title]);
|
||||
|
||||
return titles;
|
||||
return [[self itemArray] arrayByApplyingBlock:function(item)
|
||||
{
|
||||
return [item title];
|
||||
}];
|
||||
}
|
||||
|
||||
/*!
|
||||
@@ -852,7 +847,17 @@ CPPopUpButtonStatePullsDown = CPThemeState("pulls-down");
|
||||
{
|
||||
var count = [aValue count],
|
||||
options = [_info objectForKey:CPOptionsKey],
|
||||
offset = [self _getInsertNullOffset];
|
||||
offset = [self _getInsertNullOffset],
|
||||
selectedBindingInfo = [_source infoForBinding:CPSelectedObjectBinding],
|
||||
selectedObject = nil;
|
||||
|
||||
if (selectedBindingInfo)
|
||||
{
|
||||
var destination = [selectedBindingInfo objectForKey:CPObservedObjectKey],
|
||||
keyPath = [selectedBindingInfo objectForKey:CPObservedKeyPathKey];
|
||||
|
||||
selectedObject = [destination valueForKeyPath:keyPath];
|
||||
}
|
||||
|
||||
if (count + offset != [_source numberOfItems])
|
||||
{
|
||||
@@ -863,9 +868,19 @@ CPPopUpButtonStatePullsDown = CPThemeState("pulls-down");
|
||||
|
||||
for (var i = 0; i < count; i++)
|
||||
{
|
||||
var item = [[CPMenuItem alloc] initWithTitle:@"" action:NULL keyEquivalent:nil];
|
||||
[self _setValue:[aValue objectAtIndex:i] forItem:item withOptions:options];
|
||||
var item = [[CPMenuItem alloc] initWithTitle:@"" action:NULL keyEquivalent:nil],
|
||||
itemValue = [aValue objectAtIndex:i];
|
||||
|
||||
[self _setValue:itemValue forItem:item withOptions:options];
|
||||
[_source addItem:item];
|
||||
|
||||
// Select this item if it is the one selected by the selected object binding
|
||||
// This is needed if the selected object binding is set before the items
|
||||
// from the content binding
|
||||
if (itemValue === selectedObject)
|
||||
{
|
||||
[_source setSelectedIndex:[_source numberOfItems] - 1];
|
||||
}
|
||||
}
|
||||
}
|
||||
else
|
||||
|
||||
@@ -463,13 +463,13 @@ var CPProgressIndicatorSpinningStyleColors = [];
|
||||
var midX = CGRectGetMidX(rect),
|
||||
midY = CGRectGetMidY(rect),
|
||||
endAngle = Math.PI * 2 * (([self doubleValue] - [self minValue]) / ([self maxValue] - [self minValue])) - Math.PI / 2,
|
||||
radius = MIN(rect.size.width / 2, rect.size.height / 2)
|
||||
radius = MIN(rect.size.width / 2, rect.size.height / 2);
|
||||
|
||||
CGContextBeginPath(context);
|
||||
CGContextSetLineWidth(context, borderSize);
|
||||
CGContextSetFillColor(context, [self currentValueForThemeAttribute:@"circular-color"])
|
||||
CGContextSetFillColor(context, [self currentValueForThemeAttribute:@"circular-color"]);
|
||||
CGContextMoveToPoint(context, midX, midY);
|
||||
CGContextAddArc(context, midX, midY, radius, 3 * Math.PI / 2, endAngle, YES)
|
||||
CGContextAddArc(context, midX, midY, radius, 3 * Math.PI / 2, endAngle, YES);
|
||||
CGContextAddLineToPoint(context, midX, midY);
|
||||
CGContextClosePath(context);
|
||||
CGContextFillPath(context);
|
||||
@@ -478,7 +478,7 @@ var CPProgressIndicatorSpinningStyleColors = [];
|
||||
else if ([self doubleValue] == [self maxValue])
|
||||
{
|
||||
CGContextBeginPath(context);
|
||||
CGContextSetFillColor(context, [self currentValueForThemeAttribute:@"circular-color"])
|
||||
CGContextSetFillColor(context, [self currentValueForThemeAttribute:@"circular-color"]);
|
||||
CGContextAddEllipseInRect(context, rect);
|
||||
CGContextClosePath(context);
|
||||
CGContextFillPath(context);
|
||||
|
||||
@@ -471,9 +471,11 @@ CPTransformableAttributeType = 1800;
|
||||
|
||||
var value;
|
||||
if (attributeType >= CPInteger16AttributeType && attributeType <= CPFloatAttributeType)
|
||||
value = [aView intValue];
|
||||
value = [aView floatValue];
|
||||
else if (attributeType == CPBooleanAttributeType)
|
||||
value = [aView state];
|
||||
else if (attributeType == CPDateAttributeType)
|
||||
value = [aView dateValue];
|
||||
else
|
||||
value = [aView stringValue];
|
||||
|
||||
|
||||
@@ -146,7 +146,7 @@ var CPRuleEditorItemPBoardType = @"CPRuleEditorItemPBoardType",
|
||||
@"slice-top-border-color": [CPNull null],
|
||||
@"slice-bottom-border-color": [CPNull null],
|
||||
@"slice-last-bottom-border-color": [CPNull null],
|
||||
@"font": [CPNull null],
|
||||
@"font": [CPFont systemFontOfSize:12],
|
||||
@"font-color": [CPNull null],
|
||||
@"add-image": [CPNull null],
|
||||
@"remove-image": [CPNull null],
|
||||
|
||||
@@ -209,8 +209,7 @@
|
||||
|
||||
- (void)_reconfigureSubviews
|
||||
{
|
||||
var ruleItems,
|
||||
criteria,
|
||||
var criteria,
|
||||
repObject,
|
||||
menuItem,
|
||||
ruleView,
|
||||
@@ -366,11 +365,12 @@
|
||||
count = [_ruleOptionViews count],
|
||||
sliceFrame = [self frame],
|
||||
image = [_ruleEditor _imageAdd],
|
||||
imageSize = image ? [image size] : CGSizeMake(0,0),
|
||||
|
||||
buttonFrame = CGRectMake(CGRectGetWidth(sliceFrame) - [image size].width - [self _rowButtonsRightHorizontalPadding], ([_ruleEditor rowHeight] - [image size].height) / 2 - 1, [image size].width, [image size].height);
|
||||
buttonFrame = CGRectMake(CGRectGetWidth(sliceFrame) - imageSize.width - [self _rowButtonsRightHorizontalPadding], ([_ruleEditor rowHeight] - imageSize.height) / 2 - 1, imageSize.width, imageSize.height);
|
||||
|
||||
[_addButton setFrame:buttonFrame];
|
||||
buttonFrame.origin.x -= [image size].width + [self _rowButtonsInterviewHorizontalPadding];
|
||||
buttonFrame.origin.x -= imageSize.width + [self _rowButtonsInterviewHorizontalPadding];
|
||||
[_subtractButton setFrame:buttonFrame];
|
||||
|
||||
if (widthChanged)
|
||||
|
||||
@@ -35,6 +35,11 @@
|
||||
CPArray _allowedFileTypes @accessors(property=allowedFileTypes);
|
||||
}
|
||||
|
||||
+ (CPURL)proposedFileURLWithDocumentName:(CPString)aDocumentName
|
||||
{
|
||||
return [CPURL URLWithString:aDocumentName];
|
||||
}
|
||||
|
||||
+ (id)savePanel
|
||||
{
|
||||
return [[CPSavePanel alloc] init];
|
||||
|
||||
@@ -1253,7 +1253,7 @@ Notifies the delegate when the scroll view has finished scrolling.
|
||||
}
|
||||
|
||||
if (_timerScrollersHide)
|
||||
[_timerScrollersHide invalidate]
|
||||
[_timerScrollersHide invalidate];
|
||||
|
||||
_timerScrollersHide = [CPTimer scheduledTimerWithTimeInterval:CPScrollViewFadeOutTime target:self selector:@selector(_hideScrollers:) userInfo:nil repeats:NO];
|
||||
}
|
||||
|
||||
+11
-9
@@ -316,7 +316,7 @@ CPThemeStateScrollerKnobDark = CPThemeState("scroller-knob-dark");
|
||||
// The ordering of these tests is important. We check the knob and
|
||||
// page rects first since they may overlap with the arrows.
|
||||
|
||||
if (![self hasThemeState:CPThemeStateSelected])
|
||||
if (![self hasThemeState:CPThemeStateSelected] && ![self hasThemeState:CPThemeStateScrollViewLegacy])
|
||||
return CPScrollerNoPart;
|
||||
|
||||
if (CGRectContainsPoint([self rectForPart:CPScrollerKnob], aPoint))
|
||||
@@ -380,9 +380,10 @@ CPThemeStateScrollerKnobDark = CPThemeState("scroller-knob-dark");
|
||||
effectiveIncrementLineHeight = incrementLineSize.height + trackInset.bottom,
|
||||
slotSize = height - effectiveDecrementLineHeight - effectiveIncrementLineHeight,
|
||||
minimumKnobLength = [self currentValueForThemeAttribute:"minimum-knob-length"],
|
||||
knobVerticalInset = knobInset.top + knobInset.bottom,
|
||||
knobWidth = width - knobInset.left - knobInset.right,
|
||||
knobHeight = MAX(minimumKnobLength, (slotSize * _knobProportion)),
|
||||
knobLocation = effectiveDecrementLineHeight + (slotSize - knobHeight) * [self floatValue];
|
||||
knobHeight = MAX(minimumKnobLength, ((slotSize - knobVerticalInset) * _knobProportion)),
|
||||
knobLocation = effectiveDecrementLineHeight + (slotSize - knobHeight - knobVerticalInset) * [self floatValue] + knobInset.top;
|
||||
|
||||
_partRects[CPScrollerDecrementPage] = CGRectMake(0.0, effectiveDecrementLineHeight, width, knobLocation - effectiveDecrementLineHeight);
|
||||
_partRects[CPScrollerKnob] = CGRectMake(knobInset.left, knobLocation, knobWidth, knobHeight);
|
||||
@@ -409,9 +410,10 @@ CPThemeStateScrollerKnobDark = CPThemeState("scroller-knob-dark");
|
||||
effectiveIncrementLineWidth = incrementLineSize.width + trackInset.right,
|
||||
slotSize = width - effectiveDecrementLineWidth - effectiveIncrementLineWidth,
|
||||
minimumKnobLength = [self currentValueForThemeAttribute:"minimum-knob-length"],
|
||||
knobWidth = MAX(minimumKnobLength, (slotSize * _knobProportion)),
|
||||
knobHorizontalInset = knobInset.left + knobInset.right,
|
||||
knobWidth = MAX(minimumKnobLength, ((slotSize - knobHorizontalInset) * _knobProportion)),
|
||||
knobHeight = height - knobInset.top - knobInset.bottom,
|
||||
knobLocation = effectiveDecrementLineWidth + (slotSize - knobWidth) * [self floatValue];
|
||||
knobLocation = effectiveDecrementLineWidth + (slotSize - knobWidth - knobHorizontalInset) * [self floatValue] + knobInset.left;
|
||||
|
||||
_partRects[CPScrollerDecrementPage] = CGRectMake(effectiveDecrementLineWidth, 0.0, knobLocation - effectiveDecrementLineWidth, height);
|
||||
_partRects[CPScrollerKnob] = CGRectMake(knobLocation, knobInset.top, knobWidth, knobHeight);
|
||||
@@ -460,9 +462,6 @@ CPThemeStateScrollerKnobDark = CPThemeState("scroller-knob-dark");
|
||||
*/
|
||||
- (void)fadeOut
|
||||
{
|
||||
if ([self hasThemeState:CPThemeStateScrollViewLegacy])
|
||||
return;
|
||||
|
||||
[_animationScroller startAnimation];
|
||||
}
|
||||
|
||||
@@ -765,7 +764,10 @@ CPThemeStateScrollerKnobDark = CPThemeState("scroller-knob-dark");
|
||||
if (_timerFadeOut)
|
||||
[_timerFadeOut invalidate];
|
||||
|
||||
_timerFadeOut = [CPTimer scheduledTimerWithTimeInterval:1.2 target:self selector:@selector(_performFadeOut:) userInfo:nil repeats:NO];
|
||||
if ([self hasThemeState:CPThemeStateScrollViewLegacy])
|
||||
[self unsetThemeState:CPThemeStateSelected];
|
||||
else
|
||||
_timerFadeOut = [CPTimer scheduledTimerWithTimeInterval:1.2 target:self selector:@selector(_performFadeOut:) userInfo:nil repeats:NO];
|
||||
}
|
||||
|
||||
|
||||
|
||||
+143
-39
@@ -34,8 +34,7 @@ CPSearchFieldRecentsMenuItemTag = 1001;
|
||||
CPSearchFieldClearRecentsMenuItemTag = 1002;
|
||||
CPSearchFieldNoRecentsMenuItemTag = 1003;
|
||||
|
||||
var CPAutosavedRecentsChangedNotification = @"CPAutosavedRecentsChangedNotification",
|
||||
RECENT_SEARCH_PREFIX = @" ";
|
||||
var CPAutosavedRecentsChangedNotification = @"CPAutosavedRecentsChangedNotification";
|
||||
|
||||
/*!
|
||||
@ingroup appkit
|
||||
@@ -74,10 +73,19 @@ var CPAutosavedRecentsChangedNotification = @"CPAutosavedRecentsChangedNotificat
|
||||
@"image-cancel": [CPNull null],
|
||||
@"image-cancel-pressed": [CPNull null],
|
||||
@"image-search-inset" : CGInsetMake(0, 0, 0, 5),
|
||||
@"image-cancel-inset" : CGInsetMake(0, 5, 0, 0)
|
||||
@"image-cancel-inset" : CGInsetMake(0, 5, 0, 0),
|
||||
@"search-right-margin": 2
|
||||
};
|
||||
}
|
||||
|
||||
+ (Class)_binderClassForBinding:(CPString)aBinding
|
||||
{
|
||||
if (aBinding === CPPredicateBinding)
|
||||
return [_CPSearchFieldPredicateBinder class];
|
||||
|
||||
return [super _binderClassForBinding:aBinding];
|
||||
}
|
||||
|
||||
- (id)initWithFrame:(CGRect)frame
|
||||
{
|
||||
if (self = [super initWithFrame:frame])
|
||||
@@ -88,10 +96,6 @@ var CPAutosavedRecentsChangedNotification = @"CPAutosavedRecentsChangedNotificat
|
||||
_recentsAutosaveName = nil;
|
||||
|
||||
[self _init];
|
||||
#if PLATFORM(DOM)
|
||||
_cancelButton._DOMElement.style.cursor = "default";
|
||||
_searchButton._DOMElement.style.cursor = "default";
|
||||
#endif
|
||||
}
|
||||
|
||||
return self;
|
||||
@@ -178,7 +182,7 @@ var CPAutosavedRecentsChangedNotification = @"CPAutosavedRecentsChangedNotificat
|
||||
- (void)resetSearchButton
|
||||
{
|
||||
var button = [self searchButton],
|
||||
searchButtonImage = (_searchMenuTemplate === nil) ? [self valueForThemeAttribute:@"image-search"] : [self valueForThemeAttribute:@"image-find"];
|
||||
searchButtonImage = (_searchMenuTemplate === nil) ? [self currentValueForThemeAttribute:@"image-search"] : [self currentValueForThemeAttribute:@"image-find"];
|
||||
|
||||
[button setBordered:NO];
|
||||
[button setImageScaling:CPImageScaleAxesIndependently];
|
||||
@@ -247,8 +251,9 @@ var CPAutosavedRecentsChangedNotification = @"CPAutosavedRecentsChangedNotificat
|
||||
|
||||
if (_searchButton)
|
||||
{
|
||||
var searchBounds = [self searchButtonRectForBounds:bounds];
|
||||
leftOffset = CGRectGetMaxX(searchBounds) + 2;
|
||||
var searchBounds = [self searchButtonRectForBounds:bounds],
|
||||
rightMargin = [self currentValueForThemeAttribute:@"search-right-margin"];
|
||||
leftOffset = CGRectGetMaxX(searchBounds) + rightMargin;
|
||||
}
|
||||
|
||||
if (_cancelButton)
|
||||
@@ -387,9 +392,9 @@ var CPAutosavedRecentsChangedNotification = @"CPAutosavedRecentsChangedNotificat
|
||||
*/
|
||||
- (void)setRecentSearches:(CPArray)searches
|
||||
{
|
||||
var max = MIN([self maximumRecents], [searches count]),
|
||||
searches = [searches subarrayWithRange:CPMakeRange(0, max)];
|
||||
var max = MIN([self maximumRecents], [searches count]);
|
||||
|
||||
searches = [searches subarrayWithRange:CPMakeRange(0, max)];
|
||||
_recentSearches = searches;
|
||||
[self _autosaveRecentSearchList];
|
||||
}
|
||||
@@ -532,19 +537,16 @@ var CPAutosavedRecentsChangedNotification = @"CPAutosavedRecentsChangedNotificat
|
||||
}
|
||||
|
||||
/*!
|
||||
Provides the common case items for a recent searches menu. If there are not recent searches,
|
||||
displays a single disabled item:
|
||||
|
||||
No Recent Searches
|
||||
Provides the common case items for a recent searches menu.
|
||||
|
||||
If there are 1 more recent searches, it displays:
|
||||
|
||||
Recent Searches
|
||||
recent search 1
|
||||
recent search 2
|
||||
etc.
|
||||
Clear
|
||||
---------------------
|
||||
Clear Recent Searches
|
||||
Recent Searches
|
||||
recent search 1
|
||||
recent search 2
|
||||
etc.
|
||||
|
||||
If you wish to add items before or after the template, you can. If you put items
|
||||
before, a separator will automatically be placed before the default template item.
|
||||
@@ -569,6 +571,13 @@ var CPAutosavedRecentsChangedNotification = @"CPAutosavedRecentsChangedNotificat
|
||||
var template = [[CPMenu alloc] init],
|
||||
item;
|
||||
|
||||
item = [[CPMenuItem alloc] initWithTitle:@"Clear"
|
||||
action:@selector(_searchFieldClearRecents:)
|
||||
keyEquivalent:@""];
|
||||
[item setTag:CPSearchFieldClearRecentsMenuItemTag];
|
||||
[item setTarget:self];
|
||||
[template addItem:item];
|
||||
|
||||
item = [[CPMenuItem alloc] initWithTitle:@"Recent Searches"
|
||||
action:nil
|
||||
keyEquivalent:@""];
|
||||
@@ -583,20 +592,6 @@ var CPAutosavedRecentsChangedNotification = @"CPAutosavedRecentsChangedNotificat
|
||||
[item setTarget:self];
|
||||
[template addItem:item];
|
||||
|
||||
item = [[CPMenuItem alloc] initWithTitle:@"Clear Recent Searches"
|
||||
action:@selector(_searchFieldClearRecents:)
|
||||
keyEquivalent:@""];
|
||||
[item setTag:CPSearchFieldClearRecentsMenuItemTag];
|
||||
[item setTarget:self];
|
||||
[template addItem:item];
|
||||
|
||||
item = [[CPMenuItem alloc] initWithTitle:@"No Recent Searches"
|
||||
action:nil
|
||||
keyEquivalent:@""];
|
||||
[item setTag:CPSearchFieldNoRecentsMenuItemTag];
|
||||
[item setEnabled:NO];
|
||||
[template addItem:item];
|
||||
|
||||
return template;
|
||||
}
|
||||
|
||||
@@ -609,6 +604,8 @@ var CPAutosavedRecentsChangedNotification = @"CPAutosavedRecentsChangedNotificat
|
||||
countOfRecents = [_recentSearches count],
|
||||
numberOfItems = [_searchMenuTemplate numberOfItems];
|
||||
|
||||
[menu setAutoenablesItems:NO];
|
||||
|
||||
for (var i = 0; i < numberOfItems; i++)
|
||||
{
|
||||
var item = [[_searchMenuTemplate itemAtIndex:i] copy];
|
||||
@@ -629,8 +626,7 @@ var CPAutosavedRecentsChangedNotification = @"CPAutosavedRecentsChangedNotificat
|
||||
|
||||
for (var recentIndex = 0; recentIndex < countOfRecents; ++recentIndex)
|
||||
{
|
||||
// RECENT_SEARCH_PREFIX is a hack until CPMenuItem -setIndentationLevel works
|
||||
var recentItem = [[CPMenuItem alloc] initWithTitle:RECENT_SEARCH_PREFIX + [_recentSearches objectAtIndex:recentIndex]
|
||||
var recentItem = [[CPMenuItem alloc] initWithTitle:[_recentSearches objectAtIndex:recentIndex]
|
||||
action:itemAction
|
||||
keyEquivalent:[item keyEquivalent]];
|
||||
[item setTarget:self];
|
||||
@@ -690,7 +686,7 @@ var CPAutosavedRecentsChangedNotification = @"CPAutosavedRecentsChangedNotificat
|
||||
|
||||
- (void)_showMenu
|
||||
{
|
||||
if (_searchMenu === nil || [_searchMenu numberOfItems] === 0 || ![self isEnabled])
|
||||
if (_searchMenu === nil || [_searchMenu numberOfItems] === 0 || ![self isEnabled] || ([_recentSearches count] === 0))
|
||||
return;
|
||||
|
||||
var aFrame = [[self superview] convertRect:[self frame] toView:nil],
|
||||
@@ -718,7 +714,7 @@ var CPAutosavedRecentsChangedNotification = @"CPAutosavedRecentsChangedNotificat
|
||||
|
||||
- (void)_searchFieldSearch:(id)sender
|
||||
{
|
||||
var searchString = [[sender title] substringFromIndex:[RECENT_SEARCH_PREFIX length]];
|
||||
var searchString = [sender title];
|
||||
|
||||
if ([sender tag] != CPSearchFieldRecentsMenuItemTag)
|
||||
[self _addStringToRecentSearches:searchString];
|
||||
@@ -772,8 +768,75 @@ var CPAutosavedRecentsChangedNotification = @"CPAutosavedRecentsChangedNotificat
|
||||
_recentSearches = list;
|
||||
}
|
||||
|
||||
- (void)unbind:(CPString)aBinding
|
||||
{
|
||||
[super unbind:aBinding];
|
||||
|
||||
// our CPPredicateBinding binder adds a binding to the value.
|
||||
// this private binding has to be also removed
|
||||
if (aBinding === CPPredicateBinding)
|
||||
[[[self class] _binderClassForBinding:aBinding] unbind:CPValueBinding forObject:self];
|
||||
}
|
||||
|
||||
@end
|
||||
|
||||
#pragma mark -
|
||||
|
||||
@implementation CPSearchField (CPTrackingArea)
|
||||
{
|
||||
CPTrackingArea _searchButtonTrackingArea;
|
||||
CPTrackingArea _cancelButtonTrackingArea;
|
||||
}
|
||||
|
||||
- (void)updateTrackingAreas
|
||||
{
|
||||
if (_searchButtonTrackingArea)
|
||||
{
|
||||
[self removeTrackingArea:_searchButtonTrackingArea];
|
||||
_searchButtonTrackingArea = nil;
|
||||
}
|
||||
|
||||
if (_cancelButtonTrackingArea)
|
||||
{
|
||||
[self removeTrackingArea:_cancelButtonTrackingArea];
|
||||
_cancelButtonTrackingArea = nil;
|
||||
}
|
||||
|
||||
if (_searchButton)
|
||||
{
|
||||
_searchButtonTrackingArea = [[CPTrackingArea alloc] initWithRect:[_searchButton frame]
|
||||
options:CPTrackingCursorUpdate | CPTrackingActiveInKeyWindow
|
||||
owner:self
|
||||
userInfo:@{ @"isButton": YES }];
|
||||
|
||||
[self addTrackingArea:_searchButtonTrackingArea];
|
||||
}
|
||||
|
||||
if (_cancelButton)
|
||||
{
|
||||
_cancelButtonTrackingArea = [[CPTrackingArea alloc] initWithRect:[_cancelButton frame]
|
||||
options:CPTrackingCursorUpdate | CPTrackingActiveInKeyWindow
|
||||
owner:self
|
||||
userInfo:@{ @"isButton": YES }];
|
||||
|
||||
[self addTrackingArea:_cancelButtonTrackingArea];
|
||||
}
|
||||
|
||||
[super updateTrackingAreas];
|
||||
}
|
||||
|
||||
- (void)cursorUpdate:(CPEvent)anEvent
|
||||
{
|
||||
if ([[[anEvent trackingArea] userInfo] objectForKey:@"isButton"])
|
||||
[[CPCursor arrowCursor] set];
|
||||
else
|
||||
[super cursorUpdate:anEvent];
|
||||
}
|
||||
|
||||
@end
|
||||
|
||||
#pragma mark -
|
||||
|
||||
var CPRecentsAutosaveNameKey = @"CPRecentsAutosaveNameKey",
|
||||
CPSendsWholeSearchStringKey = @"CPSendsWholeSearchStringKey",
|
||||
CPSendsSearchStringImmediatelyKey = @"CPSendsSearchStringImmediatelyKey",
|
||||
@@ -826,3 +889,44 @@ var CPRecentsAutosaveNameKey = @"CPRecentsAutosaveNameKey",
|
||||
}
|
||||
|
||||
@end
|
||||
|
||||
@implementation _CPSearchFieldPredicateBinder : CPBinder
|
||||
{
|
||||
CPArrayController _controller;
|
||||
CPString _predicateFormat;
|
||||
}
|
||||
|
||||
- (void)setValue:(id)aValue forBinding:(CPString)aBinding
|
||||
{
|
||||
if (aBinding === CPPredicateBinding)
|
||||
{
|
||||
var options = [_info objectForKey:CPOptionsKey];
|
||||
|
||||
_controller = [_info objectForKey:CPObservedObjectKey];
|
||||
_predicateFormat = [options objectForKey:"CPPredicateFormat"];
|
||||
[_source bind:CPValueBinding toObject:self withKeyPath:"searchFieldValue" options:nil];
|
||||
}
|
||||
}
|
||||
|
||||
- (void)setSearchFieldValue:(CPString)aValue
|
||||
{
|
||||
var destination = [_info objectForKey:CPObservedObjectKey],
|
||||
keyPath = [_info objectForKey:CPObservedKeyPathKey];
|
||||
|
||||
var formatString = _predicateFormat.replace(/\$value/g, "%@");
|
||||
[self suppressSpecificNotificationFromObject:destination keyPath:keyPath];
|
||||
|
||||
if (aValue)
|
||||
[_controller setFilterPredicate:[CPPredicate predicateWithFormat:formatString, aValue]];
|
||||
else
|
||||
[_controller setFilterPredicate:nil];
|
||||
|
||||
[self unsuppressSpecificNotificationFromObject:destination keyPath:keyPath];
|
||||
}
|
||||
- (CPString)searchFieldValue
|
||||
{
|
||||
return [_source stringValue];
|
||||
}
|
||||
|
||||
@end
|
||||
|
||||
|
||||
+57
-48
@@ -604,7 +604,12 @@ CPSegmentSwitchTrackingMomentary = 2;
|
||||
- (CPView)createEphemeralSubviewNamed:(CPString)aName
|
||||
{
|
||||
if ([aName hasPrefix:@"segment-content"])
|
||||
return [[_CPImageAndTextView alloc] initWithFrame:CGRectMakeZero()];
|
||||
{
|
||||
var view = [[_CPImageAndTextView alloc] initWithFrame:CGRectMakeZero()];
|
||||
[view _setUsesSingleLineMode:YES];
|
||||
|
||||
return view;
|
||||
}
|
||||
|
||||
return [[CPView alloc] initWithFrame:CGRectMakeZero()];
|
||||
}
|
||||
@@ -867,64 +872,68 @@ CPSegmentSwitchTrackingMomentary = 2;
|
||||
var type = [anEvent type],
|
||||
location = [self convertPoint:[anEvent locationInWindow] fromView:nil];
|
||||
|
||||
if (type == CPLeftMouseUp)
|
||||
switch (type)
|
||||
{
|
||||
if (_trackingSegment == -1)
|
||||
case CPLeftMouseUp:
|
||||
|
||||
if (_trackingSegment === CPNotFound)
|
||||
return;
|
||||
|
||||
if (_trackingSegment === [self testSegment:location])
|
||||
{
|
||||
if (_trackingMode == CPSegmentSwitchTrackingSelectAny)
|
||||
{
|
||||
[self setSelected:![self isSelectedForSegment:_trackingSegment] forSegment:_trackingSegment];
|
||||
|
||||
// With ANY, _selectedSegment means last pressed.
|
||||
_selectedSegment = _trackingSegment;
|
||||
}
|
||||
else
|
||||
[self setSelected:YES forSegment:_trackingSegment];
|
||||
|
||||
[self sendAction:[self action] to:[self target]];
|
||||
|
||||
if (_trackingMode == CPSegmentSwitchTrackingMomentary)
|
||||
{
|
||||
[self setSelected:NO forSegment:_trackingSegment];
|
||||
|
||||
_selectedSegment = CPNotFound;
|
||||
}
|
||||
}
|
||||
|
||||
[self drawSegmentBezel:_trackingSegment highlight:NO];
|
||||
|
||||
_trackingSegment = CPNotFound;
|
||||
|
||||
return;
|
||||
|
||||
if (_trackingSegment === [self testSegment:location])
|
||||
{
|
||||
if (_trackingMode == CPSegmentSwitchTrackingSelectAny)
|
||||
case CPLeftMouseDown:
|
||||
|
||||
var trackingSegment = [self testSegment:location];
|
||||
if (trackingSegment > CPNotFound && [self isEnabledForSegment:trackingSegment])
|
||||
{
|
||||
[self setSelected:![self isSelectedForSegment:_trackingSegment] forSegment:_trackingSegment];
|
||||
|
||||
// With ANY, _selectedSegment means last pressed.
|
||||
_selectedSegment = _trackingSegment;
|
||||
_trackingHighlighted = YES;
|
||||
_trackingSegment = trackingSegment;
|
||||
[self drawSegmentBezel:_trackingSegment highlight:YES];
|
||||
}
|
||||
else
|
||||
[self setSelected:YES forSegment:_trackingSegment];
|
||||
|
||||
[self sendAction:[self action] to:[self target]];
|
||||
break;
|
||||
|
||||
if (_trackingMode == CPSegmentSwitchTrackingMomentary)
|
||||
case CPLeftMouseDragged:
|
||||
|
||||
if (_trackingSegment === CPNotFound)
|
||||
return;
|
||||
|
||||
var highlighted = [self testSegment:location] === _trackingSegment;
|
||||
|
||||
if (highlighted != _trackingHighlighted)
|
||||
{
|
||||
[self setSelected:NO forSegment:_trackingSegment];
|
||||
_trackingHighlighted = highlighted;
|
||||
|
||||
_selectedSegment = CPNotFound;
|
||||
[self drawSegmentBezel:_trackingSegment highlight:_trackingHighlighted];
|
||||
}
|
||||
}
|
||||
|
||||
[self drawSegmentBezel:_trackingSegment highlight:NO];
|
||||
|
||||
_trackingSegment = -1;
|
||||
|
||||
return;
|
||||
}
|
||||
|
||||
if (type == CPLeftMouseDown)
|
||||
{
|
||||
var trackingSegment = [self testSegment:location];
|
||||
if (trackingSegment > -1 && [self isEnabledForSegment:trackingSegment])
|
||||
{
|
||||
_trackingHighlighted = YES;
|
||||
_trackingSegment = trackingSegment;
|
||||
[self drawSegmentBezel:_trackingSegment highlight:YES];
|
||||
}
|
||||
}
|
||||
|
||||
else if (type == CPLeftMouseDragged)
|
||||
{
|
||||
if (_trackingSegment == -1)
|
||||
return;
|
||||
|
||||
var highlighted = [self testSegment:location] === _trackingSegment;
|
||||
|
||||
if (highlighted != _trackingHighlighted)
|
||||
{
|
||||
_trackingHighlighted = highlighted;
|
||||
|
||||
[self drawSegmentBezel:_trackingSegment highlight:_trackingHighlighted];
|
||||
}
|
||||
break;
|
||||
}
|
||||
|
||||
[CPApp setTarget:self selector:@selector(trackSegment:) forNextEventMatchingMask:CPLeftMouseDraggedMask | CPLeftMouseUpMask untilDate:nil inMode:nil dequeue:YES];
|
||||
|
||||
+92
-60
@@ -117,6 +117,7 @@ var ShouldSuppressResizeNotifications = 1,
|
||||
CPArray _DOMDividerElements;
|
||||
CPString _dividerImagePath;
|
||||
int _drawingDivider;
|
||||
BOOL _isTracking;
|
||||
|
||||
CPString _autosaveName;
|
||||
BOOL _shouldAutosave;
|
||||
@@ -158,6 +159,7 @@ var ShouldSuppressResizeNotifications = 1,
|
||||
_buttonBars = [];
|
||||
|
||||
_shouldAutosave = YES;
|
||||
_isTracking = NO;
|
||||
|
||||
[self _setVertical:YES];
|
||||
}
|
||||
@@ -213,6 +215,9 @@ var ShouldSuppressResizeNotifications = 1,
|
||||
[_subviews[index] setFrame:CGRectMake(0, ROUND((eachSize + dividerThickness) * index), frame.size.width, eachSize)];
|
||||
}
|
||||
|
||||
if (_DOMDividerElements[_drawingDivider])
|
||||
[self _setupDOMDivider];
|
||||
|
||||
[self setNeedsDisplay:YES];
|
||||
[self _postNotificationDidResize];
|
||||
|
||||
@@ -363,9 +368,9 @@ var ShouldSuppressResizeNotifications = 1,
|
||||
_DOMDividerElements[_drawingDivider].style.backgroundRepeat = "repeat";
|
||||
|
||||
CPDOMDisplayServerAppendChild(_DOMElement, _DOMDividerElements[_drawingDivider]);
|
||||
[self _setupDOMDivider];
|
||||
}
|
||||
|
||||
[self _setupDOMDivider];
|
||||
CPDOMDisplayServerSetStyleLeftTop(_DOMDividerElements[_drawingDivider], NULL, CGRectGetMinX(aRect), CGRectGetMinY(aRect));
|
||||
CPDOMDisplayServerSetStyleSize(_DOMDividerElements[_drawingDivider], CGRectGetWidth(aRect), CGRectGetHeight(aRect));
|
||||
#endif
|
||||
@@ -452,17 +457,26 @@ var ShouldSuppressResizeNotifications = 1,
|
||||
return nil;
|
||||
|
||||
var point = [self convertPoint:aPoint fromView:[self superview]],
|
||||
count = [_subviews count] - 1;
|
||||
dividerIndex = [self _dividerAtPoint:point];
|
||||
|
||||
for (var i = 0; i < count; i++)
|
||||
{
|
||||
if ([self cursorAtPoint:point hitDividerAtIndex:i])
|
||||
return self;
|
||||
}
|
||||
if (dividerIndex !== CPNotFound)
|
||||
return self;
|
||||
|
||||
return [super hitTest:aPoint];
|
||||
}
|
||||
|
||||
- (CPInteger)_dividerAtPoint:(CGPoint)aPoint
|
||||
{
|
||||
var count = [_subviews count] - 1;
|
||||
|
||||
for (var i = 0; i < count; i++)
|
||||
{
|
||||
if ([self cursorAtPoint:aPoint hitDividerAtIndex:i])
|
||||
return i;
|
||||
}
|
||||
|
||||
return CPNotFound;
|
||||
}
|
||||
/*
|
||||
Tracks the divider.
|
||||
@param anEvent the input event
|
||||
@@ -473,12 +487,13 @@ var ShouldSuppressResizeNotifications = 1,
|
||||
|
||||
if (type == CPLeftMouseUp)
|
||||
{
|
||||
// We disabled autosaving during tracking.
|
||||
_shouldAutosave = YES;
|
||||
|
||||
if (_currentDivider != CPNotFound)
|
||||
if ([anEvent clickCount] == 2 || (_isTracking && _currentDivider != CPNotFound))
|
||||
{
|
||||
// We disabled autosaving during tracking.
|
||||
_shouldAutosave = YES;
|
||||
_currentDivider = CPNotFound;
|
||||
_isTracking = NO;
|
||||
|
||||
[self _autosave];
|
||||
[self _updateResizeCursor:anEvent];
|
||||
}
|
||||
@@ -488,59 +503,55 @@ var ShouldSuppressResizeNotifications = 1,
|
||||
|
||||
if (type == CPLeftMouseDown)
|
||||
{
|
||||
var point = [self convertPoint:[anEvent locationInWindow] fromView:nil],
|
||||
count = [_subviews count] - 1;
|
||||
var point = [self convertPoint:[anEvent locationInWindow] fromView:nil];
|
||||
_currentDivider = [self _dividerAtPoint:point];
|
||||
|
||||
_currentDivider = CPNotFound;
|
||||
var frame = [_subviews[_currentDivider] frame],
|
||||
startPosition = frame.origin[_originComponent] + frame.size[_sizeComponent];
|
||||
|
||||
for (var i = 0; i < count; i++)
|
||||
if ([anEvent clickCount] == 2 &&
|
||||
[self _delegateRespondsToSplitViewCanCollapseSubview] &&
|
||||
[self _delegateRespondsToSplitViewshouldCollapseSubviewForDoubleClickOnDividerAtIndex])
|
||||
{
|
||||
var frame = [_subviews[i] frame],
|
||||
startPosition = frame.origin[_originComponent] + frame.size[_sizeComponent];
|
||||
var minPosition = [self minPossiblePositionOfDividerAtIndex:_currentDivider],
|
||||
maxPosition = [self maxPossiblePositionOfDividerAtIndex:_currentDivider],
|
||||
preCollapsePosition = [_preCollapsePositions objectForKey:"" + _currentDivider] || 0;
|
||||
|
||||
if ([self cursorAtPoint:point hitDividerAtIndex:i])
|
||||
if ([self _sendDelegateSplitViewCanCollapseSubview:_subviews[_currentDivider]] && [self _sendDelegateSplitViewShouldCollapseSubview:_subviews[_currentDivider] forDoubleClickOnDividerAtIndex:_currentDivider])
|
||||
{
|
||||
if ([anEvent clickCount] == 2 &&
|
||||
[self _delegateRespondsToSplitViewCanCollapseSubview] &&
|
||||
[self _delegateRespondsToSplitViewshouldCollapseSubviewForDoubleClickOnDividerAtIndex])
|
||||
{
|
||||
var minPosition = [self minPossiblePositionOfDividerAtIndex:i],
|
||||
maxPosition = [self maxPossiblePositionOfDividerAtIndex:i],
|
||||
preCollapsePosition = [_preCollapsePositions objectForKey:"" + i] || 0;
|
||||
|
||||
if ([self _sendDelegateSplitViewCanCollapseSubview:_subviews[i]] && [self _sendDelegateSplitViewShouldCollapseSubview:_subviews[i] forDoubleClickOnDividerAtIndex:i])
|
||||
{
|
||||
if ([self isSubviewCollapsed:_subviews[i]])
|
||||
[self setPosition:preCollapsePosition ? preCollapsePosition : (minPosition + (maxPosition - minPosition) / 2) ofDividerAtIndex:i];
|
||||
else
|
||||
[self setPosition:minPosition ofDividerAtIndex:i];
|
||||
}
|
||||
else if ([self _sendDelegateSplitViewCanCollapseSubview:_subviews[i + 1]] && [self _sendDelegateSplitViewShouldCollapseSubview:_subviews[i + 1] forDoubleClickOnDividerAtIndex:i])
|
||||
{
|
||||
if ([self isSubviewCollapsed:_subviews[i + 1]])
|
||||
[self setPosition:preCollapsePosition ? preCollapsePosition : (minPosition + (maxPosition - minPosition) / 2) ofDividerAtIndex:i];
|
||||
else
|
||||
[self setPosition:maxPosition ofDividerAtIndex:i];
|
||||
}
|
||||
}
|
||||
if ([self isSubviewCollapsed:_subviews[_currentDivider]])
|
||||
[self setPosition:preCollapsePosition ? preCollapsePosition : (minPosition + (maxPosition - minPosition) / 2) ofDividerAtIndex:_currentDivider];
|
||||
else
|
||||
{
|
||||
_currentDivider = i;
|
||||
_initialOffset = startPosition - point[_originComponent];
|
||||
|
||||
// Don't autosave during a resize. We'll wait until it's done.
|
||||
_shouldAutosave = NO;
|
||||
[self _postNotificationWillResize];
|
||||
}
|
||||
[self setPosition:minPosition ofDividerAtIndex:_currentDivider];
|
||||
}
|
||||
else if ([self _sendDelegateSplitViewCanCollapseSubview:_subviews[_currentDivider + 1]] && [self _sendDelegateSplitViewShouldCollapseSubview:_subviews[_currentDivider + 1] forDoubleClickOnDividerAtIndex:_currentDivider])
|
||||
{
|
||||
if ([self isSubviewCollapsed:_subviews[_currentDivider + 1]])
|
||||
[self setPosition:preCollapsePosition ? preCollapsePosition : (minPosition + (maxPosition - minPosition) / 2) ofDividerAtIndex:_currentDivider];
|
||||
else
|
||||
[self setPosition:maxPosition ofDividerAtIndex:_currentDivider];
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
_initialOffset = startPosition - point[_originComponent];
|
||||
// Don't autosave during a resize. We'll wait until it's done.
|
||||
_shouldAutosave = NO;
|
||||
[self _postNotificationWillResize];
|
||||
}
|
||||
|
||||
if (_currentDivider === CPNotFound)
|
||||
return;
|
||||
}
|
||||
|
||||
else if (type == CPLeftMouseDragged && _currentDivider != CPNotFound)
|
||||
{
|
||||
if (!_isTracking)
|
||||
{
|
||||
// Don't autosave during a resize. We'll wait until it's done.
|
||||
_shouldAutosave = NO;
|
||||
[self _postNotificationWillResize];
|
||||
|
||||
_isTracking = YES;
|
||||
}
|
||||
|
||||
var point = [self convertPoint:[anEvent locationInWindow] fromView:nil];
|
||||
|
||||
[self setPosition:(point[_originComponent] + _initialOffset) ofDividerAtIndex:_currentDivider];
|
||||
@@ -553,8 +564,11 @@ var ShouldSuppressResizeNotifications = 1,
|
||||
|
||||
- (void)mouseDown:(CPEvent)anEvent
|
||||
{
|
||||
// FIXME: This should not trap events if not on a divider!
|
||||
[self trackDivider:anEvent];
|
||||
var point = [self convertPoint:[anEvent locationInWindow] fromView:nil],
|
||||
dividerIndex = [self _dividerAtPoint:point];
|
||||
|
||||
if (dividerIndex !== CPNotFound)
|
||||
[self trackDivider:anEvent];
|
||||
}
|
||||
|
||||
- (void)viewDidMoveToWindow
|
||||
@@ -1187,18 +1201,35 @@ The sum of the views and the sum of the dividers should be equal to the size of
|
||||
@end
|
||||
|
||||
@implementation CPSplitView (CPTrackingArea)
|
||||
{
|
||||
CPMutableArray _splitViewTrackingAreas;
|
||||
}
|
||||
|
||||
- (void)updateTrackingAreas
|
||||
{
|
||||
[self removeAllTrackingAreas];
|
||||
if (_splitViewTrackingAreas)
|
||||
{
|
||||
for (var i = 0, count = [_splitViewTrackingAreas count]; i < count; i++)
|
||||
[self removeTrackingArea:_splitViewTrackingAreas[i]];
|
||||
|
||||
_splitViewTrackingAreas = nil;
|
||||
}
|
||||
|
||||
var options = CPTrackingCursorUpdate | CPTrackingActiveInKeyWindow;
|
||||
|
||||
|
||||
_splitViewTrackingAreas = @[];
|
||||
|
||||
for (var i = 0; i < _subviews.length - 1; i++)
|
||||
[self addTrackingArea:[[CPTrackingArea alloc] initWithRect:[self effectiveRectOfDividerAtIndex:i]
|
||||
options:options
|
||||
owner:self
|
||||
userInfo:nil]];
|
||||
{
|
||||
[_splitViewTrackingAreas addObject:[[CPTrackingArea alloc] initWithRect:[self effectiveRectOfDividerAtIndex:i]
|
||||
options:options
|
||||
owner:self
|
||||
userInfo:nil]];
|
||||
|
||||
[self addTrackingArea:_splitViewTrackingAreas[i]];
|
||||
}
|
||||
|
||||
[super updateTrackingAreas];
|
||||
}
|
||||
|
||||
- (void)cursorUpdate:(CPEvent)anEvent
|
||||
@@ -1438,6 +1469,7 @@ var CPSplitViewDelegateKey = "CPSplitViewDelegateKey",
|
||||
|
||||
_currentDivider = CPNotFound;
|
||||
_shouldAutosave = YES;
|
||||
_isTracking = NO;
|
||||
|
||||
_DOMDividerElements = [];
|
||||
|
||||
|
||||
@@ -24,9 +24,13 @@
|
||||
|
||||
@import "CGGeometry.j"
|
||||
@import "CPPlatformString.j"
|
||||
@import "CPFont.j"
|
||||
@import "CPCompatibility.j"
|
||||
|
||||
|
||||
var CPStringSizeWithFontInWidthCache = {};
|
||||
var CPStringSizeWithFontInWidthCache = [],
|
||||
CPStringSizeWithFontHeightCache = [],
|
||||
CPStringSizeMeasuringContext;
|
||||
|
||||
CPStringSizeCachingEnabled = YES;
|
||||
|
||||
@@ -53,21 +57,67 @@ CPStringSizeCachingEnabled = YES;
|
||||
return [self sizeWithFont:aFont inWidth:NULL];
|
||||
}
|
||||
|
||||
- (CGSize)sizeWithFont:(CPFont)aFont inWidth:(float)aWidth
|
||||
+ (void) initialize
|
||||
{
|
||||
if ([self class] != [CPString class])
|
||||
return;
|
||||
|
||||
#if PLATFORM(DOM)
|
||||
if (CPFeatureIsCompatible(CPHTMLCanvasFeature) && !CPStringSizeMeasuringContext)
|
||||
CPStringSizeMeasuringContext = CGBitmapGraphicsContextCreate();
|
||||
#endif
|
||||
}
|
||||
|
||||
- (CGSize)_sizeWithFont:(CPFont)aFont inWidth:(float)aWidth
|
||||
{
|
||||
var size;
|
||||
|
||||
#if PLATFORM(DOM)
|
||||
if (!CPStringSizeCachingEnabled)
|
||||
return [CPPlatformString sizeOfString:self withFont:aFont forWidth:aWidth];
|
||||
|
||||
var cacheKey = self + [aFont cssString] + aWidth,
|
||||
size = CPStringSizeWithFontInWidthCache[cacheKey];
|
||||
var sizeCacheForFont = CPStringSizeWithFontInWidthCache[self];
|
||||
|
||||
if (size === undefined)
|
||||
{
|
||||
if (sizeCacheForFont === undefined)
|
||||
sizeCacheForFont = CPStringSizeWithFontInWidthCache[self] = [];
|
||||
|
||||
if (!aWidth)
|
||||
aWidth = '0';
|
||||
|
||||
var cssString = [aFont cssString],
|
||||
cacheKey = cssString + '_' + aWidth;
|
||||
|
||||
size = sizeCacheForFont[cacheKey];
|
||||
|
||||
if (size !== undefined && sizeCacheForFont.hasOwnProperty(cacheKey))
|
||||
return CGSizeMakeCopy(size);
|
||||
|
||||
if (!CPFeatureIsCompatible(CPHTMLCanvasFeature) || aWidth > 0)
|
||||
size = [CPPlatformString sizeOfString:self withFont:aFont forWidth:aWidth];
|
||||
CPStringSizeWithFontInWidthCache[cacheKey] = size;
|
||||
else
|
||||
{
|
||||
if (CPPlatformHasBug(CPTextSizingAlwaysNeedsSetFontBug) || CPStringSizeMeasuringContext.font !== cssString)
|
||||
CPStringSizeMeasuringContext.font = cssString;
|
||||
|
||||
var fontHeight = CPStringSizeWithFontHeightCache[cssString];
|
||||
|
||||
if (fontHeight === undefined)
|
||||
fontHeight = CPStringSizeWithFontHeightCache[cssString] = [aFont defaultLineHeightForFont];
|
||||
|
||||
size = CGSizeMake(CPStringSizeMeasuringContext.measureText(self).width, fontHeight);
|
||||
}
|
||||
|
||||
sizeCacheForFont[cacheKey] = size;
|
||||
#else
|
||||
size = CGSizeMake(0, 0);
|
||||
#endif
|
||||
return CGSizeMakeCopy(size);
|
||||
}
|
||||
|
||||
- (CGSize)sizeWithFont:(CPFont)aFont inWidth:(float)aWidth
|
||||
{
|
||||
var size = [self _sizeWithFont:aFont inWidth:aWidth];
|
||||
return CGSizeMake(CEIL(size.width), size.height);
|
||||
}
|
||||
|
||||
@end
|
||||
|
||||
+127
-22
@@ -35,6 +35,7 @@ CPNoTabsLineBorder = 5; //Has no tabs and displays a line border.
|
||||
CPNoTabsNoBorder = 6; //Displays no tabs and no border.
|
||||
|
||||
@class _CPTabViewBox
|
||||
@class _CPSegmentedControl
|
||||
|
||||
var CPTabViewDidSelectTabViewItemSelector = 1 << 1,
|
||||
CPTabViewShouldSelectTabViewItemSelector = 1 << 2,
|
||||
@@ -65,9 +66,9 @@ var CPTabViewDidSelectTabViewItemSelector = 1 << 1,
|
||||
{
|
||||
CPArray _items;
|
||||
|
||||
CPSegmentedControl _tabs;
|
||||
_CPSegmentedControl _tabs;
|
||||
_CPTabViewBox _box;
|
||||
CPView _placeHolderView;
|
||||
CPView _placeholderView @accessors;
|
||||
|
||||
CPTabViewItem _selectedTabViewItem;
|
||||
|
||||
@@ -92,8 +93,8 @@ var CPTabViewDidSelectTabViewItemSelector = 1 << 1,
|
||||
|
||||
- (void)_init
|
||||
{
|
||||
_tabs = [[CPSegmentedControl alloc] initWithFrame:CGRectMakeZero()];
|
||||
[_tabs setHitTests:NO];
|
||||
_tabs = [[_CPSegmentedControl alloc] initWithFrame:CGRectMakeZero()];
|
||||
[_tabs setTabView:self];
|
||||
[_tabs setSegments:[CPArray array]];
|
||||
|
||||
var height = [_tabs valueForThemeAttribute:@"min-size"].height;
|
||||
@@ -106,7 +107,7 @@ var CPTabViewDidSelectTabViewItemSelector = 1 << 1,
|
||||
[self addSubview:_box];
|
||||
[self addSubview:_tabs];
|
||||
|
||||
_placeHolderView = nil;
|
||||
_placeholderView = nil;
|
||||
}
|
||||
|
||||
- (CPArray)items
|
||||
@@ -136,6 +137,8 @@ var CPTabViewDidSelectTabViewItemSelector = 1 << 1,
|
||||
|
||||
- (void)_insertTabViewItems:(CPArray)tabViewItems atIndexes:(CPIndexSet)indexes
|
||||
{
|
||||
var prevItemsCount = [self numberOfTabViewItems];
|
||||
|
||||
[_tabs insertSegments:tabViewItems atIndexes:indexes];
|
||||
[tabViewItems makeObjectsPerformSelector:@selector(_setTabView:) withObject:self];
|
||||
|
||||
@@ -143,6 +146,10 @@ var CPTabViewDidSelectTabViewItemSelector = 1 << 1,
|
||||
[self _reverseSetContent];
|
||||
|
||||
[self _sendDelegateTabViewDidChangeNumberOfTabViewItems];
|
||||
|
||||
// Do not allow empty selection if selection bindings are not enabled.
|
||||
if (prevItemsCount == 0 && [self numberOfTabViewItems] > 0 && ![self _isSelectionBinded])
|
||||
[self _selectTabViewItemAtIndex:0];
|
||||
}
|
||||
|
||||
/*!
|
||||
@@ -169,7 +176,7 @@ var CPTabViewDidSelectTabViewItemSelector = 1 << 1,
|
||||
- (void)_didRemoveTabViewItem:(CPTabViewItem)aTabViewItem atIndex:(CPInteger)idx
|
||||
{
|
||||
// If the selection is managed by bindings, let the binder do that.
|
||||
if ([self binderForBinding:CPSelectionIndexesBinding] || [self binderForBinding:CPSelectedIndexBinding])
|
||||
if ([self _isSelectionBinded])
|
||||
return;
|
||||
|
||||
if (_selectedTabViewItem == aTabViewItem)
|
||||
@@ -335,13 +342,40 @@ var CPTabViewDidSelectTabViewItemSelector = 1 << 1,
|
||||
|
||||
[_tabs setSelectedSegment:anIndex];
|
||||
_selectedTabViewItem = aTabViewItem;
|
||||
[self _displayItemView:[aTabViewItem view]];
|
||||
[self _loadTabViewItem:aTabViewItem];
|
||||
|
||||
[self _sendDelegateDidSelectTabViewItem:aTabViewItem];
|
||||
|
||||
return YES;
|
||||
}
|
||||
|
||||
- (void)_loadTabViewItem:(CPTabViewItem)aTabViewItem
|
||||
{
|
||||
var controller = [aTabViewItem viewController];
|
||||
|
||||
if (controller !== nil && ![controller isViewLoaded])
|
||||
{
|
||||
[controller loadViewWithCompletionHandler:function(view, error)
|
||||
{
|
||||
if (error !== nil)
|
||||
{
|
||||
CPLog.warn("Could not load the view for item " + aTabViewItem + ". " + error);
|
||||
}
|
||||
else if (view !== nil)
|
||||
{
|
||||
[aTabViewItem setView:view];
|
||||
|
||||
if ([self selectedTabViewItem] == aTabViewItem)
|
||||
[self _displayItemView:view];
|
||||
}
|
||||
}];
|
||||
}
|
||||
else
|
||||
{
|
||||
[self _displayItemView:[aTabViewItem view]];
|
||||
}
|
||||
}
|
||||
|
||||
/*!
|
||||
Returns the current item being displayed.
|
||||
@return the tab view item currenly being displayed by the receiver
|
||||
@@ -492,14 +526,6 @@ var CPTabViewDidSelectTabViewItemSelector = 1 << 1,
|
||||
return [_box backgroundColor];
|
||||
}
|
||||
|
||||
- (void)mouseDown:(CPEvent)anEvent
|
||||
{
|
||||
var segmentIndex = [_tabs testSegment:[_tabs convertPoint:[anEvent locationInWindow] fromView:nil]];
|
||||
|
||||
if (segmentIndex != CPNotFound && [self selectTabViewItemAtIndex:segmentIndex])
|
||||
[_tabs trackSegment:anEvent];
|
||||
}
|
||||
|
||||
- (void)_repositionTabs
|
||||
{
|
||||
var horizontalCenterOfSelf = CGRectGetWidth([self bounds]) / 2,
|
||||
@@ -584,10 +610,15 @@ var CPTabViewDidSelectTabViewItemSelector = 1 << 1,
|
||||
|
||||
- (CPBinder)binderForBinding:(CPString)aBinding
|
||||
{
|
||||
var cls = [[self class] _binderClassForBinding:aBinding]
|
||||
var cls = [[self class] _binderClassForBinding:aBinding];
|
||||
return [cls getBinding:aBinding forObject:self];
|
||||
}
|
||||
|
||||
- (BOOL)_isSelectionBinded
|
||||
{
|
||||
return [self binderForBinding:CPSelectionIndexesBinding] || [self binderForBinding:CPSelectedIndexBinding];
|
||||
}
|
||||
|
||||
- (void)setItems:(CPArray)tabViewItems
|
||||
{
|
||||
if ([tabViewItems isEqualToArray:[_tabs segments]])
|
||||
@@ -614,17 +645,17 @@ var CPTabViewDidSelectTabViewItemSelector = 1 << 1,
|
||||
|
||||
- (void)_displayPlaceholder:(CPString)aPlaceholder
|
||||
{
|
||||
if (_placeHolderView == nil)
|
||||
if (_placeholderView == nil)
|
||||
{
|
||||
_placeHolderView = [[CPView alloc] initWithFrame:CGRectMakeZero()];
|
||||
_placeholderView = [[CPView alloc] initWithFrame:CGRectMakeZero()];
|
||||
var textField = [[CPTextField alloc] initWithFrame:CGRectMakeZero()];
|
||||
[textField setTag:1000];
|
||||
[textField setTextColor:[CPColor whiteColor]];
|
||||
[textField setFont:[CPFont boldFontWithName:@"Geneva" size:18 italic:YES]];
|
||||
[_placeHolderView addSubview:textField];
|
||||
[_placeholderView addSubview:textField];
|
||||
}
|
||||
|
||||
var textField = [_placeHolderView viewWithTag:1000];
|
||||
var textField = [_placeholderView viewWithTag:1000];
|
||||
[textField setStringValue:aPlaceholder];
|
||||
[textField sizeToFit];
|
||||
|
||||
@@ -634,7 +665,7 @@ var CPTabViewDidSelectTabViewItemSelector = 1 << 1,
|
||||
|
||||
[textField setFrameOrigin:origin];
|
||||
|
||||
[self _displayItemView:_placeHolderView];
|
||||
[self _displayItemView:_placeholderView];
|
||||
}
|
||||
|
||||
#pragma mark -
|
||||
@@ -848,4 +879,78 @@ var CPTabViewItemsKey = "CPTabViewItemsKey",
|
||||
return [super hitTest:aPoint];
|
||||
}
|
||||
|
||||
@end
|
||||
@end
|
||||
|
||||
# pragma mark -
|
||||
|
||||
// This subclass of CPSegmentedControl implements specific behaviour needed by CPTabView, which
|
||||
// differs in some ways from normal CPSegmentedControl habits :
|
||||
//
|
||||
// - all items are enabled
|
||||
// - when you select a tab (mouse down, holding) and drag upon another tab, the second one reacts
|
||||
// (showing a pushed state) and can then be selected by releasing the mouse over it.
|
||||
// - a delegate has the opportunity to accept or not the selection of an item
|
||||
|
||||
@implementation _CPSegmentedControl : CPSegmentedControl
|
||||
{
|
||||
CPTabView _tabView @accessors(property=tabView);
|
||||
}
|
||||
|
||||
- (void)trackSegment:(CPEvent)anEvent
|
||||
{
|
||||
var type = [anEvent type],
|
||||
location = [self convertPoint:[anEvent locationInWindow] fromView:nil],
|
||||
currentSegment = [self testSegment:location];
|
||||
|
||||
switch (type)
|
||||
{
|
||||
case CPLeftMouseUp:
|
||||
|
||||
if (_trackingSegment === CPNotFound)
|
||||
return;
|
||||
|
||||
if ((_trackingSegment !== _selectedSegment) &&
|
||||
[_tabView _sendDelegateShouldSelectTabViewItem:[_tabView tabViewItemAtIndex:_trackingSegment]])
|
||||
{
|
||||
[self setSelected:YES forSegment:_trackingSegment];
|
||||
_selectedSegment = _trackingSegment;
|
||||
[_tabView selectTabViewItemAtIndex:_selectedSegment];
|
||||
}
|
||||
|
||||
[self drawSegmentBezel:_trackingSegment highlight:NO];
|
||||
|
||||
_trackingSegment = CPNotFound;
|
||||
|
||||
return;
|
||||
|
||||
case CPLeftMouseDown:
|
||||
|
||||
if (currentSegment > CPNotFound)
|
||||
{
|
||||
_trackingSegment = currentSegment;
|
||||
[self drawSegmentBezel:_trackingSegment highlight:YES];
|
||||
}
|
||||
|
||||
break;
|
||||
|
||||
case CPLeftMouseDragged:
|
||||
|
||||
if (_trackingSegment !== currentSegment)
|
||||
{
|
||||
if (_trackingSegment > CPNotFound)
|
||||
[self drawSegmentBezel:_trackingSegment highlight:NO];
|
||||
|
||||
_trackingSegment = currentSegment;
|
||||
|
||||
if (_trackingSegment > CPNotFound)
|
||||
[self drawSegmentBezel:_trackingSegment highlight:YES];
|
||||
}
|
||||
|
||||
break;
|
||||
}
|
||||
|
||||
[CPApp setTarget:self selector:@selector(trackSegment:) forNextEventMatchingMask:CPLeftMouseDraggedMask | CPLeftMouseUpMask untilDate:nil inMode:nil dequeue:YES];
|
||||
}
|
||||
|
||||
@end
|
||||
|
||||
|
||||
@@ -270,7 +270,7 @@ CPPressedTab = 2;
|
||||
[self setLabel:title];
|
||||
|
||||
if ([_tabView selectedTabViewItem] == self)
|
||||
[_tabView _displayItemView:[_viewController view]];
|
||||
[_tabView _loadTabViewItem:self];
|
||||
}
|
||||
|
||||
@end
|
||||
|
||||
+10
-2
@@ -287,7 +287,7 @@ CPTableColumnUserResizingMask = 1 << 1;
|
||||
if (width < [self minWidth])
|
||||
[self setMinWidth:width];
|
||||
else if (width > [self maxWidth])
|
||||
[self setMaxWidth:width]
|
||||
[self setMaxWidth:width];
|
||||
|
||||
if (_width !== width)
|
||||
[self setWidth:width];
|
||||
@@ -499,7 +499,7 @@ CPTableColumnUserResizingMask = 1 << 1;
|
||||
*/
|
||||
- (void)setHidden:(BOOL)shouldBeHidden
|
||||
{
|
||||
shouldBeHidden = !!shouldBeHidden
|
||||
shouldBeHidden = !!shouldBeHidden;
|
||||
|
||||
if (_isHidden === shouldBeHidden)
|
||||
return;
|
||||
@@ -537,6 +537,14 @@ CPTableColumnUserResizingMask = 1 << 1;
|
||||
return _headerToolTip;
|
||||
}
|
||||
|
||||
/*!
|
||||
@ignore
|
||||
*/
|
||||
- (CPString)description
|
||||
{
|
||||
return [CPString stringWithFormat:"<%@ 0x%@ identifier=%@>", [self className], [CPString stringWithHash:[self UID]], [self identifier]];
|
||||
}
|
||||
|
||||
@end
|
||||
|
||||
@implementation CPTableColumnValueBinder : CPBinder
|
||||
|
||||
+33
-13
@@ -89,7 +89,7 @@
|
||||
var inset = [self currentValueForThemeAttribute:@"text-inset"],
|
||||
bounds = [self bounds];
|
||||
|
||||
[_textField setFrame:CGRectMake(inset.right, inset.top, bounds.size.width - inset.right - inset.left, bounds.size.height - inset.top - inset.bottom)];
|
||||
[_textField setFrame:CGRectMake(inset.left, inset.top, bounds.size.width - inset.right - inset.left, bounds.size.height - inset.top - inset.bottom)];
|
||||
[_textField setTextColor:[self currentValueForThemeAttribute:@"text-color"]];
|
||||
[_textField setFont:[self currentValueForThemeAttribute:@"font"]];
|
||||
[_textField setTextShadowColor:[self currentValueForThemeAttribute:@"text-shadow-color"]];
|
||||
@@ -417,8 +417,7 @@ var CPTableHeaderViewResizeZone = 3.0,
|
||||
}
|
||||
else if (_isDragging)
|
||||
{
|
||||
// Disable autoscrolling until it behaves correctly.
|
||||
//[self _autoscroll:theEvent localLocation:currentLocation];
|
||||
[self _autoscroll:theEvent localLocation:currentLocation];
|
||||
[self _dragTableColumn:_activeColumn to:currentLocation];
|
||||
}
|
||||
else // tracking a press, could become a drag
|
||||
@@ -462,18 +461,38 @@ var CPTableHeaderViewResizeZone = 3.0,
|
||||
@end
|
||||
|
||||
@implementation CPTableHeaderView (CPTrackingArea)
|
||||
{
|
||||
CPMutableArray _tableHeaderViewTrackingAreas;
|
||||
}
|
||||
|
||||
- (void)updateTrackingAreas
|
||||
{
|
||||
[self removeAllTrackingAreas];
|
||||
|
||||
if (_tableHeaderViewTrackingAreas)
|
||||
{
|
||||
for (var i = 0, count = [_tableHeaderViewTrackingAreas count]; i < count; i++)
|
||||
[self removeTrackingArea:_tableHeaderViewTrackingAreas[i]];
|
||||
|
||||
_tableHeaderViewTrackingAreas = nil;
|
||||
}
|
||||
|
||||
var options = CPTrackingCursorUpdate | CPTrackingActiveInKeyWindow;
|
||||
|
||||
|
||||
if (!_tableView)
|
||||
return;
|
||||
|
||||
_tableHeaderViewTrackingAreas = @[];
|
||||
|
||||
for (var i = 0; i < _tableView._tableColumns.length; i++)
|
||||
[self addTrackingArea:[[CPTrackingArea alloc] initWithRect:[self _cursorRectForColumn:i]
|
||||
options:options
|
||||
owner:self
|
||||
userInfo:nil]];
|
||||
{
|
||||
[_tableHeaderViewTrackingAreas addObject:[[CPTrackingArea alloc] initWithRect:[self _cursorRectForColumn:i]
|
||||
options:options
|
||||
owner:self
|
||||
userInfo:nil]];
|
||||
|
||||
[self addTrackingArea:_tableHeaderViewTrackingAreas[i]];
|
||||
}
|
||||
|
||||
[super updateTrackingAreas];
|
||||
}
|
||||
|
||||
- (void)cursorUpdate:(CPEvent)anEvent
|
||||
@@ -679,20 +698,21 @@ var CPTableHeaderViewResizeZone = 3.0,
|
||||
[_columnDragClipView removeFromSuperview];
|
||||
[_tableView _setDraggedColumn:-1];
|
||||
|
||||
var headerView = [[[_tableView tableColumns] objectAtIndex:aColumnIndex] headerView];
|
||||
var tableColumn = [[_tableView tableColumns] objectAtIndex:aColumnIndex],
|
||||
headerView = [tableColumn headerView];
|
||||
|
||||
[[headerView subviews] makeObjectsPerformSelector:@selector(setHidden:) withObject:NO];
|
||||
|
||||
if (_tableView._draggedColumnIsSelected)
|
||||
[headerView setThemeState:CPThemeStateSelected];
|
||||
|
||||
var columnRect = [_tableView rectOfColumn:aColumnIndex];
|
||||
|
||||
[_tableView _reloadDataViews];
|
||||
[[_tableView headerView] setNeedsLayout];
|
||||
|
||||
[[CPCursor arrowCursor] set];
|
||||
[self updateTrackingAreas];
|
||||
|
||||
[_tableView _sendDelegateDidDragTableColumn:tableColumn];
|
||||
}
|
||||
|
||||
- (BOOL)_shouldResizeTableColumn:(CPInteger)aColumnIndex at:(CGPoint)aPoint
|
||||
|
||||
+40
-34
@@ -203,7 +203,7 @@ CPTableViewFirstColumnOnlyAutoresizingStyle = 5;
|
||||
@ingroup appkit
|
||||
@class CPTableView
|
||||
|
||||
CPTableView object displays record-oriented data in a table and
|
||||
A table view control displays record-oriented data in a table and
|
||||
allows the user to edit values and resize and rearrange columns.
|
||||
A CPTableView requires you to either set a data source which implements
|
||||
@ref numberofrows "numberOfRowsInTableView:" and @ref objectValueForTable "tableView:objectValueForTableColumn:row:",
|
||||
@@ -346,8 +346,8 @@ CPTableViewFirstColumnOnlyAutoresizingStyle = 5;
|
||||
+ (CPDictionary)themeAttributes
|
||||
{
|
||||
return @{
|
||||
@"alternating-row-colors": [CPNull null],
|
||||
@"grid-color": [CPNull null],
|
||||
@"alternating-row-colors": @[[CPColor whiteColor], [CPColor colorWithRed:245.0 / 255.0 green:249.0 / 255.0 blue:252.0 / 255.0 alpha:1.0]],
|
||||
@"grid-color": [CPColor colorWithHexString:@"dce0e2"],
|
||||
@"grid-line-thickness": 1.0,
|
||||
@"highlighted-grid-color": [CPNull null],
|
||||
@"selection-color": [CPNull null],
|
||||
@@ -368,7 +368,8 @@ CPTableViewFirstColumnOnlyAutoresizingStyle = 5;
|
||||
@"dropview-above-border-color": [CPNull null],
|
||||
@"dropview-above-border-width": [CPNull null],
|
||||
@"dropview-above-selected-border-color": [CPNull null],
|
||||
@"dropview-above-selected-border-width": [CPNull null]
|
||||
@"dropview-above-selected-border-width": [CPNull null],
|
||||
@"header-view-height": 25.0
|
||||
};
|
||||
}
|
||||
|
||||
@@ -390,8 +391,7 @@ CPTableViewFirstColumnOnlyAutoresizingStyle = 5;
|
||||
_selectionHighlightStyle = CPTableViewSelectionHighlightStyleRegular;
|
||||
|
||||
[self setUsesAlternatingRowBackgroundColors:NO];
|
||||
[self setAlternatingRowBackgroundColors:
|
||||
[[CPColor whiteColor], [CPColor colorWithRed:245.0 / 255.0 green:249.0 / 255.0 blue:252.0 / 255.0 alpha:1.0]]];
|
||||
[self setAlternatingRowBackgroundColors:[self valueForThemeAttribute:@"alternating-row-colors"]];
|
||||
|
||||
_tableColumns = [];
|
||||
_tableColumnRanges = [];
|
||||
@@ -401,10 +401,10 @@ CPTableViewFirstColumnOnlyAutoresizingStyle = 5;
|
||||
_intercellSpacing = CGSizeMake(3.0, 2.0);
|
||||
_rowHeight = [self valueForThemeAttribute:@"default-row-height"];
|
||||
|
||||
[self setGridColor:[CPColor colorWithHexString:@"dce0e2"]];
|
||||
[self setGridColor:[self valueForThemeAttribute:@"grid-color"]];
|
||||
[self setGridStyleMask:CPTableViewGridNone];
|
||||
|
||||
[self setHeaderView:[[CPTableHeaderView alloc] initWithFrame:CGRectMake(0, 0, [self bounds].size.width, _rowHeight)]];
|
||||
[self setHeaderView:[[CPTableHeaderView alloc] initWithFrame:CGRectMake(0, 0, [self bounds].size.width, [self valueForThemeAttribute:@"header-view-height"])]];
|
||||
[self setCornerView:[[_CPCornerView alloc] initWithFrame:CGRectMake(0, 0, [CPScroller scrollerWidth], CGRectGetHeight([_headerView frame]))]];
|
||||
|
||||
_currentHighlightedTableColumn = nil;
|
||||
@@ -3096,10 +3096,15 @@ Your delegate can implement this method to avoid subclassing the tableview to ad
|
||||
*/
|
||||
- (CPView)dragViewForRowsWithIndexes:(CPIndexSet)theDraggedRows tableColumns:(CPArray)theTableColumns event:(CPEvent)theDragEvent offset:(CGPoint)dragViewOffset
|
||||
{
|
||||
var bounds = [self bounds],
|
||||
var firstRowFrame = [self frameOfDataViewAtColumn:0 row:[theDraggedRows firstIndex]],
|
||||
lastRowFrame = [self frameOfDataViewAtColumn:0 row:[theDraggedRows lastIndex]],
|
||||
dragViewHeight = CGRectGetMaxY(lastRowFrame) - CGRectGetMinY(firstRowFrame);
|
||||
|
||||
var bounds = CGRectMake(0, 0, CGRectGetWidth([self bounds]), dragViewHeight),
|
||||
dragView = [[CPView alloc] initWithFrame:bounds];
|
||||
|
||||
[dragView setAlphaValue:0.7];
|
||||
// [dragView setBackgroundColor:[CPColor colorWithWhite:1 alpha:0.5]];
|
||||
|
||||
// We have to fetch all the data views for the selected rows and columns
|
||||
// After that we can copy these add them to a transparent drag view and use that drag view
|
||||
@@ -3107,7 +3112,11 @@ Your delegate can implement this method to avoid subclassing the tableview to ad
|
||||
[self _enumerateViewsInRows:theDraggedRows tableColumns:theTableColumns usingBlock:function(v, row, tableColumn, stop)
|
||||
{
|
||||
var column = [_tableColumns indexOfObjectIdenticalTo:tableColumn],
|
||||
newDataView = [self preparedViewAtColumn:column row:row];
|
||||
newDataView = [self preparedViewAtColumn:column row:row],
|
||||
origin = [newDataView frame].origin;
|
||||
|
||||
origin.y -= CGRectGetMinY(firstRowFrame);
|
||||
[newDataView setFrameOrigin:origin];
|
||||
|
||||
[dragView addSubview:newDataView];
|
||||
}];
|
||||
@@ -3751,7 +3760,7 @@ Your delegate can implement this method to avoid subclassing the tableview to ad
|
||||
{
|
||||
if (!dataViewsForRow)
|
||||
{
|
||||
dataViewsForRow = {}
|
||||
dataViewsForRow = {};
|
||||
_dataViewsForRows[row] = dataViewsForRow;
|
||||
}
|
||||
|
||||
@@ -4600,7 +4609,9 @@ Your delegate can implement this method to avoid subclassing the tableview to ad
|
||||
}
|
||||
|
||||
var bounds = [view bounds],
|
||||
viewLocation = CGPointMake(aPoint.x - CGRectGetWidth(bounds) / 2 + offset.x, aPoint.y - CGRectGetHeight(bounds) / 2 + offset.y);
|
||||
firstRowFrame = [self frameOfDataViewAtColumn:0 row:[_draggedRowIndexes firstIndex]],
|
||||
viewLocation = CGPointMake(aPoint.x - CGRectGetWidth(bounds) / 2 + offset.x, aPoint.y - CGRectGetHeight(bounds) / 2 + offset.y + CGRectGetMinY(firstRowFrame));
|
||||
|
||||
[self dragView:view at:viewLocation offset:CGPointMakeZero() event:[CPApp currentEvent] pasteboard:pboard source:self slideBack:YES];
|
||||
_startTrackingPoint = nil;
|
||||
|
||||
@@ -4896,7 +4907,7 @@ Your delegate can implement this method to avoid subclassing the tableview to ad
|
||||
row = _retargetedDropRow;
|
||||
|
||||
if (row === nil)
|
||||
var row = [self _proposedRowAtPoint:location];
|
||||
row = [self _proposedRowAtPoint:location];
|
||||
|
||||
return [self _sendDataSourceAcceptDrop:sender row:row dropOperation:operation];
|
||||
}
|
||||
@@ -5097,14 +5108,7 @@ Your delegate can implement this method to avoid subclassing the tableview to ad
|
||||
}
|
||||
}
|
||||
else if (!_isViewBased && [aView isKindOfClass:[CPControl class]] && ![aView isKindOfClass:[CPTextField class]])
|
||||
{
|
||||
[self getColumn:@ref(column) row:@ref(row) forView:aView];
|
||||
|
||||
_editingColumn = column;
|
||||
_editingRow = row;
|
||||
|
||||
[aView addObserver:self forKeyPath:@"objectValue" options:CPKeyValueObservingOptionOld | CPKeyValueObservingOptionNew context:"editing"];
|
||||
}
|
||||
|
||||
return aView;
|
||||
}
|
||||
@@ -5135,7 +5139,7 @@ Your delegate can implement this method to avoid subclassing the tableview to ad
|
||||
{
|
||||
[super viewWillMoveToWindow:aWindow];
|
||||
|
||||
[self _stopObservingFirstResponderForWindow:aWindow];
|
||||
[self _stopObservingFirstResponderForWindow:[self window]];
|
||||
|
||||
if (aWindow)
|
||||
[self _startObservingFirstResponderForWindow:aWindow];
|
||||
@@ -5236,7 +5240,7 @@ Your delegate can implement this method to avoid subclassing the tableview to ad
|
||||
if (!_isViewBased)
|
||||
{
|
||||
[self _setEditingState:NO forView:textField];
|
||||
[self _commitDataViewObjectValue:textField];
|
||||
[self _commitDataViewObjectValue:textField forColumn:_editingColumn andRow:_editingRow];
|
||||
}
|
||||
else
|
||||
[textField setBezeled:NO];
|
||||
@@ -5286,19 +5290,19 @@ Your delegate can implement this method to avoid subclassing the tableview to ad
|
||||
The action for any dataview that supports editing. This will only be called when the value was changed.
|
||||
The table view becomes the first responder after user is done editing a dataview.
|
||||
*/
|
||||
- (void)_commitDataViewObjectValue:(id)aDataView
|
||||
- (void)_commitDataViewObjectValue:(id)aDataView forColumn:(CPInteger)column andRow:(CPInteger)row
|
||||
{
|
||||
var editingTableColumn = _tableColumns[_editingColumn];
|
||||
var editingTableColumn = _tableColumns[column];
|
||||
|
||||
if (_implementedDataSourceMethods & CPTableViewDataSource_tableView_setObjectValue_forTableColumn_row_)
|
||||
[_dataSource tableView:self setObjectValue:[aDataView objectValue] forTableColumn:editingTableColumn row:_editingRow];
|
||||
[_dataSource tableView:self setObjectValue:[aDataView objectValue] forTableColumn:editingTableColumn row:row];
|
||||
|
||||
// Allow the column binding to do a reverse set. Note that we do this even if the data source method above
|
||||
// is implemented.
|
||||
[editingTableColumn _reverseSetDataView:aDataView forRow:_editingRow];
|
||||
[editingTableColumn _reverseSetDataView:aDataView forRow:row];
|
||||
|
||||
if (_editingRow !== CPNotFound && _editingColumn !== CPNotFound)
|
||||
[self reloadDataForRowIndexes:[CPIndexSet indexSetWithIndex:_editingRow] columnIndexes:[CPIndexSet indexSetWithIndex:_editingColumn]];
|
||||
if (row !== CPNotFound && column !== CPNotFound)
|
||||
[self _reloadDataForRowIndexes:[CPIndexSet indexSetWithIndex:row] columnIndexes:[CPIndexSet indexSetWithIndex:column]];
|
||||
}
|
||||
|
||||
- (void)_setEditingState:(BOOL)editingState forView:(CPView)aView
|
||||
@@ -5348,9 +5352,12 @@ Your delegate can implement this method to avoid subclassing the tableview to ad
|
||||
if (context === "editing" && [object superview] === self)
|
||||
{
|
||||
[object removeObserver:self forKeyPath:keyPath];
|
||||
[self _commitDataViewObjectValue:object];
|
||||
_editingRow = CPNotFound;
|
||||
_editingColumn = CPNotFound;
|
||||
|
||||
var row,
|
||||
column;
|
||||
|
||||
[self getColumn:@ref(column) row:@ref(row) forView:object];
|
||||
[self _commitDataViewObjectValue:object forColumn:column andRow:row];
|
||||
}
|
||||
}
|
||||
|
||||
@@ -5752,10 +5759,10 @@ Your delegate can implement this method to avoid subclassing the tableview to ad
|
||||
@ignore
|
||||
Call the delegate didDragTableColumn with the given tableColumn
|
||||
*/
|
||||
- (void)_sendDelegateDidDragTableColumn:(CPInteger)column
|
||||
- (void)_sendDelegateDidDragTableColumn:(CPTableColumn)aTableColumn
|
||||
{
|
||||
if (_implementedDelegateMethods & CPTableViewDelegate_tableView_didDragTableColumn_)
|
||||
[_delegate tableView:self didDragTableColumn:_tableColumns[column]];
|
||||
[_delegate tableView:self didDragTableColumn:aTableColumn];
|
||||
}
|
||||
|
||||
/*!
|
||||
@@ -6111,7 +6118,6 @@ var CPTableViewDataSourceKey = @"CPTableViewDataSourceKey",
|
||||
CPTableViewGridStyleMaskKey = @"CPTableViewGridStyleMaskKey",
|
||||
CPTableViewUsesAlternatingBackgroundKey = @"CPTableViewUsesAlternatingBackgroundKey",
|
||||
CPTableViewAlternatingRowColorsKey = @"CPTableViewAlternatingRowColorsKey",
|
||||
CPTableViewHeaderViewKey = @"CPTableViewHeaderViewKey",
|
||||
CPTableViewCornerViewKey = @"CPTableViewCornerViewKey",
|
||||
CPTableViewAutosaveNameKey = @"CPTableViewAutosaveNameKey",
|
||||
CPTableViewArchivedReusableViewsKey = @"CPTableViewArchivedReusableViewsKey";
|
||||
|
||||
+313
-5
@@ -5,6 +5,13 @@
|
||||
* Created by Alexander Ljungberg.
|
||||
* Copyright 2010, WireLoad, LLC.
|
||||
*
|
||||
* additions from
|
||||
*
|
||||
* Daniel Boehringer on 8/02/2014.
|
||||
* Copyright Daniel Boehringer on 8/02/2014.
|
||||
*
|
||||
*
|
||||
*
|
||||
* This library is free software; you can redistribute it and/or
|
||||
* modify it under the terms of the GNU Lesser General Public
|
||||
* License as published by the Free Software Foundation; either
|
||||
@@ -20,6 +27,30 @@
|
||||
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
|
||||
*/
|
||||
|
||||
@import <Foundation/Foundation.j>
|
||||
|
||||
@import "CPPasteboard.j"
|
||||
@import "CPView.j"
|
||||
|
||||
@global CPStringPboardType
|
||||
@global _CPSmartPboardType
|
||||
|
||||
@class CPAttributedString
|
||||
@class _CPRTFParser
|
||||
|
||||
@protocol CPTextDelegate <CPObject>
|
||||
|
||||
@optional
|
||||
- (BOOL)textShouldBeginEditing:(CPText)aTextObject;
|
||||
- (BOOL)textShouldEndEditing:(CPText)aTextObject;
|
||||
- (void)textDidBeginEditing:(CPNotification)aNotification;
|
||||
- (void)textDidChange:(CPNotification)aNotification;
|
||||
- (void)textDidEndEditing:(CPNotification)aNotification;
|
||||
|
||||
@end
|
||||
|
||||
CPParagraphSeparatorCharacter = 0x2029;
|
||||
CPLineSeparatorCharacter = 0x2028;
|
||||
CPEnterCharacter = "\u0003";
|
||||
CPBackspaceCharacter = "\u0008";
|
||||
CPTabCharacter = "\u0009";
|
||||
@@ -29,6 +60,7 @@ CPCarriageReturnCharacter = "\u000d";
|
||||
CPBackTabCharacter = "\u0019";
|
||||
CPDeleteCharacter = "\u007f";
|
||||
|
||||
@typedef CPTextMovement
|
||||
CPIllegalTextMovement = 0;
|
||||
CPOtherTextMovement = 0;
|
||||
CPReturnTextMovement = 16;
|
||||
@@ -46,8 +78,284 @@ CPWritingDirectionLeftToRight = 0;
|
||||
CPWritingDirectionRightToLeft = 1;
|
||||
|
||||
@typedef CPTextAlignment
|
||||
CPLeftTextAlignment = 0;
|
||||
CPRightTextAlignment = 1;
|
||||
CPCenterTextAlignment = 2;
|
||||
CPJustifiedTextAlignment = 3;
|
||||
CPNaturalTextAlignment = 4;
|
||||
CPLeftTextAlignment = 0;
|
||||
CPRightTextAlignment = 1;
|
||||
CPCenterTextAlignment = 2;
|
||||
CPJustifiedTextAlignment = 3;
|
||||
CPNaturalTextAlignment = 4;
|
||||
|
||||
/*
|
||||
CPText notifications
|
||||
*/
|
||||
CPTextDidBeginEditingNotification = @"CPTextDidBeginEditingNotification";
|
||||
CPTextDidChangeNotification = @"CPTextDidChangeNotification";
|
||||
CPTextDidEndEditingNotification = @"CPTextDidEndEditingNotification";
|
||||
|
||||
/*
|
||||
CPTextView Notifications
|
||||
*/
|
||||
CPTextViewDidChangeSelectionNotification = @"CPTextViewDidChangeSelectionNotification";
|
||||
CPTextViewDidChangeTypingAttributesNotification = @"CPTextViewDidChangeTypingAttributesNotification";
|
||||
|
||||
/*
|
||||
FIXME: move these to CPAttributed string
|
||||
Make use of attributed keys in AppKit
|
||||
*/
|
||||
CPFontAttributeName = @"CPFontAttributeName";
|
||||
CPForegroundColorAttributeName = @"CPForegroundColorAttributeName";
|
||||
CPBackgroundColorAttributeName = @"CPBackgroundColorAttributeName";
|
||||
CPShadowAttributeName = @"CPShadowAttributeName";
|
||||
CPUnderlineStyleAttributeName = @"CPUnderlineStyleAttributeName";
|
||||
CPSuperscriptAttributeName = @"CPSuperscriptAttributeName";
|
||||
CPBaselineOffsetAttributeName = @"CPBaselineOffsetAttributeName";
|
||||
CPAttachmentAttributeName = @"CPAttachmentAttributeName";
|
||||
CPLigatureAttributeName = @"CPLigatureAttributeName";
|
||||
CPKernAttributeName = @"CPKernAttributeName";
|
||||
|
||||
@implementation CPText : CPView
|
||||
{
|
||||
BOOL _isEditable @accessors(getter=isEditable, setter=setEditable:);
|
||||
BOOL _isSelectable @accessors(getter=isSelectable, setter=setSelectable:);
|
||||
BOOL _isRichText @accessors(getter=isRichText, setter=setRichText:);
|
||||
}
|
||||
|
||||
- (void)setSelectable:(BOOL)flag
|
||||
{
|
||||
[self willChangeValueForKey:@"selectable"];
|
||||
_isSelectable = flag;
|
||||
[self didChangeValueForKey:@"selectable"];
|
||||
|
||||
if (!flag)
|
||||
[self setEditable:flag];
|
||||
}
|
||||
|
||||
- (void)setEditable:(BOOL)flag
|
||||
{
|
||||
[self willChangeValueForKey:@"editable"];
|
||||
_isEditable = flag;
|
||||
[self didChangeValueForKey:@"editable"];
|
||||
|
||||
if (flag)
|
||||
[self setSelectable:flag];
|
||||
}
|
||||
|
||||
- (void)changeFont:(id)sender
|
||||
{
|
||||
_CPRaiseInvalidAbstractInvocation(self, _cmd);
|
||||
}
|
||||
|
||||
- (void)copy:(id)sender
|
||||
{
|
||||
var selectedRange = [self selectedRange];
|
||||
|
||||
if (selectedRange.length < 1)
|
||||
return;
|
||||
|
||||
var pasteboard = [CPPasteboard generalPasteboard];
|
||||
|
||||
// put plain representation on the pasteboad unconditionally
|
||||
[pasteboard declareTypes:[CPStringPboardType] owner:nil];
|
||||
[pasteboard setString:[[self stringValue] substringWithRange:selectedRange] forType:CPStringPboardType];
|
||||
}
|
||||
|
||||
- (id)_plainStringForPasting
|
||||
{
|
||||
return [[CPPasteboard generalPasteboard] stringForType:CPStringPboardType];
|
||||
}
|
||||
|
||||
- (id)_stringForPasting
|
||||
{
|
||||
var pasteboard = [CPPasteboard generalPasteboard],
|
||||
dataForPasting = [pasteboard stringForType:CPRTFPboardType],
|
||||
stringForPasting = [pasteboard stringForType:CPStringPboardType];
|
||||
|
||||
if (dataForPasting || [stringForPasting hasPrefix:"{\\rtf1\\ansi"])
|
||||
stringForPasting = [[_CPRTFParser new] parseRTF:dataForPasting ? dataForPasting : stringForPasting];
|
||||
|
||||
if (![self isRichText] && [stringForPasting isKindOfClass:[CPAttributedString class]])
|
||||
stringForPasting = stringForPasting._string;
|
||||
|
||||
return stringForPasting;
|
||||
}
|
||||
|
||||
- (BOOL)_shouldUseSmartPasting
|
||||
{
|
||||
return parseInt([[CPPasteboard generalPasteboard] stringForType:_CPSmartPboardType], 10) > 0 || NO;
|
||||
}
|
||||
|
||||
- (void)paste:(id)sender
|
||||
{
|
||||
var stringForPasting = [self _stringForPasting];
|
||||
|
||||
if (stringForPasting)
|
||||
[self insertText:stringForPasting];
|
||||
}
|
||||
|
||||
- (void)copyFont:(id)sender
|
||||
{
|
||||
_CPRaiseInvalidAbstractInvocation(self, _cmd);
|
||||
}
|
||||
|
||||
- (void)delete:(id)sender
|
||||
{
|
||||
_CPRaiseInvalidAbstractInvocation(self, _cmd);
|
||||
}
|
||||
|
||||
- (CPFont)font:(CPFont)aFont
|
||||
{
|
||||
_CPRaiseInvalidAbstractInvocation(self, _cmd);
|
||||
|
||||
return nil;
|
||||
}
|
||||
|
||||
- (BOOL)isHorizontallyResizable
|
||||
{
|
||||
_CPRaiseInvalidAbstractInvocation(self, _cmd);
|
||||
|
||||
return NO;
|
||||
}
|
||||
|
||||
- (BOOL)isRulerVisible
|
||||
{
|
||||
_CPRaiseInvalidAbstractInvocation(self, _cmd);
|
||||
|
||||
return NO;
|
||||
}
|
||||
|
||||
- (BOOL)isVerticallyResizable
|
||||
{
|
||||
_CPRaiseInvalidAbstractInvocation(self, _cmd);
|
||||
|
||||
return NO;
|
||||
}
|
||||
|
||||
- (CGSize)maxSize
|
||||
{
|
||||
_CPRaiseInvalidAbstractInvocation(self, _cmd);
|
||||
|
||||
return CGSizeMake(0,0);
|
||||
}
|
||||
|
||||
- (CGSize)minSize
|
||||
{
|
||||
_CPRaiseInvalidAbstractInvocation(self, _cmd);
|
||||
return CGSizeMake(0,0);
|
||||
}
|
||||
|
||||
- (void)pasteFont:(id)sender
|
||||
{
|
||||
_CPRaiseInvalidAbstractInvocation(self, _cmd);
|
||||
}
|
||||
|
||||
- (void)replaceCharactersInRange:(CPRange)aRange withString:(CPString)aString
|
||||
{
|
||||
_CPRaiseInvalidAbstractInvocation(self, _cmd);
|
||||
}
|
||||
|
||||
- (void)scrollRangeToVisible:(CPRange)aRange
|
||||
{
|
||||
_CPRaiseInvalidAbstractInvocation(self, _cmd);
|
||||
}
|
||||
|
||||
- (void)selectedAll:(id)sender
|
||||
{
|
||||
_CPRaiseInvalidAbstractInvocation(self, _cmd);
|
||||
}
|
||||
|
||||
- (CPRange)selectedRange
|
||||
{
|
||||
_CPRaiseInvalidAbstractInvocation(self, _cmd);
|
||||
|
||||
return CPMakeRange(CPNotFound, 0);
|
||||
}
|
||||
|
||||
- (void)setFont:(CPFont)aFont
|
||||
{
|
||||
_CPRaiseInvalidAbstractInvocation(self, _cmd);
|
||||
}
|
||||
|
||||
- (void)setFont:(CPFont)aFont range:(CPRange)aRange
|
||||
{
|
||||
_CPRaiseInvalidAbstractInvocation(self, _cmd);
|
||||
}
|
||||
|
||||
- (void)setHorizontallyResizable:(BOOL)flag
|
||||
{
|
||||
_CPRaiseInvalidAbstractInvocation(self, _cmd);
|
||||
}
|
||||
|
||||
- (void)setMaxSize:(CGSize)aSize
|
||||
{
|
||||
_CPRaiseInvalidAbstractInvocation(self, _cmd);
|
||||
}
|
||||
|
||||
- (void)setMinSize:(CGSize)aSize
|
||||
{
|
||||
_CPRaiseInvalidAbstractInvocation(self, _cmd);
|
||||
}
|
||||
|
||||
- (void)setString:(CPString)aString
|
||||
{
|
||||
[self replaceCharactersInRange:CPMakeRange(0, [[self string] length]) withString:aString];
|
||||
}
|
||||
|
||||
- (void)setUsesFontPanel:(BOOL)flag
|
||||
{
|
||||
_CPRaiseInvalidAbstractInvocation(self, _cmd);
|
||||
}
|
||||
|
||||
- (void)setVerticallyResizable:(BOOL)flag
|
||||
{
|
||||
_CPRaiseInvalidAbstractInvocation(self, _cmd);
|
||||
}
|
||||
|
||||
- (CPString)string
|
||||
{
|
||||
_CPRaiseInvalidAbstractInvocation(self, _cmd);
|
||||
|
||||
return nil;
|
||||
}
|
||||
|
||||
- (void)underline:(id)sender
|
||||
{
|
||||
_CPRaiseInvalidAbstractInvocation(self, _cmd);
|
||||
}
|
||||
|
||||
- (BOOL)usesFontPanel
|
||||
{
|
||||
_CPRaiseInvalidAbstractInvocation(self, _cmd);
|
||||
|
||||
return NO;
|
||||
}
|
||||
|
||||
@end
|
||||
|
||||
var CPTextViewIsEditableKey = @"CPTextViewIsEditableKey",
|
||||
CPTextViewIsSelectableKey = @"CPTextViewIsSelectableKey",
|
||||
CPTextViewIsRichTextKey = @"CPTextViewIsRichTextKey";
|
||||
|
||||
@implementation CPText (CPCoding)
|
||||
|
||||
- (id)initWithCoder:(CPCoder)aCoder
|
||||
{
|
||||
self = [super initWithCoder:aCoder];
|
||||
|
||||
if (self)
|
||||
{
|
||||
[self setSelectable:[aCoder decodeBoolForKey:CPTextViewIsSelectableKey]];
|
||||
[self setEditable:[aCoder decodeBoolForKey:CPTextViewIsEditableKey]];
|
||||
[self setRichText:[aCoder decodeBoolForKey:CPTextViewIsRichTextKey]];
|
||||
}
|
||||
|
||||
return self;
|
||||
}
|
||||
|
||||
- (void)encodeWithCoder:(CPCoder)aCoder
|
||||
{
|
||||
[super encodeWithCoder:aCoder];
|
||||
[aCoder encodeBool:_isEditable forKey:CPTextViewIsEditableKey];
|
||||
[aCoder encodeBool:_isSelectable forKey:CPTextViewIsSelectableKey];
|
||||
[aCoder encodeBool:_isRichText forKey:CPTextViewIsRichTextKey];
|
||||
}
|
||||
|
||||
@end
|
||||
|
||||
+69
-55
@@ -77,26 +77,16 @@ function CPTextFieldBlurFunction(anEvent, owner, domElement, inputElement, resig
|
||||
|
||||
var ownerWindow = [owner window];
|
||||
|
||||
if (!resigning && [ownerWindow isKeyWindow])
|
||||
if (!resigning && [ownerWindow isKeyWindow] && [ownerWindow firstResponder] === owner)
|
||||
{
|
||||
/*
|
||||
Browsers blur text fields when a click occurs anywhere outside the text field. That is normal for browsers, but in Cocoa the key view retains focus unless the click target accepts first responder. So if we lost focus but were not told to resign and our window is still key, restore focus,
|
||||
but only if the text field is completely within the browser window. If we restore focus when it
|
||||
is off screen, the entire body scrolls out of our control.
|
||||
Previously we had code here which would force the input to regain focus if the input lost focus without the CPTextField having actually lost first responder status. This typically happened because the user clicked away from the input in the browser, without clicking on something that could become the first responder. In the browser, clicking outside of an input blurs it, but in Cocoa and Cappuccino it does not (unless you actually click something that will become the first responder).
|
||||
|
||||
That refocusing code has now been removed because we simply prevent the default action on clicks in the browser instead, which combined with the fix in 58d5d7d7, successfully prevents unintentional focus loss in (at least) Safari 9.1.1, Chrome 51 and Safari for iOS 9.3 when you click outside of a text field.
|
||||
|
||||
Now we can still lose focus unexpectedly: this is when the 'done' button is tapped on the virtual keyboard of a mobile device. In this case we actually do want to resign first responder status, because that is what the done button should do (and if we did not the keyboard would go away and then immediately come back which looks dumb and isn't what the user wanted).
|
||||
*/
|
||||
if ([owner _isWithinUsablePlatformRect])
|
||||
{
|
||||
[[CPRunLoop mainRunLoop] performBlock:function()
|
||||
{
|
||||
// This will prevent to jump to the focused element
|
||||
var previousScrollingOrigin = [owner _scrollToVisibleRectAndReturnPreviousOrigin];
|
||||
|
||||
inputElement.focus();
|
||||
|
||||
[owner _restorePreviousScrollingOrigin:previousScrollingOrigin];
|
||||
}
|
||||
argument:nil order:0 modes:[CPDefaultRunLoopMode]];
|
||||
}
|
||||
[ownerWindow makeFirstResponder:nil];
|
||||
}
|
||||
|
||||
CPTextFieldHandleBlur(anEvent, @ref(owner));
|
||||
@@ -341,7 +331,7 @@ CPTextFieldStatePlaceholder = CPThemeState("placeholder");
|
||||
[CPTextFieldInputOwner keyUp:cappEvent];
|
||||
|
||||
[[CPRunLoop currentRunLoop] limitDateForMode:CPDefaultRunLoopMode];
|
||||
}
|
||||
};
|
||||
|
||||
CPTextFieldDOMPasswordInputElement.oninput = CPTextFieldInputFunction;
|
||||
CPTextFieldDOMPasswordInputElement.onblur = CPTextFieldBlurHandler;
|
||||
@@ -403,7 +393,7 @@ CPTextFieldStatePlaceholder = CPThemeState("placeholder");
|
||||
else
|
||||
[self unsetThemeState:CPThemeStateEditable];
|
||||
|
||||
// We only allow first responder status if the field is enable, and editable or selectable.
|
||||
// We only allow first responder status if the field is enabled, and editable or selectable.
|
||||
if (!(shouldBeEditable && ![self isSelectable]) && [[self window] firstResponder] === self)
|
||||
[[self window] makeFirstResponder:nil];
|
||||
|
||||
@@ -411,6 +401,8 @@ CPTextFieldStatePlaceholder = CPThemeState("placeholder");
|
||||
[self setThemeState:CPThemeStateEditable];
|
||||
else
|
||||
[self unsetThemeState:CPThemeStateEditable];
|
||||
|
||||
[self updateTrackingAreas];
|
||||
}
|
||||
|
||||
/*!
|
||||
@@ -432,6 +424,8 @@ CPTextFieldStatePlaceholder = CPThemeState("placeholder");
|
||||
// We only allow first responder status if the field is enabled.
|
||||
if (!shouldBeEnabled && [[self window] firstResponder] === self)
|
||||
[[self window] makeFirstResponder:nil];
|
||||
|
||||
[self updateTrackingAreas];
|
||||
}
|
||||
|
||||
/*!
|
||||
@@ -441,6 +435,8 @@ CPTextFieldStatePlaceholder = CPThemeState("placeholder");
|
||||
- (void)setSelectable:(BOOL)aFlag
|
||||
{
|
||||
_isSelectable = aFlag;
|
||||
|
||||
[self updateTrackingAreas];
|
||||
}
|
||||
|
||||
/*!
|
||||
@@ -839,7 +835,7 @@ CPTextFieldStatePlaceholder = CPThemeState("placeholder");
|
||||
[self _resignFirstKeyResponder];
|
||||
|
||||
_isEditing = NO;
|
||||
|
||||
|
||||
if ([self isEditable])
|
||||
{
|
||||
[self textDidEndEditing:[CPNotification notificationWithName:CPControlTextDidEndEditingNotification object:self userInfo:@{"CPTextMovement": [self _currentTextMovement]}]];
|
||||
@@ -877,12 +873,18 @@ CPTextFieldStatePlaceholder = CPThemeState("placeholder");
|
||||
if (!CPTextFieldInputDidBlur)
|
||||
CPTextFieldBlurHandler();
|
||||
|
||||
CPTextFieldInputDidBlur = NO;
|
||||
CPTextFieldInputResigning = NO;
|
||||
|
||||
if (element.parentNode == _DOMElement)
|
||||
element.parentNode.removeChild(element);
|
||||
|
||||
// Previosly, we unflagged CPTextFieldInputDidBlur and CPTextFieldInputResigning before
|
||||
// the call to removeChild. This resulted in DOM exceptions in Chrome under certain conditions.
|
||||
// See https://stackoverflow.com/questions/21926083/failed-to-execute-removechild-on-node
|
||||
// for why we need to unflag CPTextFieldInputDidBlur and CPTextFieldInputResigning
|
||||
// only after removing the element.
|
||||
|
||||
CPTextFieldInputDidBlur = NO;
|
||||
CPTextFieldInputResigning = NO;
|
||||
|
||||
CPTextFieldInputIsActive = NO;
|
||||
|
||||
if (document.attachEvent)
|
||||
@@ -999,14 +1001,11 @@ CPTextFieldStatePlaceholder = CPThemeState("placeholder");
|
||||
[self textDidChange:[CPNotification notificationWithName:CPControlTextDidChangeNotification object:self userInfo:nil]];
|
||||
}
|
||||
|
||||
- (void)mouseMoved:(CPEvent)anEvent
|
||||
{
|
||||
[super mouseMoved:anEvent];
|
||||
[self _updateCursorForEvent:anEvent];
|
||||
}
|
||||
|
||||
- (void)mouseDown:(CPEvent)anEvent
|
||||
{
|
||||
if (![self isEnabled])
|
||||
return [[self nextResponder] mouseDown:anEvent];
|
||||
|
||||
// Don't track! (ever?)
|
||||
if ([self isEditable] && [self isEnabled])
|
||||
{
|
||||
@@ -1041,7 +1040,7 @@ CPTextFieldStatePlaceholder = CPThemeState("placeholder");
|
||||
[[self window] platformWindow]._DOMBodyElement.ondrag = CPTextFieldCachedDragFunction;
|
||||
[[self window] platformWindow]._DOMBodyElement.onselectstart = CPTextFieldCachedSelectStartFunction;
|
||||
|
||||
CPTextFieldCachedSelectStartFunction = nil
|
||||
CPTextFieldCachedSelectStartFunction = nil;
|
||||
CPTextFieldCachedDragFunction = nil;
|
||||
}
|
||||
|
||||
@@ -1244,7 +1243,7 @@ CPTextFieldStatePlaceholder = CPThemeState("placeholder");
|
||||
return;
|
||||
|
||||
if (_implementedDelegateMethods & CPTextFieldDelegate_controlTextDidBeginEditing_)
|
||||
[_delegate controlTextDidBeginEditing:[[CPNotification alloc] initWithName:CPControlTextDidBeginEditingNotification object:self userInfo:@{"CPFieldEditor": [note object]}]]
|
||||
[_delegate controlTextDidBeginEditing:[[CPNotification alloc] initWithName:CPControlTextDidBeginEditingNotification object:self userInfo:@{"CPFieldEditor": [note object]}]];
|
||||
|
||||
[super textDidBeginEditing:note];
|
||||
}
|
||||
@@ -1261,27 +1260,6 @@ CPTextFieldStatePlaceholder = CPThemeState("placeholder");
|
||||
[_delegate controlTextDidEndEditing:note];
|
||||
}
|
||||
|
||||
- (void)_updateCursorForEvent:(CPEvent)anEvent
|
||||
{
|
||||
var frame = CGRectMakeCopy([self frame]),
|
||||
contentInset = [self currentValueForThemeAttribute:@"content-inset"];
|
||||
|
||||
frame = [[self superview] convertRectToBase:CGRectInsetByInset(frame, contentInset)];
|
||||
|
||||
if ([self isEnabled] && ([self isSelectable] || [self isEditable]) && CGRectContainsPoint(frame, [anEvent locationInWindow]))
|
||||
{
|
||||
#if PLATFORM(DOM)
|
||||
self._DOMElement.style.cursor = "text";
|
||||
#endif
|
||||
}
|
||||
else
|
||||
{
|
||||
#if PLATFORM(DOM)
|
||||
self._DOMElement.style.cursor = "default";
|
||||
#endif
|
||||
}
|
||||
}
|
||||
|
||||
/*!
|
||||
Returns the string in the text field.
|
||||
*/
|
||||
@@ -1812,7 +1790,7 @@ CPTextFieldStatePlaceholder = CPThemeState("placeholder");
|
||||
_implementedDelegateMethods = 0;
|
||||
|
||||
if ([_delegate respondsToSelector:@selector(control:didFailToFormatString:errorDescription:)])
|
||||
_implementedDelegateMethods |= CPTextFieldDelegate_control_didFailToFormatString_errorDescription_
|
||||
_implementedDelegateMethods |= CPTextFieldDelegate_control_didFailToFormatString_errorDescription_;
|
||||
|
||||
if ([_delegate respondsToSelector:@selector(controlTextDidBeginEditing:)])
|
||||
_implementedDelegateMethods |= CPTextFieldDelegate_controlTextDidBeginEditing_;
|
||||
@@ -2015,7 +1993,7 @@ CPTextFieldStatePlaceholder = CPThemeState("placeholder");
|
||||
|
||||
frame.origin = [wind convertBaseToGlobal:frame.origin];
|
||||
|
||||
// Here we restore the the previous scrolling posiition
|
||||
// Here we restore the previous scrolling posiition
|
||||
[self _restorePreviousScrollingOrigin:previousScrollingOrigin];
|
||||
|
||||
return (CGRectGetMinX(frame) >= CGRectGetMinX(usableRect) &&
|
||||
@@ -2202,4 +2180,40 @@ var CPTextFieldIsEditableKey = "CPTextFieldIsEditableKey",
|
||||
[_source setObjectValue:aValue];
|
||||
}
|
||||
|
||||
@end
|
||||
@end
|
||||
|
||||
@implementation CPTextField (CPTrackingArea)
|
||||
{
|
||||
CPTrackingArea _textFieldTrackingArea;
|
||||
}
|
||||
|
||||
- (void)updateTrackingAreas
|
||||
{
|
||||
if (_textFieldTrackingArea)
|
||||
{
|
||||
[self removeTrackingArea:_textFieldTrackingArea];
|
||||
_textFieldTrackingArea = nil;
|
||||
}
|
||||
|
||||
if ([self isEnabled] && (_isEditable || _isSelectable))
|
||||
{
|
||||
var myBounds = CGRectMakeCopy([self bounds]),
|
||||
contentInset = [self currentValueForThemeAttribute:@"content-inset"];
|
||||
|
||||
_textFieldTrackingArea = [[CPTrackingArea alloc] initWithRect:CGRectInsetByInset(myBounds, contentInset)
|
||||
options:CPTrackingCursorUpdate | CPTrackingActiveInKeyWindow
|
||||
owner:self
|
||||
userInfo:nil];
|
||||
|
||||
[self addTrackingArea:_textFieldTrackingArea];
|
||||
}
|
||||
|
||||
[super updateTrackingAreas];
|
||||
}
|
||||
|
||||
- (void)cursorUpdate:(CPEvent)anEvent
|
||||
{
|
||||
[[CPCursor IBeamCursor] set];
|
||||
}
|
||||
|
||||
@end
|
||||
|
||||
@@ -0,0 +1,315 @@
|
||||
/*
|
||||
* CPFontDescriptor.j
|
||||
* AppKit
|
||||
*
|
||||
* Created by Emmanuel Maillard on 07/03/10.
|
||||
* Copyright Emmanuel Maillard 2010.
|
||||
*
|
||||
* This library is free software; you can redistribute it and/or
|
||||
* modify it under the terms of the GNU Lesser General Public
|
||||
* License as published by the Free Software Foundation; either
|
||||
* version 2.1 of the License, or (at your option) any later version.
|
||||
*
|
||||
* This library is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
|
||||
* Lesser General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU Lesser General Public
|
||||
* License along with this library; if not, write to the Free Software
|
||||
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
|
||||
*/
|
||||
|
||||
@import <Foundation/CPObject.j>
|
||||
/*
|
||||
Font descriptor dictionary keys
|
||||
*/
|
||||
|
||||
/*
|
||||
CPFontNameAttribute contains a CPString that specified the font name
|
||||
(may be an name list like: 'Marker Felt, Lucida Grande, Helvetica')
|
||||
*/
|
||||
CPFontNameAttribute = @"CPFontNameAttribute";
|
||||
/*
|
||||
CPFontSizeAttribute contains a CPString that specified the font size
|
||||
(as a float value)
|
||||
*/
|
||||
CPFontSizeAttribute = @"CPFontSizeAttribute";
|
||||
/*
|
||||
CPFontTraitsAttribute a CPDictionary that contains font traits keys
|
||||
(CPFontSymbolicTrait or CPFontWeightTrait)
|
||||
*/
|
||||
CPFontTraitsAttribute = @"CPFontTraitsAttribute";
|
||||
|
||||
// Font traits dictionary keys
|
||||
/*
|
||||
CPFontSymbolicTrait a CPNumber that contains CPFontFamilyClass and
|
||||
typeface information flags.
|
||||
*/
|
||||
CPFontSymbolicTrait = @"CPFontSymbolicTrait";
|
||||
|
||||
/*
|
||||
CPFontWeightTrait
|
||||
We use CPString with CSS string values for font weight
|
||||
(normal | bold | bolder | lighter | 100 | 200 | 300 | 400
|
||||
| 500 | 600 | 700 | 800 | 900)
|
||||
NOTE: Cocoa compatibility issue: NSFontWeightTrait are NSNumber for
|
||||
font weight (from -1.0 to 1.0, 0.0 for normal weight).
|
||||
*/
|
||||
CPFontWeightTrait = @"CPFontWeightTrait";
|
||||
|
||||
/*
|
||||
CPFontFamilyClass
|
||||
*/
|
||||
CPFontUnknownClass = 0 << 28;
|
||||
CPFontOldStyleSerifsClass = 1 << 28;
|
||||
CPFontTransitionalSerifsClass = 2 << 28;
|
||||
CPFontModernSerifsClass = 3 << 28;
|
||||
CPFontClarendonSerifsClass = 4 << 28;
|
||||
CPFontSlabSerifsClass = 5 << 28;
|
||||
CPFontFreeformSerifsClass = 7 << 28;
|
||||
CPFontSansSerifClass = 8 << 28;
|
||||
|
||||
CPFontSerifClass = (CPFontOldStyleSerifsClass | CPFontTransitionalSerifsClass |
|
||||
CPFontModernSerifsClass | CPFontClarendonSerifsClass |
|
||||
CPFontSlabSerifsClass | CPFontFreeformSerifsClass);
|
||||
|
||||
CPFontFamilyClassMask = 0xF0000000;
|
||||
|
||||
/*
|
||||
Typeface information
|
||||
*/
|
||||
CPFontItalicTrait = 1 << 0;
|
||||
CPFontBoldTrait = 1 << 1;
|
||||
CPFontExpandedTrait = 1 << 5; /* TODO: CCS 3 font-stretch */
|
||||
CPFontCondensedTrait = 1 << 6;
|
||||
CPFontSmallCapsTrait = 1 << 7;
|
||||
|
||||
/*!
|
||||
@ingroup appkit
|
||||
@class CPFontDescriptor
|
||||
*/
|
||||
@implementation CPFontDescriptor : CPObject
|
||||
{
|
||||
CPDictionary _attributes;
|
||||
}
|
||||
|
||||
/*!
|
||||
Returns a font descriptor with the specified attributes.
|
||||
|
||||
@param attributes a dictionary that describe the desired font descriptor
|
||||
@return the requested font descriptor
|
||||
*/
|
||||
+ (CPFontDescriptor)fontDescriptorWithFontAttributes:(CPDictionary)attributes
|
||||
{
|
||||
return [[CPFontDescriptor alloc] initWithFontAttributes:attributes];
|
||||
}
|
||||
|
||||
/*!
|
||||
Returns a font descriptor with the specified name and size.
|
||||
|
||||
@param fontName the name of the font
|
||||
@param aSize the size of the font (in points)
|
||||
@return the requested font descriptor
|
||||
*/
|
||||
+ (CPFontDescriptor)fontDescriptorWithName:(CPString)fontName size:(float)size
|
||||
{
|
||||
return [[CPFontDescriptor alloc] initWithFontAttributes:[CPDictionary dictionaryWithObjects:[fontName, [CPString stringWithString:size + '']] forKeys:[CPFontNameAttribute,CPFontSizeAttribute]]];
|
||||
}
|
||||
|
||||
/*!
|
||||
Initialize a font descriptor with the specified attributes.
|
||||
|
||||
@param attributes a dictionary that describe the desired font descriptor
|
||||
@return the requested font descriptor
|
||||
*/
|
||||
- (id)initWithFontAttributes:(CPDictionary)attributes
|
||||
{
|
||||
if (self = [super init])
|
||||
{
|
||||
_attributes = [[CPMutableDictionary alloc] init];
|
||||
|
||||
if (attributes)
|
||||
[_attributes addEntriesFromDictionary:attributes];
|
||||
}
|
||||
|
||||
return self;
|
||||
}
|
||||
|
||||
/*!
|
||||
Returns a new font descriptor that is the same as the receiver but with the
|
||||
specified attributes taking precedence over the existing ones.
|
||||
|
||||
@param attributes a dictionary that describe the desired font descriptor
|
||||
@return the new font descriptor
|
||||
*/
|
||||
- (CPFontDescriptor)fontDescriptorByAddingAttributes:(CPDictionary)attributes
|
||||
{
|
||||
var attrib = [_attributes copy];
|
||||
|
||||
[attrib addEntriesFromDictionary:attributes];
|
||||
|
||||
return [[CPFontDescriptor alloc] initWithFontAttributes:attrib];
|
||||
}
|
||||
|
||||
/*!
|
||||
Returns a new font descriptor that is the same as the receiver but with the specified size taking precedence over the existing ones.
|
||||
|
||||
@param aSize the new size
|
||||
@return the new font descriptor
|
||||
*/
|
||||
- (CPFontDescriptor)fontDescriptorWithSize:(float)aSize
|
||||
{
|
||||
var attrib = [_attributes copy];
|
||||
|
||||
[attrib setObject:[CPString stringWithString:aSize + ''] forKey:CPFontSizeAttribute];
|
||||
|
||||
return [[CPFontDescriptor alloc] initWithFontAttributes:attrib];
|
||||
}
|
||||
|
||||
/*!
|
||||
Returns a new font descriptor that is the same as the receiver but with
|
||||
the specified symbolic traits taking precedence over the existing ones.
|
||||
|
||||
@param symbolicTraits the desired new symbolic traits
|
||||
@return the new font descriptor
|
||||
*/
|
||||
- (CPFontDescriptor)fontDescriptorWithSymbolicTraits:(CPFontSymbolicTraits)symbolicTraits
|
||||
{
|
||||
var attrib = [_attributes copy];
|
||||
|
||||
if ([attrib objectForKey:CPFontTraitsAttribute])
|
||||
[[attrib objectForKey:CPFontTraitsAttribute] setObject:[CPNumber numberWithUnsignedInt:symbolicTraits]
|
||||
forKey:CPFontSymbolicTrait];
|
||||
else
|
||||
[attrib setObject:[CPDictionary dictionaryWithObject:[CPNumber numberWithUnsignedInt:symbolicTraits]
|
||||
forKey:CPFontSymbolicTrait] forKey:CPFontTraitsAttribute];
|
||||
|
||||
return [[CPFontDescriptor alloc] initWithFontAttributes:attrib];
|
||||
}
|
||||
|
||||
- (id)objectForKey:(id)aKey
|
||||
{
|
||||
return [_attributes objectForKey:aKey];
|
||||
}
|
||||
|
||||
- (CPDictionary)fontAttributes
|
||||
{
|
||||
return _attributes;
|
||||
}
|
||||
|
||||
- (float)pointSize
|
||||
{
|
||||
var value = [_attributes objectForKey:CPFontSizeAttribute];
|
||||
|
||||
return value ? [value floatValue] : 0.0;
|
||||
}
|
||||
|
||||
- (CPFontSymbolicTraits)symbolicTraits
|
||||
{
|
||||
var traits = [_attributes objectForKey:CPFontTraitsAttribute];
|
||||
|
||||
return (traits && [traits objectForKey:CPFontSymbolicTrait]) ? [[traits objectForKey:CPFontSymbolicTrait] unsignedIntValue] : 0;
|
||||
}
|
||||
|
||||
@end
|
||||
|
||||
var CPFontDescriptorAttributesKey = @"CPFontDescriptorAttributesKey";
|
||||
|
||||
@implementation CPFontDescriptor (CPCoding)
|
||||
|
||||
/*!
|
||||
Initializes the font descriptor from a coder.
|
||||
|
||||
@param aCoder the coder from which to read the font descriptor data
|
||||
@return the initialized font
|
||||
*/
|
||||
- (id)initWithCoder:(CPCoder)aCoder
|
||||
{
|
||||
return [self initWithFontAttributes:[aCoder decodeObjectForKey:CPFontDescriptorAttributesKey]];
|
||||
}
|
||||
|
||||
/*!
|
||||
Writes the font descriptor to a coder.
|
||||
|
||||
@param aCoder the coder to which the data will be written
|
||||
*/
|
||||
- (void)encodeWithCoder:(CPCoder)aCoder
|
||||
{
|
||||
[aCoder encodeObject:_attributes forKey:CPFontDescriptorAttributesKey];
|
||||
}
|
||||
|
||||
@end
|
||||
|
||||
var _wrapNameRegEx = new RegExp(/(\w+\s+\w+)(,*)/g);
|
||||
|
||||
/*
|
||||
Helper methods to CPFont for generating CSS font style
|
||||
*/
|
||||
@implementation CPFontDescriptor (CPFontCSSHelper)
|
||||
|
||||
- (CPString)fontStyleCSSString
|
||||
{
|
||||
return [self symbolicTraits] & CPFontItalicTrait ? @"italic" : @"normal";
|
||||
}
|
||||
|
||||
- (CPString)fontWeightCSSString
|
||||
{
|
||||
var traitsAttributes = [_attributes objectForKey:CPFontTraitsAttribute];
|
||||
|
||||
if (traitsAttributes)
|
||||
{
|
||||
/* give preference to CPFontWeightTrait */
|
||||
if ([traitsAttributes objectForKey:CPFontWeightTrait])
|
||||
return [traitsAttributes objectForKey:CPFontWeightTrait];
|
||||
/* else fallback to facetype symbolic traits */
|
||||
if ([self symbolicTraits] & CPFontBoldTrait)
|
||||
return @"bold";
|
||||
}
|
||||
|
||||
return @"normal";
|
||||
}
|
||||
|
||||
- (CPString)fontSizeCSSString
|
||||
{
|
||||
return [_attributes objectForKey:CPFontSizeAttribute] ? [[_attributes objectForKey:CPFontSizeAttribute] intValue] + "px" : @"";
|
||||
}
|
||||
|
||||
- (CPString)fontFamilyCSSString
|
||||
{
|
||||
var aName = @"";
|
||||
|
||||
if ([_attributes objectForKey:CPFontNameAttribute])
|
||||
aName += [_attributes objectForKey:CPFontNameAttribute].replace(_wrapNameRegEx, '"$1"$2');
|
||||
|
||||
var symbolicTraits = [self symbolicTraits];
|
||||
|
||||
if (symbolicTraits)
|
||||
{
|
||||
if ((symbolicTraits & CPFontFamilyClassMask) & CPFontSansSerifClass)
|
||||
aName += @", sans-serif";
|
||||
else if ((symbolicTraits & CPFontFamilyClassMask) & CPFontSerifClass)
|
||||
aName += @", serif";
|
||||
}
|
||||
|
||||
return aName;
|
||||
}
|
||||
|
||||
- (CPString)fontVariantCSSString
|
||||
{
|
||||
if ([self symbolicTraits] & CPFontSmallCapsTrait)
|
||||
return @"small-caps";
|
||||
|
||||
return @"normal";
|
||||
}
|
||||
|
||||
- (CPString)cssString
|
||||
{
|
||||
return [CPString stringWithString:[self fontStyleCSSString] + " "
|
||||
+ [self fontVariantCSSString] + " "
|
||||
+ [self fontWeightCSSString] + " "
|
||||
+ [self fontSizeCSSString] + " "
|
||||
+ [self fontFamilyCSSString]];
|
||||
}
|
||||
|
||||
@end
|
||||
@@ -0,0 +1,443 @@
|
||||
/*
|
||||
* CPFontPanel.j
|
||||
* AppKit
|
||||
*
|
||||
* TODOs:
|
||||
* 1. make browser-width for size smaller and fix columns
|
||||
* 2. add all the missing features from the MacOS X counterpart (sampleview)
|
||||
*
|
||||
*
|
||||
* Created by Daniel Boehringer on 2/JAN/2014.
|
||||
* All modifications copyright Daniel Boehringer 2013.
|
||||
* Extensive code formatting and review by Andrew Hankinson
|
||||
* Based on original work by
|
||||
* Created by Emmanuel Maillard on 06/03/2010.
|
||||
* Copyright Emmanuel Maillard 2010.
|
||||
*
|
||||
* This library is free software; you can redistribute it and/or
|
||||
* modify it under the terms of the GNU Lesser General Public
|
||||
* License as published by the Free Software Foundation; either
|
||||
* version 2.1 of the License, or (at your option) any later version.
|
||||
*
|
||||
* This library is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
|
||||
* Lesser General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU Lesser General Public
|
||||
* License along with this library; if not, write to the Free Software
|
||||
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
|
||||
*/
|
||||
|
||||
|
||||
@import "CPPanel.j"
|
||||
@import "CPColorWell.j"
|
||||
@import "CPColorPanel.j"
|
||||
@import "CPBrowser.j"
|
||||
@import "CPText.j"
|
||||
@import "CPFontManager.j"
|
||||
|
||||
|
||||
@class CPTextStorage
|
||||
@class CPLayoutManager
|
||||
@class CPTextContainer
|
||||
@class CPFontManager
|
||||
|
||||
/*
|
||||
Collection indexes
|
||||
*/
|
||||
var kTypefaceIndex_Normal = 0,
|
||||
kTypefaceIndex_Italic = 1,
|
||||
kTypefaceIndex_Bold = 2,
|
||||
kTypefaceIndex_BoldItalic = 3,
|
||||
kToolbarHeight = 32,
|
||||
kBorderSpacing = 6,
|
||||
kInnerSpacing = 2,
|
||||
kNothingChanged = 0,
|
||||
kFontNameChanged = 1,
|
||||
kTypefaceChanged = 2,
|
||||
kSizeChanged = 3,
|
||||
kTextColorChanged = 4,
|
||||
kBackgroundColorChanged = 5,
|
||||
kUnderlineChanged = 6,
|
||||
kWeightChanged = 7,
|
||||
_sharedFontPanel;
|
||||
|
||||
// FIXME<!> Locale support
|
||||
var _availableTraits= [@"Normal", @"Italic", @"Bold", @"Bold Italic"],
|
||||
_availableSizes = [@"9", @"10", @"11", @"12", @"13", @"14", @"18", @"24", @"36", @"48", @"72", @"96"];
|
||||
|
||||
|
||||
/*!
|
||||
@ingroup appkit
|
||||
@class CPFontPanel
|
||||
*/
|
||||
@implementation CPFontPanel : CPPanel
|
||||
{
|
||||
id _fontBrowser;
|
||||
id _traitBrowser;
|
||||
id _sizeBrowser;
|
||||
CPArray _availableFonts;
|
||||
id _textColorWell;
|
||||
CPColor _textColor;
|
||||
int _currentColorButtonTag;
|
||||
BOOL _setupDone;
|
||||
int _fontChanges;
|
||||
}
|
||||
|
||||
|
||||
#pragma mark -
|
||||
#pragma mark Class methods
|
||||
|
||||
/*!
|
||||
Check if the shared Font panel exists.
|
||||
*/
|
||||
+ (BOOL)sharedFontPanelExists
|
||||
{
|
||||
return _sharedFontPanel !== nil;
|
||||
}
|
||||
|
||||
/*!
|
||||
Return the shared Font panel.
|
||||
*/
|
||||
+ (CPFontPanel)sharedFontPanel
|
||||
{
|
||||
if (!_sharedFontPanel)
|
||||
_sharedFontPanel = [[CPFontPanel alloc] init];
|
||||
|
||||
return _sharedFontPanel;
|
||||
}
|
||||
|
||||
|
||||
#pragma mark -
|
||||
#pragma mark Init methods
|
||||
|
||||
/*! @ignore */
|
||||
- (id)init
|
||||
{
|
||||
if (self = [super initWithContentRect:CGRectMake(100, 90, 450, 394) styleMask:(CPTitledWindowMask | CPClosableWindowMask /*| CPResizableWindowMask*/ )])
|
||||
{
|
||||
[[self contentView] setBackgroundColor:[CPColor colorWithWhite:0.95 alpha:1.0]];
|
||||
[self setTitle:@"Font Panel"];
|
||||
[self setLevel:CPFloatingWindowLevel];
|
||||
[self setFloatingPanel:YES];
|
||||
[self setBecomesKeyOnlyIfNeeded:YES];
|
||||
[self setMinSize:CGSizeMake(378, 394)];
|
||||
|
||||
_availableFonts = [[CPFontManager sharedFontManager] availableFonts];
|
||||
_textColor = [CPColor blackColor];
|
||||
_setupDone = NO;
|
||||
_fontChanges = kNothingChanged;
|
||||
}
|
||||
|
||||
return self;
|
||||
}
|
||||
|
||||
/*! @ignore */
|
||||
- (void)_setupToolbarView
|
||||
{
|
||||
_toolbarView = [[CPView alloc] initWithFrame:CGRectMake(0, kBorderSpacing, CGRectGetWidth([self frame]), kToolbarHeight)];
|
||||
[_toolbarView setAutoresizingMask:CPViewWidthSizable];
|
||||
|
||||
// Text color
|
||||
_textColorWell = [[CPColorWell alloc] initWithFrame:CGRectMake(10, 0, 25, 25)];
|
||||
[_textColorWell setColor:_textColor];
|
||||
[_toolbarView addSubview:_textColorWell];
|
||||
}
|
||||
|
||||
- (void)_setupBrowser:(CPBrowser)aBrowser
|
||||
{
|
||||
[aBrowser setTarget:self];
|
||||
[aBrowser setAction:@selector(browserClicked:)];
|
||||
[aBrowser setDoubleAction:@selector(dblClicked:)];
|
||||
[aBrowser setAllowsEmptySelection:NO];
|
||||
[aBrowser setAllowsMultipleSelection:NO];
|
||||
[aBrowser setDelegate:self];
|
||||
[[self contentView] addSubview:aBrowser];
|
||||
}
|
||||
|
||||
- (void)_setupContents
|
||||
{
|
||||
if (_setupDone)
|
||||
return;
|
||||
|
||||
_setupDone = YES;
|
||||
|
||||
[self _setupToolbarView];
|
||||
|
||||
var contentView = [self contentView],
|
||||
label = [CPTextField labelWithTitle:@"Font name"],
|
||||
contentBounds = [contentView bounds],
|
||||
upperView = [[CPView alloc] initWithFrame:CGRectMake(0, 0, CGRectGetWidth(contentBounds), CGRectGetHeight(contentBounds) - (kBorderSpacing + kToolbarHeight + kInnerSpacing))];
|
||||
|
||||
[contentView addSubview:_toolbarView];
|
||||
|
||||
_fontBrowser = [[CPBrowser alloc] initWithFrame:CGRectMake(10, 35, 150, 350)];
|
||||
_traitBrowser = [[CPBrowser alloc] initWithFrame:CGRectMake(155, 35, 150, 350)];
|
||||
_sizeBrowser = [[CPBrowser alloc] initWithFrame:CGRectMake(300, 35, 140, 350)];
|
||||
|
||||
[self _setupBrowser:_fontBrowser];
|
||||
[self _setupBrowser:_traitBrowser];
|
||||
[self _setupBrowser:_sizeBrowser];
|
||||
|
||||
[[CPNotificationCenter defaultCenter] addObserver:self
|
||||
selector:@selector(textViewDidChangeSelection:)
|
||||
name:CPTextViewDidChangeSelectionNotification
|
||||
object:nil];
|
||||
}
|
||||
|
||||
- (void)textViewDidChangeSelection:(CPNotification)notification
|
||||
{
|
||||
[self _refreshWithTextView:[notification object]];
|
||||
|
||||
}
|
||||
|
||||
- (void)_refreshWithTextView:(CPTextView)textView
|
||||
{
|
||||
if (![self isVisible])
|
||||
return;
|
||||
|
||||
var attribs = [textView _attributesForFontPanel],
|
||||
font = [attribs objectForKey:CPFontAttributeName] || [[textView textStorage] font] || [CPFont systemFontOfSize:12.0],
|
||||
color = [attribs objectForKey:CPForegroundColorAttributeName];
|
||||
|
||||
if (!font)
|
||||
return;
|
||||
|
||||
var trait = kTypefaceIndex_Normal;
|
||||
|
||||
if ([font isItalic] && [font isBold])
|
||||
trait = kTypefaceIndex_BoldItalic;
|
||||
else if ([font isItalic])
|
||||
trait = kTypefaceIndex_Italic;
|
||||
else if ([font isBold])
|
||||
trait = kTypefaceIndex_Bold;
|
||||
|
||||
[self setCurrentFont:font];
|
||||
[self setCurrentTrait:trait];
|
||||
[self setCurrentSize:[font size] + ""]; //cast to string
|
||||
|
||||
if (!color)
|
||||
return;
|
||||
|
||||
[_textColorWell setColor:color];
|
||||
}
|
||||
|
||||
- (void)orderFront:(id)sender
|
||||
{
|
||||
[self _setupContents];
|
||||
[super orderFront:sender];
|
||||
[self _refreshWithTextView:[[CPApp keyWindow] firstResponder]];
|
||||
}
|
||||
|
||||
- (void)reloadDefaultFontFamilies
|
||||
{
|
||||
_availableFonts = [[CPFontManager sharedFontManager] availableFonts];
|
||||
}
|
||||
|
||||
- (BOOL)worksWhenModal
|
||||
{
|
||||
return YES;
|
||||
}
|
||||
|
||||
/*!
|
||||
@param aFont the font to convert.
|
||||
@return The converted font or \c aFont if failed to convert.
|
||||
*/
|
||||
- (CPFont)panelConvertFont:(CPFont)aFont
|
||||
{
|
||||
var newFont = aFont,
|
||||
index = 0;
|
||||
|
||||
switch (_fontChanges)
|
||||
{
|
||||
case kFontNameChanged:
|
||||
newFont = [CPFont fontWithDescriptor:[[aFont fontDescriptor] fontDescriptorByAddingAttributes:
|
||||
[CPDictionary dictionaryWithObject:[self currentFont] forKey:CPFontNameAttribute]] size:0.0];
|
||||
break;
|
||||
|
||||
case kTypefaceChanged:
|
||||
index = [self currentTrait];
|
||||
if (index == kTypefaceIndex_BoldItalic)
|
||||
newFont = [[CPFontManager sharedFontManager] convertFont:aFont toHaveTrait:CPBoldFontMask | CPItalicFontMask];
|
||||
else if (index == kTypefaceIndex_Bold)
|
||||
newFont = [[CPFontManager sharedFontManager] convertFont:aFont toHaveTrait:CPBoldFontMask];
|
||||
else if (index == kTypefaceIndex_Italic)
|
||||
newFont = [[CPFontManager sharedFontManager] convertFont:aFont toHaveTrait:CPItalicFontMask];
|
||||
else
|
||||
newFont = [[CPFontManager sharedFontManager] convertFont:aFont toNotHaveTrait:CPBoldFontMask | CPItalicFontMask];
|
||||
break;
|
||||
|
||||
case kSizeChanged:
|
||||
newFont = [[CPFontManager sharedFontManager] convertFont:aFont toSize:[self currentSize]];
|
||||
break;
|
||||
|
||||
case kNothingChanged:
|
||||
break;
|
||||
|
||||
default:
|
||||
CPLog.trace(@"FIXME: -[" + [self className] + " " + _cmd + "] unhandled _fontChanges: " + _fontChanges);
|
||||
break;
|
||||
}
|
||||
|
||||
return newFont;
|
||||
}
|
||||
|
||||
- (void)setCurrentSize:(CGSize)aSize
|
||||
{
|
||||
[_sizeBrowser selectRow:[_availableSizes indexOfObject:aSize] inColumn:0];
|
||||
}
|
||||
|
||||
- (CPString)currentSize
|
||||
{
|
||||
return [_sizeBrowser selectedItem];
|
||||
}
|
||||
|
||||
- (void)setCurrentFont:(CPFont)aFont
|
||||
{
|
||||
[_fontBrowser selectRow:[_availableFonts indexOfObject:[aFont familyName]] inColumn:0];
|
||||
}
|
||||
|
||||
- (CPString)currentFont
|
||||
{
|
||||
return [_fontBrowser selectedItem];
|
||||
}
|
||||
|
||||
- (void)setCurrentTrait:(unsigned)aTrait
|
||||
{
|
||||
var row = 0;
|
||||
|
||||
switch (aTrait)
|
||||
{
|
||||
case kTypefaceIndex_Italic:
|
||||
row = 1;
|
||||
break;
|
||||
|
||||
case kTypefaceIndex_Bold:
|
||||
row = 2;
|
||||
break;
|
||||
|
||||
case kTypefaceIndex_BoldItalic:
|
||||
row = 3;
|
||||
break;
|
||||
}
|
||||
|
||||
[_traitBrowser selectRow:row inColumn:0];
|
||||
}
|
||||
|
||||
// FIXME<!> Locale support
|
||||
- (unsigned)currentTrait
|
||||
{
|
||||
var sel = [_traitBrowser selectedItem];
|
||||
|
||||
if (sel === "Italic")
|
||||
return kTypefaceIndex_Italic;
|
||||
|
||||
if (sel === "Bold")
|
||||
return kTypefaceIndex_Bold;
|
||||
|
||||
if (sel === "Bold Italic")
|
||||
return kTypefaceIndex_BoldItalic;
|
||||
|
||||
return kTypefaceIndex_Normal;
|
||||
}
|
||||
|
||||
/*!
|
||||
Set the selected font in Font panel.
|
||||
@param font the selected font
|
||||
@param flag if \c the current selection have multiple fonts.
|
||||
*/
|
||||
- (void)setPanelFont:(CPFont)font isMultiple:(BOOL)flag
|
||||
{
|
||||
[self _setupContents];
|
||||
|
||||
if ([self currentFont] !== [font familyName])
|
||||
[self setCurrentFont:[font familyName]];
|
||||
|
||||
if ([self currentSize] != [font size])
|
||||
[self setCurrentSize:[font size]];
|
||||
|
||||
var typefaceIndex = kTypefaceIndex_Normal,
|
||||
symbolicTraits = [[font fontDescriptor] symbolicTraits];
|
||||
|
||||
if ((symbolicTraits & CPFontItalicTrait) && (symbolicTraits & CPFontBoldTrait))
|
||||
typefaceIndex = kTypefaceIndex_BoldItalic;
|
||||
else if (symbolicTraits & CPFontItalicTrait)
|
||||
typefaceIndex = kTypefaceIndex_Italic;
|
||||
else if (symbolicTraits & CPFontBoldTrait)
|
||||
typefaceIndex = kTypefaceIndex_Bold;
|
||||
|
||||
if ([self currentTrait] != typefaceIndex)
|
||||
[self setCurrentTrait:typefaceIndex ];
|
||||
|
||||
_fontChanges = kNothingChanged;
|
||||
}
|
||||
|
||||
- (void)changeColor:(id)sender
|
||||
{
|
||||
_textColor = [sender color];
|
||||
_fontChanges = kTextColorChanged;
|
||||
[[CPFontManager sharedFontManager] modifyFontViaPanel:self];
|
||||
}
|
||||
|
||||
////////////////////////////////////////////////////////////////////
|
||||
// TODO: ask CPFontManager for traits //
|
||||
- (void)browserClicked:(id)aBrowser
|
||||
{
|
||||
if (aBrowser === _fontBrowser)
|
||||
{
|
||||
_fontChanges = kFontNameChanged;
|
||||
[[CPFontManager sharedFontManager] modifyFontViaPanel:self];
|
||||
}
|
||||
else if (aBrowser === _traitBrowser)
|
||||
{
|
||||
_fontChanges = kTypefaceChanged;
|
||||
[[CPFontManager sharedFontManager] modifyFontViaPanel:self];
|
||||
}
|
||||
else if (aBrowser === _sizeBrowser)
|
||||
{
|
||||
_fontChanges = kSizeChanged;
|
||||
[[CPFontManager sharedFontManager] modifyFontViaPanel:self];
|
||||
}
|
||||
}
|
||||
|
||||
- (void)dblClicked:(id)sender
|
||||
{
|
||||
// alert("DOUBLE");
|
||||
}
|
||||
|
||||
- (id)browser:(id)aBrowser numberOfChildrenOfItem:(id)anItem
|
||||
{
|
||||
if (aBrowser === _fontBrowser)
|
||||
return [_availableFonts count];
|
||||
|
||||
if (aBrowser === _traitBrowser)
|
||||
return [_availableTraits count];
|
||||
|
||||
return [_availableSizes count]
|
||||
}
|
||||
|
||||
- (id)browser:(id)aBrowser child:(int)index ofItem:(id)anItem
|
||||
{
|
||||
if (aBrowser === _fontBrowser)
|
||||
return [_availableFonts objectAtIndex:index];
|
||||
|
||||
if (aBrowser === _traitBrowser)
|
||||
return [_availableTraits objectAtIndex:index];
|
||||
|
||||
return [_availableSizes objectAtIndex:index];
|
||||
}
|
||||
|
||||
- (id)browser:(id)aBrowser objectValueForItem:(id)anItem
|
||||
{
|
||||
return anItem;
|
||||
}
|
||||
|
||||
- (BOOL)browser:(id)aBrowser isLeafItem:(id)anItem
|
||||
{
|
||||
return YES;
|
||||
}
|
||||
|
||||
@end
|
||||
|
||||
[CPFontManager setFontPanelFactory:[CPFontPanel class]];
|
||||
File diff suppressed because it is too large
Load Diff
@@ -0,0 +1,223 @@
|
||||
/*
|
||||
* CPParagraphStyle.j
|
||||
* AppKit
|
||||
*
|
||||
* FIXME
|
||||
* This is basically a stub.
|
||||
* We need to store all the spacing informations as well as writing direction (among others)
|
||||
*
|
||||
* Created by Daniel Boehringer on 11/01/2014
|
||||
* Copyright Daniel Boehringer 2014.
|
||||
*
|
||||
* This library is free software; you can redistribute it and/or
|
||||
* modify it under the terms of the GNU Lesser General Public
|
||||
* License as published by the Free Software Foundation; either
|
||||
* version 2.1 of the License, or (at your option) any later version.
|
||||
*
|
||||
* This library is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
|
||||
* Lesser General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU Lesser General Public
|
||||
* License along with this library; if not, write to the Free Software
|
||||
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
|
||||
*/
|
||||
|
||||
@import <Foundation/CPObject.j>
|
||||
@import <Foundation/CPArray.j>
|
||||
|
||||
@import "CPText.j"
|
||||
|
||||
CPLeftTabStopType = 0;
|
||||
|
||||
CPParagraphStyleAttributeName = @"CPParagraphStyleAttributeName";
|
||||
|
||||
var _sharedDefaultParagraphStyle,
|
||||
_defaultTabStopArray;
|
||||
|
||||
@implementation CPParagraphStyle : CPObject
|
||||
{
|
||||
CPArray _tabStops @accessors(property=tabStops);
|
||||
CPTextAlignment _alignment @accessors(property=alignment);
|
||||
unsigned _firstLineHeadIndent @accessors(property=firstLineHeadIndent);
|
||||
unsigned _headIndent @accessors(property=headIndent);
|
||||
unsigned _tailIndent @accessors(property=tailIndent);
|
||||
unsigned _paragraphSpacing @accessors(property=paragraphSpacing);
|
||||
unsigned _minimumLineHeight @accessors(property=minimumLineHeight);
|
||||
unsigned _maximumLineHeight @accessors(property=maximumLineHeight);
|
||||
unsigned _lineSpacing @accessors(property=lineSpacing);
|
||||
}
|
||||
|
||||
|
||||
#pragma mark -
|
||||
#pragma mark Class methods
|
||||
|
||||
+ (CPParagraphStyle)defaultParagraphStyle
|
||||
{
|
||||
if (!_sharedDefaultParagraphStyle)
|
||||
_sharedDefaultParagraphStyle = [self new];
|
||||
|
||||
return _sharedDefaultParagraphStyle;
|
||||
}
|
||||
|
||||
+ (CPArray)_defaultTabStops
|
||||
{
|
||||
if (!_defaultTabStopArray)
|
||||
{
|
||||
var i;
|
||||
_defaultTabStopArray = [];
|
||||
|
||||
// <!> FIXME: Define constants for these magic numbers: 13, 28
|
||||
for (i = 1; i < 16 ; i++)
|
||||
{
|
||||
_defaultTabStopArray.push([[CPTextTab alloc] initWithType:CPLeftTabStopType location:i * 28]);
|
||||
}
|
||||
}
|
||||
|
||||
return _defaultTabStopArray;
|
||||
}
|
||||
|
||||
|
||||
#pragma mark -
|
||||
#pragma mark Init methods
|
||||
|
||||
- (id)init
|
||||
{
|
||||
[self _initWithDefaults];
|
||||
|
||||
return self;
|
||||
}
|
||||
|
||||
- (CPParagraphStyle)initWithParagraphStyle:(CPParagraphStyle)other
|
||||
{
|
||||
self = [super init];
|
||||
|
||||
_tabStops = [other._tabStops copy];
|
||||
_alignment = other._alignment;
|
||||
_firstLineHeadIndent = other._firstLineHeadIndent;
|
||||
_headIndent = other._headIndent;
|
||||
_tailIndent = other._tailIndent;
|
||||
_paragraphSpacing = other._paragraphSpacing;
|
||||
_minimumLineHeight = other._minimumLineHeight;
|
||||
_maximumLineHeight = other._maximumLineHeight;
|
||||
_lineSpacing = other._lineSpacing;
|
||||
|
||||
return self;
|
||||
}
|
||||
|
||||
- (void)_initWithDefaults
|
||||
{
|
||||
_alignment = CPLeftTextAlignment;
|
||||
_tabStops = [[[self class] _defaultTabStops] copy];
|
||||
}
|
||||
|
||||
- (void)addTabStop:(CPTextTab)aStop
|
||||
{
|
||||
_tabStops.push(aStop);
|
||||
}
|
||||
|
||||
- (id)copy
|
||||
{
|
||||
var other = [[self class] alloc];
|
||||
|
||||
return [other initWithParagraphStyle:self];
|
||||
}
|
||||
|
||||
@end
|
||||
|
||||
|
||||
var CPParagraphStyleTabStopsKey = @"CPParagraphStyleTabStopsKey",
|
||||
CPParagraphStyleAlignmentKey = @"CPParagraphStyleAlignmentKey",
|
||||
CPParagraphStyleFirstLineHeadIndentKey = @"CPParagraphStyleFirstLineHeadIndentKey",
|
||||
CPParagraphStyleHeadIndentKey = @"CPParagraphStyleHeadIndentKey",
|
||||
CPParagraphStyleTailIndentKey = @"CPParagraphStyleTailIndentKey",
|
||||
CPParagraphStyleParagraphSpacingKey = @"CPParagraphStyleParagraphSpacingKey",
|
||||
CPParagraphStyleMinimumLineHeightKey = @"CPParagraphStyleMinimumLineHeightKey",
|
||||
CPParagraphStyleMaximumLineHeightKey = @"CPParagraphStyleMaximumLineHeightKey",
|
||||
CPParagraphStyleLineSpacingKey = @"CPParagraphStyleLineSpacingKey";
|
||||
|
||||
@implementation CPParagraphStyle (CPCoding)
|
||||
|
||||
- (id)initWithCoder:(id)aCoder
|
||||
{
|
||||
self = [self init];
|
||||
|
||||
if (self)
|
||||
{
|
||||
_tabStops = [aCoder decodeObjectForKey:"CPParagraphStyleTabStopsKey"];
|
||||
_alignment = [aCoder decodeIntForKey:"CPParagraphStyleAlignmentKey"];
|
||||
_firstLineHeadIndent = [aCoder decodeIntForKey:"CPParagraphStyleFirstLineHeadIndentKey"];
|
||||
_headIndent = [aCoder decodeIntForKey:"CPParagraphStyleHeadIndentKey"];
|
||||
_tailIndent = [aCoder decodeIntForKey:"CPParagraphStyleTailIndentKey"];
|
||||
_paragraphSpacing = [aCoder decodeIntForKey:"CPParagraphStyleParagraphSpacingKey"];
|
||||
_minimumLineHeight = [aCoder decodeIntForKey:"CPParagraphStyleMinimumLineHeightKey"];
|
||||
_maximumLineHeight = [aCoder decodeIntForKey:"CPParagraphStyleMaximumLineHeightKey"];
|
||||
_lineSpacing = [aCoder decodeIntForKey:"CPParagraphStyleLineSpacingKey"];
|
||||
}
|
||||
|
||||
return self;
|
||||
}
|
||||
|
||||
- (void)encodeWithCoder:(id)aCoder
|
||||
{
|
||||
[aCoder encodeInt:_alignment forKey:"CPParagraphStyleAlignmentKey"];
|
||||
[aCoder encodeObject:_tabStops forKey:"CPParagraphStyleTabStopsKey"];
|
||||
[aCoder encodeInt:_firstLineHeadIndent forKey:"CPParagraphStyleFirstLineHeadIndentKey"];
|
||||
[aCoder encodeInt:_headIndent forKey:"CPParagraphStyleHeadIndentKey"];
|
||||
[aCoder encodeInt:_tailIndent forKey:"CPParagraphStyleTailIndentKey"];
|
||||
[aCoder encodeInt:_paragraphSpacing forKey:"CPParagraphStyleParagraphSpacingKey"];
|
||||
[aCoder encodeInt:_minimumLineHeight forKey:"CPParagraphStyleMinimumLineHeightKey"];
|
||||
[aCoder encodeInt:_maximumLineHeight forKey:"CPParagraphStyleMaximumLineHeightKey"];
|
||||
[aCoder encodeInt:_lineSpacing forKey:"CPParagraphStyleLineSpacingKey"];
|
||||
}
|
||||
|
||||
|
||||
@end
|
||||
|
||||
|
||||
@implementation CPTextTab : CPObject
|
||||
{
|
||||
int _type @accessors(property = tabStopType);
|
||||
double _location @accessors(property = location);
|
||||
}
|
||||
|
||||
- (id)initWithType:(CPTabStopType) aType location:(double) aLocation
|
||||
{
|
||||
if ([self = [super init]])
|
||||
{
|
||||
_type = aType;
|
||||
_location = aLocation;
|
||||
}
|
||||
|
||||
return self;
|
||||
}
|
||||
|
||||
@end
|
||||
|
||||
|
||||
var CPTextTabTypeKey = @"CPTextTabTypeKey",
|
||||
CPTextTabLocationKey = @"CPTextTabLocationKey";
|
||||
|
||||
@implementation CPTextTab (CPCoding)
|
||||
|
||||
- (id)initWithCoder:(id)aCoder
|
||||
{
|
||||
self = [self init];
|
||||
|
||||
if (self)
|
||||
{
|
||||
_type = [aCoder decodeIntForKey:"CPTextTabTypeKey"];
|
||||
_location = [aCoder decodeDoubleForKey:"CPTextTabLocationKey"];
|
||||
}
|
||||
|
||||
return self;
|
||||
}
|
||||
|
||||
- (void)encodeWithCoder:(id)aCoder
|
||||
{
|
||||
[aCoder encodeInt:_type forKey:"CPTextTabTypeKey"];
|
||||
[aCoder encodeDouble:_location forKey:"CPTextTabLocationKey"];
|
||||
}
|
||||
|
||||
@end
|
||||
@@ -0,0 +1,252 @@
|
||||
/*
|
||||
* CPTextContainer.j
|
||||
* AppKit
|
||||
*
|
||||
* Created by Emmanuel Maillard on 27/02/2010.
|
||||
* Copyright Emmanuel Maillard 2010.
|
||||
*
|
||||
* This library is free software; you can redistribute it and/or
|
||||
* modify it under the terms of the GNU Lesser General Public
|
||||
* License as published by the Free Software Foundation; either
|
||||
* version 2.1 of the License, or (at your option) any later version.
|
||||
*
|
||||
* This library is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
|
||||
* Lesser General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU Lesser General Public
|
||||
* License along with this library; if not, write to the Free Software
|
||||
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
|
||||
*/
|
||||
|
||||
@import <Foundation/CPGeometry.j>
|
||||
@import "CPLayoutManager.j"
|
||||
|
||||
@class CPTextView
|
||||
@class CPLayoutManager
|
||||
|
||||
/*
|
||||
@global
|
||||
@group CPLineSweepDirection
|
||||
*/
|
||||
CPLineSweepLeft = 0;
|
||||
/*
|
||||
@global
|
||||
@group CPLineSweepDirection
|
||||
*/
|
||||
CPLineSweepRight = 1;
|
||||
/*
|
||||
@global
|
||||
@group CPLineSweepDirection
|
||||
*/
|
||||
CPLineSweepDown = 2;
|
||||
/*
|
||||
@global
|
||||
@group CPLineSweepDirection
|
||||
*/
|
||||
CPLineSweepUp = 3;
|
||||
|
||||
/*
|
||||
@global
|
||||
@group CPLineMovementDirection
|
||||
*/
|
||||
CPLineDoesntMoves = 0;
|
||||
/*
|
||||
@global
|
||||
@group CPLineMovementDirection
|
||||
*/
|
||||
CPLineMovesLeft = 1;
|
||||
/*
|
||||
@global
|
||||
@group CPLineMovementDirection
|
||||
*/
|
||||
CPLineMovesRight = 2;
|
||||
/*
|
||||
@global
|
||||
@group CPLineMovementDirection
|
||||
*/
|
||||
CPLineMovesDown = 3;
|
||||
/*
|
||||
@global
|
||||
@group CPLineMovementDirection
|
||||
*/
|
||||
CPLineMovesUp = 4;
|
||||
|
||||
/*!
|
||||
@ingroup appkit
|
||||
@class CPTextContainer
|
||||
*/
|
||||
@implementation CPTextContainer : CPObject
|
||||
{
|
||||
float _lineFragmentPadding @accessors(property=lineFragmentPadding);
|
||||
CGSize _size @accessors(property=containerSize);
|
||||
CPLayoutManager _layoutManager @accessors(property=layoutManager);
|
||||
CPTextView _textView @accessors(property=textView);
|
||||
BOOL _inResizing;
|
||||
}
|
||||
|
||||
|
||||
#pragma mark -
|
||||
#pragma mark Init methods
|
||||
|
||||
- (id)initWithContainerSize:(CGSize)aSize
|
||||
{
|
||||
self = [super init];
|
||||
|
||||
if (self)
|
||||
{
|
||||
_size = aSize;
|
||||
[self _init];
|
||||
}
|
||||
|
||||
return self;
|
||||
}
|
||||
|
||||
- (id)init
|
||||
{
|
||||
return [self initWithContainerSize:CPMakeSize(1e7, 1e7)];
|
||||
}
|
||||
|
||||
- (void)_init
|
||||
{
|
||||
_lineFragmentPadding = 0.0;
|
||||
|
||||
_layoutManager = [[CPLayoutManager alloc] init];
|
||||
[_layoutManager addTextContainer:self];
|
||||
}
|
||||
|
||||
#pragma mark -
|
||||
#pragma mark Setter methods
|
||||
|
||||
- (void)setContainerSize:(CGSize)someSize
|
||||
{
|
||||
var oldSize = _size;
|
||||
|
||||
_size = CGSizeMakeCopy(someSize);
|
||||
|
||||
if (oldSize.width != _size.width)
|
||||
{
|
||||
_inResizing = YES;
|
||||
[_layoutManager invalidateLayoutForCharacterRange:CPMakeRange(0, [[_layoutManager textStorage] length])
|
||||
isSoft:NO
|
||||
actualCharacterRange:NULL];
|
||||
|
||||
[_layoutManager _validateLayoutAndGlyphs];
|
||||
[_textView sizeToFit]; // this is necessary to adopt the height of CPTextView in case of rewrapping
|
||||
_inResizing = NO;
|
||||
}
|
||||
}
|
||||
|
||||
// Controls whether the receiver adjusts the width of its bounding rectangle when its text view is resized.
|
||||
- (void)setWidthTracksTextView:(BOOL)flag
|
||||
{
|
||||
[_textView setPostsFrameChangedNotifications:flag];
|
||||
|
||||
if (flag)
|
||||
{
|
||||
[[CPNotificationCenter defaultCenter] addObserver:self
|
||||
selector:@selector(textViewFrameChanged:)
|
||||
name:CPViewFrameDidChangeNotification
|
||||
object:_textView];
|
||||
}
|
||||
else
|
||||
{
|
||||
[[CPNotificationCenter defaultCenter] removeObserver:self
|
||||
name:CPViewFrameDidChangeNotification
|
||||
object:_textView];
|
||||
}
|
||||
}
|
||||
|
||||
- (void)textViewFrameChanged:(CPNotification)aNotification
|
||||
{
|
||||
var newSize = CGSizeMake([_textView frame].size.width, _size.height);
|
||||
|
||||
[self setContainerSize:newSize];
|
||||
}
|
||||
|
||||
- (void)setTextView:(CPTextView)aTextView
|
||||
{
|
||||
if (_textView)
|
||||
[_textView setTextContainer:nil];
|
||||
|
||||
_textView = aTextView;
|
||||
|
||||
if (_textView)
|
||||
[_textView setTextContainer:self];
|
||||
|
||||
[_layoutManager textContainerChangedTextView:self];
|
||||
}
|
||||
|
||||
- (BOOL)containsPoint:(CGPoint)aPoint
|
||||
{
|
||||
return CGRectContainsPoint(CGRectMake(0, 0, _size.width, _size.height), aPoint);
|
||||
}
|
||||
|
||||
- (BOOL)isSimpleRectangularTextContainer
|
||||
{
|
||||
return YES;
|
||||
}
|
||||
|
||||
- (CGRect)lineFragmentRectForProposedRect:(CGRect)proposedRect
|
||||
sweepDirection:(CPLineSweepDirection)sweep
|
||||
movementDirection:(CPLineMovementDirection)movement
|
||||
remainingRect:(CGRectPointer)remainingRect
|
||||
{
|
||||
var resultRect = CGRectCreateCopy(proposedRect);
|
||||
|
||||
if (sweep != CPLineSweepRight || movement != CPLineMovesDown)
|
||||
{
|
||||
CPLog.trace(@"FIXME: unsupported sweep (" + sweep + ") or movement (" + movement + ")");
|
||||
return CGRectMakeZero();
|
||||
}
|
||||
|
||||
if (resultRect.origin.x + resultRect.size.width > _size.width)
|
||||
resultRect.size.width = _size.width - resultRect.origin.x;
|
||||
|
||||
if (resultRect.size.width < 0)
|
||||
resultRect = CGRectMakeZero();
|
||||
|
||||
if (remainingRect)
|
||||
{
|
||||
remainingRect.origin.x = resultRect.origin.x + resultRect.size.width;
|
||||
remainingRect.origin.y = resultRect.origin.y;
|
||||
remainingRect.size.height = resultRect.size.height;
|
||||
remainingRect.size.width = _size.width - (resultRect.origin.x + resultRect.size.width);
|
||||
}
|
||||
|
||||
return resultRect;
|
||||
}
|
||||
|
||||
@end
|
||||
|
||||
|
||||
var CPTextContainerSizeKey = @"CPTextContainerSizeKey",
|
||||
CPTextContainerLayoutManagerKey = @"CPTextContainerLayoutManagerKey";
|
||||
|
||||
@implementation CPTextContainer (CPCoding)
|
||||
|
||||
- (id)initWithCoder:(CPCoder)aCoder
|
||||
{
|
||||
self = [super init];
|
||||
|
||||
if (self)
|
||||
{
|
||||
[self _init];
|
||||
|
||||
_size = [aCoder decodeSizeForKey:CPTextContainerSizeKey];
|
||||
|
||||
_layoutManager = [aCoder decodeObjectForKey:CPTextContainerLayoutManagerKey];
|
||||
[_layoutManager addTextContainer:self];
|
||||
}
|
||||
|
||||
return self;
|
||||
}
|
||||
|
||||
- (void)encodeWithCoder:(CPCoder)aCoder
|
||||
{
|
||||
[aCoder encodeSize:_size forKey:CPTextContainerSizeKey];
|
||||
[aCoder encodeObject:_layoutManager forKey:CPTextContainerLayoutManagerKey];
|
||||
}
|
||||
|
||||
@end
|
||||
@@ -0,0 +1,303 @@
|
||||
/*
|
||||
* CPTextStorage.j
|
||||
* AppKit
|
||||
*
|
||||
* Created by Emmanuel Maillard on 27/02/2010.
|
||||
* Copyright Emmanuel Maillard 2010.
|
||||
*
|
||||
* This library is free software; you can redistribute it and/or
|
||||
* modify it under the terms of the GNU Lesser General Public
|
||||
* License as published by the Free Software Foundation; either
|
||||
* version 2.1 of the License, or (at your option) any later version.
|
||||
*
|
||||
* This library is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
|
||||
* Lesser General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU Lesser General Public
|
||||
* License along with this library; if not, write to the Free Software
|
||||
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
|
||||
*/
|
||||
|
||||
|
||||
@import <Foundation/CPNotificationCenter.j>
|
||||
@import <Foundation/CPAttributedString.j>
|
||||
|
||||
@import "CPText.j"
|
||||
@import "CPFont.j"
|
||||
|
||||
@class CPLayoutManager;
|
||||
|
||||
CPTextStorageEditedAttributes = 1;
|
||||
CPTextStorageEditedCharacters = 2;
|
||||
|
||||
CPTextStorageWillProcessEditingNotification = @"CPTextStorageWillProcessEditingNotification";
|
||||
CPTextStorageDidProcessEditingNotification = @"CPTextStorageDidProcessEditingNotification";
|
||||
|
||||
@protocol CPTextStorageDelegate <CPObject>
|
||||
|
||||
- (void)textStorageWillProcessEditing:(CPNotification)aNotification;
|
||||
- (void)textStorageDidProcessEditing:(CPNotification)aNotification;
|
||||
|
||||
@end
|
||||
|
||||
|
||||
var CPTextStorageDelegate_textStorageWillProcessEditing_ = 1 << 1,
|
||||
CPTextStorageDelegate_textStorageDidProcessEditing_ = 1 << 2;
|
||||
|
||||
/*!
|
||||
@ingroup appkit
|
||||
@class CPTextStorage
|
||||
*/
|
||||
@implementation CPTextStorage : CPMutableAttributedString
|
||||
{
|
||||
CPColor _foregroundColor @accessors(property=foregroundColor);
|
||||
CPFont _font @accessors(property=font);
|
||||
CPMutableArray _layoutManagers @accessors(getter=layoutManagers);
|
||||
CPRange _editedRange @accessors(getter=editedRange);
|
||||
id <CPTextStorageDelegate> _delegate @accessors(property=delegate);
|
||||
int _changeInLength @accessors(property=changeInLength);
|
||||
unsigned _editedMask @accessors(property=editedMask);
|
||||
|
||||
int _editCount; // {begin,end}Editing counter
|
||||
unsigned _implementedDelegateMethods;
|
||||
}
|
||||
|
||||
|
||||
#pragma mark -
|
||||
#pragma mark Init methods
|
||||
|
||||
- (id)initWithString:(CPString)aString attributes:(CPDictionary)attributes
|
||||
{
|
||||
self = [super initWithString:aString attributes:attributes];
|
||||
|
||||
if (self)
|
||||
{
|
||||
_layoutManagers = [[CPMutableArray alloc] init];
|
||||
_editedRange = CPMakeRange(CPNotFound, 0);
|
||||
_changeInLength = 0;
|
||||
_editedMask = 0;
|
||||
}
|
||||
|
||||
return self;
|
||||
}
|
||||
|
||||
- (id)initWithString:(CPString)aString
|
||||
{
|
||||
return [self initWithString:aString attributes:nil];
|
||||
}
|
||||
|
||||
- (id)init
|
||||
{
|
||||
return [self initWithString:@"" attributes:nil];
|
||||
}
|
||||
|
||||
|
||||
#pragma mark -
|
||||
#pragma mark Delegate methods
|
||||
|
||||
- (void)setDelegate:(id <CPTextStorageDelegate>)aDelegate
|
||||
{
|
||||
if (_delegate === aDelegate)
|
||||
return;
|
||||
|
||||
_implementedDelegateMethods = 0;
|
||||
_delegate = aDelegate;
|
||||
|
||||
if (_delegate)
|
||||
{
|
||||
if ([_delegate respondsToSelector:@selector(textStorageWillProcessEditing:)])
|
||||
_implementedDelegateMethods |= CPTextStorageDelegate_textStorageWillProcessEditing_;
|
||||
|
||||
if ([_delegate respondsToSelector:@selector(textStorageDidProcessEditing:)])
|
||||
_implementedDelegateMethods |= CPTextStorageDelegate_textStorageDidProcessEditing_;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
#pragma mark -
|
||||
#pragma mark Layout manager methods
|
||||
|
||||
- (void)addLayoutManager:(CPLayoutManager)aManager
|
||||
{
|
||||
if ([_layoutManagers containsObject:aManager])
|
||||
return
|
||||
|
||||
[aManager setTextStorage:self];
|
||||
[_layoutManagers addObject:aManager];
|
||||
}
|
||||
|
||||
- (void)removeLayoutManager:(CPLayoutManager)aManager
|
||||
{
|
||||
if (![_layoutManagers containsObject:aManager])
|
||||
return
|
||||
|
||||
[aManager setTextStorage:nil];
|
||||
[_layoutManagers removeObject:aManager];
|
||||
}
|
||||
|
||||
- (void)invalidateAttributesInRange:(CPRange)aRange
|
||||
{
|
||||
/* FIXME: stub */
|
||||
}
|
||||
|
||||
|
||||
#pragma mark -
|
||||
#pragma mark Editing methods
|
||||
|
||||
- (void)processEditing
|
||||
{
|
||||
[self _sendDelegateWillProcessEditingNotification];
|
||||
[self invalidateAttributesInRange:[self editedRange]];
|
||||
[self _sendDelegateDidProcessEditingNotification];
|
||||
|
||||
var c = [_layoutManagers count];
|
||||
|
||||
for (var i = 0; i < c; i++)
|
||||
{
|
||||
[[_layoutManagers objectAtIndex:i] textStorage:self
|
||||
edited:_editedMask
|
||||
range:_editedRange
|
||||
changeInLength:_changeInLength
|
||||
invalidatedRange:_editedRange];
|
||||
}
|
||||
|
||||
_editedRange.location = CPNotFound;
|
||||
_editedMask = 0;
|
||||
_changeInLength = 0;
|
||||
}
|
||||
|
||||
- (void)beginEditing
|
||||
{
|
||||
if (_editCount == 0)
|
||||
_editedRange = CPMakeRange(CPNotFound, 0);
|
||||
|
||||
_editCount++;
|
||||
}
|
||||
|
||||
- (void)endEditing
|
||||
{
|
||||
_editCount--;
|
||||
|
||||
if (_editCount == 0)
|
||||
[self processEditing];
|
||||
}
|
||||
|
||||
- (void)edited:(unsigned)editedMask range:(CPRange)aRange changeInLength:(int)lengthChange
|
||||
{
|
||||
var copyRange = CPMakeRangeCopy(aRange);
|
||||
|
||||
if (_editCount == 0) // used outside a beginEditing/endEditing
|
||||
{
|
||||
_editedMask = editedMask;
|
||||
_changeInLength = lengthChange;
|
||||
copyRange.length += lengthChange;
|
||||
_editedRange = copyRange;
|
||||
[self processEditing];
|
||||
}
|
||||
else
|
||||
{
|
||||
_editedMask |= editedMask;
|
||||
_changeInLength += lengthChange;
|
||||
copyRange.length += lengthChange;
|
||||
|
||||
if (_editedRange.location == CPNotFound)
|
||||
_editedRange = copyRange;
|
||||
else
|
||||
_editedRange = CPUnionRange(_editedRange,copyRange);
|
||||
}
|
||||
}
|
||||
|
||||
- (void)removeAttribute:(CPString)anAttribute range:(CPRange)aRange
|
||||
{
|
||||
[self beginEditing];
|
||||
[super removeAttribute:anAttribute range:aRange];
|
||||
[self edited:CPTextStorageEditedAttributes range:aRange changeInLength:0];
|
||||
[self endEditing];
|
||||
}
|
||||
|
||||
- (void)addAttributes:(CPDictionary)aDictionary range:(CPRange)aRange
|
||||
{
|
||||
[self beginEditing];
|
||||
[super addAttributes:aDictionary range:aRange];
|
||||
[self edited:CPTextStorageEditedAttributes range:aRange changeInLength:0];
|
||||
[self endEditing];
|
||||
}
|
||||
|
||||
- (void)deleteCharactersInRange:(CPRange)aRange
|
||||
{
|
||||
[self beginEditing];
|
||||
[super deleteCharactersInRange:aRange];
|
||||
[self edited:CPTextStorageEditedCharacters range:aRange changeInLength:-aRange.length];
|
||||
[self endEditing];
|
||||
}
|
||||
|
||||
- (void)replaceCharactersInRange:(CPRange)aRange withString:(CPString)aString
|
||||
{
|
||||
[self beginEditing];
|
||||
[super replaceCharactersInRange:aRange withString:aString];
|
||||
[self edited:CPTextStorageEditedCharacters range:aRange changeInLength:([aString length] - aRange.length)];
|
||||
[self endEditing];
|
||||
}
|
||||
|
||||
- (void)replaceCharactersInRange:(CPRange)aRange withAttributedString:(CPAttributedString)aString
|
||||
{
|
||||
[self beginEditing];
|
||||
[super replaceCharactersInRange:aRange withAttributedString:aString];
|
||||
[self edited:(CPTextStorageEditedAttributes | CPTextStorageEditedCharacters) range:aRange changeInLength:([aString length] - aRange.length)];
|
||||
[self endEditing];
|
||||
}
|
||||
|
||||
- (CPAttributedString)attributedSubstringFromRange:(CPRange)aRange
|
||||
{
|
||||
if (!aRange.length)
|
||||
return [CPAttributedString new];
|
||||
|
||||
return [super attributedSubstringFromRange:aRange];
|
||||
}
|
||||
|
||||
@end
|
||||
|
||||
|
||||
@implementation CPTextStorage (CPTextStorageDelegate)
|
||||
|
||||
- (void)_sendDelegateWillProcessEditingNotification
|
||||
{
|
||||
if (_implementedDelegateMethods & CPTextStorageDelegate_textStorageWillProcessEditing_)
|
||||
[_delegate textStorageWillProcessEditing:[[CPNotification alloc] initWithName:CPTextStorageWillProcessEditingNotification object:self userInfo:nil]];
|
||||
|
||||
[[CPNotificationCenter defaultCenter] postNotificationName:CPTextStorageWillProcessEditingNotification object:self];
|
||||
}
|
||||
|
||||
- (void)_sendDelegateDidProcessEditingNotification
|
||||
{
|
||||
if (_implementedDelegateMethods & CPTextStorageDelegate_textStorageDidProcessEditing_)
|
||||
[_delegate textStorageWillProcessEditing:[[CPNotification alloc] initWithName:CPTextStorageDidProcessEditingNotification object:self userInfo:nil]];
|
||||
|
||||
[[CPNotificationCenter defaultCenter] postNotificationName:CPTextStorageDidProcessEditingNotification object:self];
|
||||
}
|
||||
|
||||
@end
|
||||
|
||||
|
||||
@implementation CPTextStorage (CPCoding)
|
||||
|
||||
- (id)initWithCoder:(CPCoder)aCoder
|
||||
{
|
||||
self = [super initWithCoder:aCoder];
|
||||
|
||||
if (self)
|
||||
{
|
||||
|
||||
}
|
||||
|
||||
return self;
|
||||
}
|
||||
|
||||
- (void)encodeWithCoder:(CPCoder)aCoder
|
||||
{
|
||||
[super encodeWithCoder:aCoder];
|
||||
}
|
||||
|
||||
@end
|
||||
File diff suppressed because it is too large
Load Diff
@@ -0,0 +1,429 @@
|
||||
/*
|
||||
* CPTypesetter.j
|
||||
* AppKit
|
||||
*
|
||||
* Created by Daniel Boehringer on 27/12/2013.
|
||||
* All modifications copyright Daniel Boehringer 2013.
|
||||
* Extensive code formatting and review by Andrew Hankinson
|
||||
* Based on original work by
|
||||
* Emmanuel Maillard on 27/02/2010.
|
||||
* Copyright Emmanuel Maillard 2010.
|
||||
*
|
||||
* This library is free software; you can redistribute it and/or
|
||||
* modify it under the terms of the GNU Lesser General Public
|
||||
* License as published by the Free Software Foundation; either
|
||||
* version 2.1 of the License, or (at your option) any later version.
|
||||
*
|
||||
* This library is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
|
||||
* Lesser General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU Lesser General Public
|
||||
* License along with this library; if not, write to the Free Software
|
||||
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
|
||||
*/
|
||||
|
||||
@import <Foundation/CPObject.j>
|
||||
|
||||
@import "CPParagraphStyle.j"
|
||||
@import "CPTextStorage.j"
|
||||
@import "CPFont.j"
|
||||
|
||||
// forward declare these classes for type matching
|
||||
@class CPLayoutManager
|
||||
@class CPTextContainer
|
||||
@class CPTextView
|
||||
|
||||
/*
|
||||
CPTypesetterControlCharacterAction
|
||||
*/
|
||||
CPTypesetterZeroAdvancementAction = 1 << 0;
|
||||
CPTypesetterWhitespaceAction = 1 << 1;
|
||||
CPSTypesetterHorizontalTabAction = 1 << 2;
|
||||
CPTypesetterLineBreakAction = 1 << 3;
|
||||
CPTypesetterParagraphBreakAction = 1 << 4;
|
||||
CPTypesetterContainerBreakAction = 1 << 5;
|
||||
|
||||
var CPSystemTypesetterFactory,
|
||||
_sharedSimpleTypesetter;
|
||||
|
||||
@implementation CPTypesetter : CPObject
|
||||
|
||||
|
||||
#pragma mark -
|
||||
#pragma mark Class methods
|
||||
|
||||
+ (void)initialize
|
||||
{
|
||||
[CPTypesetter _setSystemTypesetterFactory:[CPSimpleTypesetter class]];
|
||||
}
|
||||
|
||||
+ (id)sharedSystemTypesetter
|
||||
{
|
||||
return [CPSystemTypesetterFactory sharedInstance];
|
||||
}
|
||||
|
||||
+ (void)_setSystemTypesetterFactory:(Class)aClass
|
||||
{
|
||||
CPSystemTypesetterFactory = aClass;
|
||||
}
|
||||
|
||||
- (CPTypesetterControlCharacterAction)actionForControlCharacterAtIndex:(unsigned)charIndex
|
||||
{
|
||||
return CPTypesetterZeroAdvancementAction;
|
||||
}
|
||||
|
||||
- (CPLayoutManager)layoutManager
|
||||
{
|
||||
return nil;
|
||||
}
|
||||
|
||||
- (CPTextContainer)currentTextContainer
|
||||
{
|
||||
return nil;
|
||||
}
|
||||
|
||||
- (CPArray)textContainers
|
||||
{
|
||||
return nil;
|
||||
}
|
||||
|
||||
- (void)layoutGlyphsInLayoutManager:(CPLayoutManager)layoutManager
|
||||
startingAtGlyphIndex:(unsigned)startGlyphIndex
|
||||
maxNumberOfLineFragments:(unsigned)maxNumLines
|
||||
nextGlyphIndex:(UIntegerReference)nextGlyph
|
||||
{
|
||||
_CPRaiseInvalidAbstractInvocation(self, _cmd);
|
||||
}
|
||||
|
||||
@end
|
||||
|
||||
@implementation CPSimpleTypesetter : CPTypesetter
|
||||
{
|
||||
CPLayoutManager _layoutManager @accessors(property=layoutManager);
|
||||
CPTextContainer _currentTextContainer @accessors(property=currentTextContainer);
|
||||
CPTextStorage _textStorage;
|
||||
|
||||
CPRange _attributesRange;
|
||||
CPDictionary _currentAttributes;
|
||||
CPParagraphStyle _currentParagraph;
|
||||
|
||||
float _lineHeight;
|
||||
float _lineBase;
|
||||
float _lineWidth;
|
||||
|
||||
unsigned _indexOfCurrentContainer;
|
||||
|
||||
CPArray _lineFragments;
|
||||
}
|
||||
|
||||
|
||||
#pragma mark -
|
||||
#pragma mark Class methods
|
||||
|
||||
+ (id)sharedInstance
|
||||
{
|
||||
if (!_sharedSimpleTypesetter)
|
||||
_sharedSimpleTypesetter = [[CPSimpleTypesetter alloc] init];
|
||||
|
||||
return _sharedSimpleTypesetter;
|
||||
}
|
||||
|
||||
- (CPArray)textContainers
|
||||
{
|
||||
return [_layoutManager textContainers];
|
||||
}
|
||||
|
||||
- (CPTextTab)textTabForWidth:(double)aWidth writingDirection:(CPWritingDirection)direction
|
||||
{
|
||||
var tabStops = [_currentParagraph tabStops];
|
||||
|
||||
if (!tabStops)
|
||||
tabStops = [CPParagraphStyle _defaultTabStops];
|
||||
|
||||
var l = tabStops.length;
|
||||
|
||||
if (aWidth > tabStops[l - 1]._location)
|
||||
return nil;
|
||||
|
||||
for (var i = l - 1; i >= 0; i--)
|
||||
{
|
||||
if (aWidth > tabStops[i]._location)
|
||||
{
|
||||
if (i + 1 < l)
|
||||
return tabStops[i + 1];
|
||||
}
|
||||
}
|
||||
|
||||
if (i === -1)
|
||||
return tabStops[0];
|
||||
|
||||
return nil;
|
||||
}
|
||||
|
||||
- (BOOL)_flushRange:(CPRange)lineRange
|
||||
lineOrigin:(CGPoint)lineOrigin
|
||||
currentContainer:(CPTextContainer)aContainer
|
||||
advancements:(CPArray)advancements
|
||||
lineCount:(unsigned)lineCount
|
||||
sameLine:(BOOL)sameLine
|
||||
{
|
||||
var myX = 0,
|
||||
rect = CGRectMake(lineOrigin.x, lineOrigin.y, _lineWidth, _lineHeight),
|
||||
containerSize = aContainer._size;
|
||||
|
||||
[_layoutManager _appendNewLineFragmentInTextContainer:_currentTextContainer forGlyphRange:lineRange];
|
||||
|
||||
var fragment = [_layoutManager._lineFragments lastObject];
|
||||
fragment._isLast = !sameLine;
|
||||
_lineFragments.push(fragment);
|
||||
|
||||
[_layoutManager setLineFragmentRect:rect forGlyphRange:lineRange usedRect:rect];
|
||||
|
||||
switch ([_currentParagraph alignment])
|
||||
{
|
||||
case CPLeftTextAlignment:
|
||||
myX = 0;
|
||||
break;
|
||||
|
||||
case CPCenterTextAlignment:
|
||||
myX = (containerSize.width - _lineWidth) / 2;
|
||||
break;
|
||||
|
||||
case CPRightTextAlignment:
|
||||
myX = containerSize.width - _lineWidth;
|
||||
break;
|
||||
}
|
||||
|
||||
[_layoutManager setLocation:CGPointMake(myX, _lineBase) forStartOfGlyphRange:lineRange];
|
||||
[_layoutManager _setAdvancements:advancements forGlyphRange:lineRange];
|
||||
|
||||
if (!sameLine) //fix the _lineFragments when fontsizes differ
|
||||
{
|
||||
var l = _lineFragments.length;
|
||||
|
||||
for (var i = 0 ; i < l ; i++)
|
||||
[_lineFragments[i] _adjustForHeight:_lineHeight];
|
||||
}
|
||||
|
||||
if (!lineCount) // do not rescue on first line
|
||||
return NO;
|
||||
|
||||
if (aContainer._inResizing)
|
||||
return NO;
|
||||
|
||||
return ([_layoutManager _rescuingInvalidFragmentsWasPossibleForGlyphRange:lineRange]);
|
||||
}
|
||||
|
||||
- (void)layoutGlyphsInLayoutManager:(CPLayoutManager)layoutManager
|
||||
startingAtGlyphIndex:(unsigned)glyphIndex
|
||||
maxNumberOfLineFragments:(unsigned)maxNumLines
|
||||
nextGlyphIndex:(UIntegerReference)nextGlyph
|
||||
{
|
||||
var textContainers = [layoutManager textContainers],
|
||||
textContainersCount = [textContainers count];
|
||||
|
||||
_layoutManager = layoutManager;
|
||||
_textStorage = [_layoutManager textStorage];
|
||||
_indexOfCurrentContainer = MAX(0, [textContainers
|
||||
indexOfObject:[_layoutManager textContainerForGlyphAtIndex:glyphIndex effectiveRange:nil withoutAdditionalLayout:YES]
|
||||
inRange:CPMakeRange(0, textContainersCount)]);
|
||||
|
||||
_currentTextContainer = textContainers[_indexOfCurrentContainer];
|
||||
|
||||
_attributesRange = CPMakeRange(0, 0);
|
||||
_lineHeight = 0;
|
||||
_lineBase = 0;
|
||||
_lineWidth = 0;
|
||||
|
||||
var containerSize = [_currentTextContainer containerSize],
|
||||
containerSizeWidth = containerSize.width,
|
||||
containerSizeHeight = containerSize.height,
|
||||
lineRange = CPMakeRange(glyphIndex, 0),
|
||||
wrapRange = CPMakeRange(0, 0),
|
||||
wrapWidth = 0,
|
||||
isNewline = NO,
|
||||
isTabStop = NO,
|
||||
isWordWrapped = NO,
|
||||
numberOfGlyphs= [_textStorage length],
|
||||
leading,
|
||||
numLines = 0,
|
||||
theString = [_textStorage string],
|
||||
lineOrigin,
|
||||
ascent,
|
||||
descent,
|
||||
advancements = [],
|
||||
prevRangeWidth = 0,
|
||||
measuringRange = CPMakeRange(glyphIndex, 0),
|
||||
currentAnchor = 0,
|
||||
currentFont,
|
||||
currentFontLineHeight,
|
||||
previousFont,
|
||||
currentParagraphMinimumLineHeight,
|
||||
currentParagraphMaximumLineHeight,
|
||||
currentParagraphLineSpacing;
|
||||
|
||||
if (glyphIndex > 0)
|
||||
lineOrigin = CGPointCreateCopy([_layoutManager lineFragmentRectForGlyphAtIndex:glyphIndex effectiveRange:nil].origin);
|
||||
else if ([_layoutManager extraLineFragmentTextContainer])
|
||||
lineOrigin = CGPointMake(0, [_layoutManager extraLineFragmentUsedRect].origin.y);
|
||||
else
|
||||
lineOrigin = CGPointMake(0, 0);
|
||||
|
||||
[_layoutManager _removeInvalidLineFragments];
|
||||
|
||||
if (![_textStorage length])
|
||||
return;
|
||||
|
||||
_lineFragments = [];
|
||||
|
||||
for (; numLines != maxNumLines && glyphIndex < numberOfGlyphs; glyphIndex++)
|
||||
{
|
||||
// check whether there any change in the attributes from here on
|
||||
if (!CPLocationInRange(glyphIndex, _attributesRange))
|
||||
{
|
||||
_currentAttributes = [_textStorage attributesAtIndex:glyphIndex effectiveRange:_attributesRange];
|
||||
currentFont = [_currentAttributes objectForKey:CPFontAttributeName];
|
||||
_currentParagraph = [_currentAttributes objectForKey:CPParagraphStyleAttributeName] || [CPParagraphStyle defaultParagraphStyle];
|
||||
currentParagraphMinimumLineHeight = [_currentParagraph minimumLineHeight];
|
||||
currentParagraphMaximumLineHeight = [_currentParagraph maximumLineHeight];
|
||||
currentParagraphLineSpacing = [_currentParagraph lineSpacing];
|
||||
|
||||
if (!currentFont)
|
||||
currentFont = [_textStorage font] || [CPFont systemFontOfSize:12.0];
|
||||
|
||||
ascent = [currentFont ascender];
|
||||
descent = [currentFont descender];
|
||||
leading = (ascent - descent) * 0.2; // FAKE leading
|
||||
|
||||
currentFontLineHeight = ascent - descent + leading;
|
||||
|
||||
if (previousFont !== currentFont)
|
||||
{
|
||||
measuringRange = CPMakeRange(glyphIndex, 0);
|
||||
currentAnchor = prevRangeWidth;
|
||||
previousFont = currentFont;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
if (currentFontLineHeight > _lineHeight)
|
||||
_lineHeight = currentFontLineHeight;
|
||||
|
||||
if (ascent > _lineBase)
|
||||
_lineBase = ascent;
|
||||
|
||||
lineRange.length++;
|
||||
measuringRange.length++;
|
||||
|
||||
var currentCharCode = theString.charCodeAt(glyphIndex), // use pure javascript methods for performance reasons
|
||||
rangeWidth = [theString.substr(measuringRange.location, measuringRange.length) _sizeWithFont:currentFont inWidth:NULL].width + currentAnchor;
|
||||
|
||||
switch (currentCharCode) // faster than sending actionForControlCharacterAtIndex: called for each char.
|
||||
{
|
||||
case 9: // '\t'
|
||||
{
|
||||
var nextTab = [self textTabForWidth:rangeWidth + lineOrigin.x writingDirection:0];
|
||||
|
||||
isTabStop = YES;
|
||||
|
||||
if (nextTab)
|
||||
rangeWidth = nextTab._location - lineOrigin.x;
|
||||
else
|
||||
rangeWidth += 28; //FIXME
|
||||
} // fallthrough intentional
|
||||
case 32: // ' '
|
||||
wrapRange = CPMakeRangeCopy(lineRange);
|
||||
wrapWidth = rangeWidth;
|
||||
wrapRange._height = _lineHeight;
|
||||
wrapRange._base = _lineBase;
|
||||
break;
|
||||
|
||||
case 10:
|
||||
case 13:
|
||||
isNewline = YES;
|
||||
}
|
||||
|
||||
advancements.push({width: rangeWidth - prevRangeWidth, height: ascent, descent: descent});
|
||||
|
||||
prevRangeWidth = _lineWidth = rangeWidth;
|
||||
|
||||
if (lineOrigin.x + rangeWidth > containerSizeWidth)
|
||||
{
|
||||
if (wrapWidth)
|
||||
{
|
||||
lineRange = wrapRange;
|
||||
_lineWidth = wrapWidth;
|
||||
_lineHeight = wrapRange._height;
|
||||
_lineBase = wrapRange._base;
|
||||
}
|
||||
|
||||
isNewline = YES;
|
||||
isWordWrapped = YES;
|
||||
glyphIndex = CPMaxRange(lineRange) - 1; // start the line starts directly at current character
|
||||
}
|
||||
|
||||
if (isNewline || isTabStop)
|
||||
{
|
||||
if ([self _flushRange:lineRange lineOrigin:lineOrigin currentContainer:_currentTextContainer advancements:advancements lineCount:numLines sameLine:!isNewline])
|
||||
return;
|
||||
|
||||
if (isTabStop)
|
||||
{
|
||||
lineOrigin.x += rangeWidth;
|
||||
isTabStop = NO;
|
||||
}
|
||||
|
||||
if (isNewline)
|
||||
{
|
||||
if (currentParagraphMinimumLineHeight && currentParagraphMinimumLineHeight > _lineHeight)
|
||||
_lineHeight = currentParagraphMinimumLineHeight;
|
||||
|
||||
if (currentParagraphMaximumLineHeight && currentParagraphMaximumLineHeight < _lineHeight)
|
||||
_lineHeight = currentParagraphMaximumLineHeight;
|
||||
|
||||
lineOrigin.y += _lineHeight;
|
||||
|
||||
if (currentParagraphLineSpacing)
|
||||
lineOrigin.y += currentParagraphLineSpacing;
|
||||
|
||||
if (lineOrigin.y > containerSizeHeight && _indexOfCurrentContainer < textContainersCount - 1)
|
||||
{
|
||||
_currentTextContainer = textContainers[++_indexOfCurrentContainer];
|
||||
containerSize = [_currentTextContainer containerSize];
|
||||
containerSizeWidth = containerSize.width;
|
||||
containerSizeHeight = containerSize.height;
|
||||
}
|
||||
|
||||
lineOrigin.x = 0;
|
||||
numLines++;
|
||||
isNewline = NO;
|
||||
_lineFragments = [];
|
||||
_lineHeight = 0;
|
||||
_lineBase = ascent;
|
||||
}
|
||||
|
||||
_lineWidth = 0;
|
||||
advancements = [];
|
||||
currentAnchor = 0;
|
||||
prevRangeWidth = 0;
|
||||
lineRange = CPMakeRange(glyphIndex + 1, 0);
|
||||
measuringRange = CPMakeRange(glyphIndex + 1, 0);
|
||||
wrapRange = CPMakeRange(0, 0);
|
||||
wrapWidth = 0;
|
||||
isWordWrapped = NO;
|
||||
}
|
||||
}
|
||||
|
||||
// this is to "flush" the remaining characters
|
||||
if (lineRange.length)
|
||||
{
|
||||
[self _flushRange:lineRange lineOrigin:lineOrigin currentContainer:_currentTextContainer advancements:advancements lineCount:numLines sameLine:NO];
|
||||
}
|
||||
|
||||
var rect = CGRectMake(0, lineOrigin.y, containerSizeWidth, [_layoutManager._lineFragments lastObject]._usedRect.size.height - descent);
|
||||
[_layoutManager setExtraLineFragmentRect:rect usedRect:rect textContainer:_currentTextContainer];
|
||||
}
|
||||
|
||||
@end
|
||||
@@ -0,0 +1,793 @@
|
||||
/* RTFParser.j
|
||||
|
||||
Parse a RTF string into a CPAttributedString
|
||||
|
||||
Copyright (C) 2014 Daniel Boehringer
|
||||
|
||||
FIXME: this class should be redone using a 'real' parser
|
||||
|
||||
* all paragraph spacing information is currently not parsed
|
||||
|
||||
|
||||
* This library is free software; you can redistribute it and/or
|
||||
* modify it under the terms of the GNU Lesser General Public
|
||||
* License as published by the Free Software Foundation; either
|
||||
* version 2.1 of the License, or (at your option) any later version.
|
||||
*
|
||||
* This library is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
|
||||
* Lesser General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU Lesser General Public
|
||||
* License along with this library; if not, write to the Free Software
|
||||
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
|
||||
*/
|
||||
|
||||
@import <Foundation/CPAttributedString.j>
|
||||
@import <Foundation/CPGeometry.j>
|
||||
@import "CPFontManager.j"
|
||||
@import "CPParagraphStyle.j"
|
||||
|
||||
@global CPLeftTextAlignment
|
||||
@global CPRightTextAlignment
|
||||
@global CPCenterTextAlignment
|
||||
@global CPJustifiedTextAlignment
|
||||
@global CPNaturalTextAlignment
|
||||
|
||||
@global CPFontAttributeName
|
||||
@global CPForegroundColorAttributeName
|
||||
|
||||
var hexTable = [];
|
||||
|
||||
// Hold the attributes of the current run
|
||||
@implementation _RTFAttribute : CPObject
|
||||
{
|
||||
CPRange _range;
|
||||
CPParagraphStyle paragraph;
|
||||
CPColor fgColour;
|
||||
CPColor bgColour;
|
||||
CPColor ulColour;
|
||||
CPString fontName;
|
||||
unsigned fontSize;
|
||||
BOOL bold;
|
||||
BOOL italic;
|
||||
BOOL underline;
|
||||
BOOL strikethrough;
|
||||
BOOL script;
|
||||
BOOL _tabChanged;
|
||||
}
|
||||
|
||||
- (id)init
|
||||
{
|
||||
if (self = [super init])
|
||||
{
|
||||
[self resetFont];
|
||||
[self resetParagraphStyle];
|
||||
_range = CPMakeRange(0, 0);
|
||||
}
|
||||
|
||||
return self;
|
||||
}
|
||||
|
||||
- (id)copy
|
||||
{
|
||||
var mynew = [_RTFAttribute new];
|
||||
|
||||
mynew.paragraph = [paragraph copy];
|
||||
mynew.fontName = fontName;
|
||||
mynew.fgColour = fgColour;
|
||||
mynew.bgColour = bgColour;
|
||||
mynew.ulColour = ulColour;
|
||||
|
||||
return mynew;
|
||||
}
|
||||
|
||||
- (CPFont)currentFont
|
||||
{
|
||||
var font = [CPFont _fontWithName:fontName size:fontSize bold:bold italic:italic];
|
||||
|
||||
if (font)
|
||||
return font;
|
||||
|
||||
//Before giving up and using a default font, we try if this is
|
||||
//not the case of a font with a composite name, such as
|
||||
//'Helvetica-Light'. In that case, even if we don't have
|
||||
//exactly an 'Helvetica-Light' font family, we might have an
|
||||
//'Helvetica' one.
|
||||
var range = [fontName rangeOfString:@"-"];
|
||||
|
||||
if (range.location != CPNotFound)
|
||||
{
|
||||
var fontFamily = [fontName substringToIndex: range.location];
|
||||
|
||||
font = [CPFont fontWithName:fontFamily size:fontSize];
|
||||
}
|
||||
|
||||
/* Last resort, default font. :-( */
|
||||
if (font == nil)
|
||||
font = [CPFont systemFontOfSize:fontSize];
|
||||
|
||||
return font;
|
||||
}
|
||||
|
||||
- (CPNumber)script
|
||||
{
|
||||
return [CPNumber numberWithInt:script];
|
||||
}
|
||||
|
||||
- (CPNumber)underline
|
||||
{
|
||||
if (underline != 0)
|
||||
return [CPNumber numberWithInt:underline];
|
||||
else
|
||||
return nil;
|
||||
}
|
||||
|
||||
- (CPNumber)strikethrough
|
||||
{
|
||||
if (strikethrough != 0)
|
||||
return [CPNumber numberWithInt:strikethrough];
|
||||
else
|
||||
return nil;
|
||||
}
|
||||
|
||||
- (void)resetParagraphStyle
|
||||
{
|
||||
paragraph = [[CPParagraphStyle defaultParagraphStyle] copy];
|
||||
}
|
||||
|
||||
- (void)resetFont
|
||||
{
|
||||
var font = [CPFont systemFontOfSize:12];
|
||||
|
||||
fontName = [font familyName];
|
||||
fontSize = 12.0;
|
||||
italic = NO;
|
||||
bold = NO;
|
||||
underline = 0;
|
||||
strikethrough = 0;
|
||||
script = 0;
|
||||
}
|
||||
|
||||
- (void)addTab:(float)location type:(CPTextTabType)type
|
||||
{
|
||||
var tab = [[CPTextTab alloc] initWithType:CPLeftTabStopType
|
||||
location:location];
|
||||
|
||||
if (!_tabChanged)
|
||||
{
|
||||
[paragraph setTabStops:[tab]];
|
||||
_tabChanged = YES;
|
||||
}
|
||||
else
|
||||
{
|
||||
[paragraph addTabStop: tab];
|
||||
}
|
||||
}
|
||||
|
||||
- (CPDictionary)dictionary
|
||||
{
|
||||
var ret = @{};
|
||||
[ret setObject:[self currentFont] forKey:CPFontAttributeName];
|
||||
[ret setObject:paragraph forKey:CPParagraphStyleAttributeName];
|
||||
|
||||
if (fgColour)
|
||||
[ret setObject:fgColour forKey:CPForegroundColorAttributeName];
|
||||
|
||||
return ret;
|
||||
}
|
||||
@end
|
||||
|
||||
|
||||
// based on https://github.com/lazygyu/RTF-parser
|
||||
|
||||
var kRTFParserType_char = 0,
|
||||
kRTFParserType_dest = 1,
|
||||
kRTFParserType_prop = 2,
|
||||
kRTFParserType_spec = 3;
|
||||
|
||||
// Keyword descriptions
|
||||
var kRgsymRtf = {
|
||||
// keyword dflt fPassDflt kwd idx
|
||||
"b" : [ "b", 1, false, kRTFParserType_prop, "propBold"],
|
||||
"ul" : [ "ul", 1, false, kRTFParserType_prop, "propUnderline"],
|
||||
"i" : [ "i", 1, false, kRTFParserType_prop, "propItalic"],
|
||||
"li" : [ "li", 0, false, kRTFParserType_prop, "propPgnFormat"],
|
||||
"pgnucltr" : [ "pgnucltr", "pgULtr", true, kRTFParserType_prop, "propPgnFormat"],
|
||||
"pgnlcltr" : [ "pgnlcltr", "pgLLtr", true, kRTFParserType_prop, "propPgnFormat"],
|
||||
"qc" : [ "qc", "justC", true, kRTFParserType_prop, "propJust"],
|
||||
"ql" : [ "ql", "justL", true, kRTFParserType_prop, "propJust"],
|
||||
"qr" : [ "qr", "justR", true, kRTFParserType_prop, "propJust"],
|
||||
"qj" : [ "qj", "justF", true, kRTFParserType_prop, "propJust"],
|
||||
"paperw" : [ "paperw", 12240, false, kRTFParserType_prop, "propXaPage"],
|
||||
"paperh" : [ "paperh", 15480, false, kRTFParserType_prop, "propYaPage"],
|
||||
"margl" : [ "margl", 1800, false, kRTFParserType_prop, "propXaLeft"],
|
||||
"margr" : [ "margr", 1800, false, kRTFParserType_prop, "propXaRight"],
|
||||
"margt" : [ "margt", 1440, false, kRTFParserType_prop, "propYaTop"],
|
||||
"margb" : [ "margb", 1440, false, kRTFParserType_prop, "propYaBottom"],
|
||||
"pgnstart" : [ "pgnstart", 1, true, kRTFParserType_prop, "propPgnStart"],
|
||||
"facingp" : [ "facingp", 1, true, kRTFParserType_prop, "propFacingp"],
|
||||
"landscape" : [ "landscape",1, true, kRTFParserType_prop, "propLandscape"],
|
||||
"par" : [ "par", 0, false, kRTFParserType_char, "\n"],
|
||||
"pard" : [ "pard", 0, false, kRTFParserType_prop, "propDefaultPara"],
|
||||
"\0x0a" : [ "\0x0a", 0, false, kRTFParserType_char, "\n"],
|
||||
"\0x0d" : [ "\0x0d", 0, false, kRTFParserType_char, ""],
|
||||
"tab" : [ "tab", 0, false, kRTFParserType_char, "\t"],
|
||||
"ldblquote" : [ "ldblquote",0, false, kRTFParserType_char, '"'],
|
||||
"rdblquote" : [ "rdblquote",0, false, kRTFParserType_char, '"'],
|
||||
"bin" : [ "bin", 0, false, kRTFParserType_spec, "ipfnBin"],
|
||||
"*" : [ "*", 0, false, kRTFParserType_spec, "ipfnDestSkip"],
|
||||
"'" : [ "'", 0, false, kRTFParserType_spec, "ipfnHex"],
|
||||
"author" : [ "author", 0, false, kRTFParserType_dest, "destSkip"],
|
||||
"buptim" : [ "buptim", 0, false, kRTFParserType_dest, "destSkip"],
|
||||
"colortbl" : [ "colortbl", 0, false, kRTFParserType_dest, "destSkip"],
|
||||
"comment" : [ "comment", 0, false, kRTFParserType_dest, "destSkip"],
|
||||
"creatim" : [ "creatim", 0, false, kRTFParserType_dest, "destSkip"],
|
||||
"doccomm" : [ "doccomm", 0, false, kRTFParserType_dest, "destSkip"],
|
||||
"fonttbl" : [ "fonttbl", 0, false, kRTFParserType_dest, "destSkip"],
|
||||
"footer" : [ "footer", 0, false, kRTFParserType_dest, "destSkip"],
|
||||
"footerf" : [ "footerf", 0, false, kRTFParserType_dest, "destSkip"],
|
||||
"footerl" : [ "footerl", 0, false, kRTFParserType_dest, "destSkip"],
|
||||
"footerr" : [ "footerr", 0, false, kRTFParserType_dest, "destSkip"],
|
||||
"footnote" : [ "footnote", 0, false, kRTFParserType_dest, "destSkip"],
|
||||
"ftncn" : [ "ftncn", 0, false, kRTFParserType_dest, "destSkip"],
|
||||
"ftnsep" : [ "ftnsep", 0, false, kRTFParserType_dest, "destSkip"],
|
||||
"ftnsepc" : [ "ftnsepc", 0, false, kRTFParserType_dest, "destSkip"],
|
||||
"fprq" : [ "fprq", 0, false, kRTFParserType_dest, "destSkip"],
|
||||
// "fcharset" : [ "fcharset", 0, false, kRTFParserType_dest, "destSkip"],
|
||||
"rquote" : [ "rquote", 0, false, kRTFParserType_char, "'"],
|
||||
// "s" : [ "s", 0, false, kRTFParserType_dest, "destSkip"],
|
||||
"header" : [ "header", 0, false, kRTFParserType_dest, "destSkip"],
|
||||
"headerf" : [ "headerf", 0, false, kRTFParserType_dest, "destSkip"],
|
||||
"headerl" : [ "headerl", 0, false, kRTFParserType_dest, "destSkip"],
|
||||
"headerr" : [ "headerr", 0, false, kRTFParserType_dest, "destSkip"],
|
||||
"info" : [ "info", 0, false, kRTFParserType_dest, "destSkip"],
|
||||
"keywords" : [ "keywords", 0, false, kRTFParserType_dest, "destSkip"],
|
||||
"operator" : [ "operator", 0, false, kRTFParserType_dest, "destSkip"],
|
||||
"pict" : [ "pict", 0, false, kRTFParserType_dest, "destSkip"],
|
||||
"printim" : [ "printim", 0, false, kRTFParserType_dest, "destSkip"],
|
||||
"private1" : [ "private1", 0, false, kRTFParserType_dest, "destSkip"],
|
||||
"revtim" : [ "revtim", 0, false, kRTFParserType_dest, "destSkip"],
|
||||
"rxe" : [ "rxe", 0, false, kRTFParserType_dest, "destSkip"],
|
||||
"stylesheet" : [ "stylesheet",0, false, kRTFParserType_dest, "destSkip"],
|
||||
"subject" : [ "subject", 0, false, kRTFParserType_dest, "destSkip"],
|
||||
"tc" : [ "tc", 0, false, kRTFParserType_dest, "destSkip"],
|
||||
"title" : [ "title", 0, false, kRTFParserType_dest, "destSkip"],
|
||||
"txe" : [ "txe", 0, false, kRTFParserType_dest, "destSkip"],
|
||||
"xe" : [ "xe", 0, false, kRTFParserType_dest, "destSkip"],
|
||||
"[" : [ "[", 0, false, kRTFParserType_char, '['],
|
||||
" " : [ " ", 0, false, kRTFParserType_char, ' '],
|
||||
"]" : [ "]", 0, false, kRTFParserType_char, ']'],
|
||||
"{" : [ "{", 0, false, kRTFParserType_char, '{'],
|
||||
"}" : [ "}", 0, false, kRTFParserType_char, '}'],
|
||||
"\\" : [ "\\", 0, false, kRTFParserType_char, '\\']
|
||||
};
|
||||
|
||||
@implementation _CPRTFParser : CPObject
|
||||
{
|
||||
CPString _codePage;
|
||||
CGSize _paper;
|
||||
CPString _rtf;
|
||||
unsigned _curState;
|
||||
CPArray _states;
|
||||
unsigned _currentParseIndex;
|
||||
BOOL _hexreturn;
|
||||
_RTFAttribute _currentRun;
|
||||
CPAttributedString _result;
|
||||
CPArray _colorArray;
|
||||
CPArray _fontArray;
|
||||
CPString _freename;
|
||||
BOOL _parsingFontTable;
|
||||
}
|
||||
|
||||
- (id)init
|
||||
{
|
||||
if (self = [super init])
|
||||
{
|
||||
_paper = CPMakeSize(0, 0);
|
||||
_rtf = "";
|
||||
_curState = 0; // 0 = normal, 1 = skip
|
||||
_states = [];
|
||||
_currentParseIndex = 0;
|
||||
_hexreturn = NO;
|
||||
_result = [CPAttributedString new];
|
||||
_colorArray = [];
|
||||
_fontArray = ['Arial']; // FIXME: should be name of system font
|
||||
_freename = "";
|
||||
_parsingFontTable = NO;
|
||||
}
|
||||
|
||||
return self;
|
||||
}
|
||||
|
||||
- (CPString)_checkChar:(CPArray)sym parameter:(CPString)ch
|
||||
{
|
||||
switch (_curState)
|
||||
{
|
||||
case 0:
|
||||
if (sym && sym[4])
|
||||
return sym[4];
|
||||
|
||||
case 1:
|
||||
// CPLogConsole("skipped : " + sym[4]);
|
||||
return '';
|
||||
|
||||
default:
|
||||
if (sym && sym[4])
|
||||
return sym[4];
|
||||
}
|
||||
}
|
||||
|
||||
- (BOOL)pushState
|
||||
{
|
||||
_states.push["group"];
|
||||
return YES;
|
||||
}
|
||||
|
||||
- (BOOL)popState
|
||||
{
|
||||
_states.pop();
|
||||
|
||||
if (_curState > 0)
|
||||
_curState--;
|
||||
|
||||
return YES;
|
||||
}
|
||||
|
||||
- (CPString)_parseSpec:(CPArray)sym parameter:(CPString)v
|
||||
{
|
||||
var ch = '';
|
||||
|
||||
switch (sym[4])
|
||||
{
|
||||
case "ipfnDestSkip":
|
||||
_curState++;
|
||||
return '';
|
||||
|
||||
case "ipfnHex":
|
||||
ch = _rtf.charAt(++_currentParseIndex);
|
||||
|
||||
var hex = '';
|
||||
|
||||
while (new RegExp("[a-fA-F0-9\\']").test(ch))
|
||||
{
|
||||
if (ch == "'")
|
||||
{
|
||||
_currentParseIndex++;
|
||||
continue;
|
||||
}
|
||||
|
||||
hex += (ch + '');
|
||||
ch = _rtf.charAt(++_currentParseIndex);
|
||||
}
|
||||
//ch = parseInt(ch, 16);
|
||||
//console.log("hex : " + hex);
|
||||
_hexreturn = YES;
|
||||
_currentParseIndex--;
|
||||
|
||||
if (_curState !== 0)
|
||||
return '';
|
||||
else
|
||||
return hex;
|
||||
break;
|
||||
|
||||
case "codePage":
|
||||
ch = _rtf.charAt(++_currentParseIndex);
|
||||
|
||||
var code = '';
|
||||
|
||||
while (new RegExp("[0-9]").test(ch))
|
||||
{
|
||||
code += (ch + '');
|
||||
ch = _rtf.charAt(++_currentParseIndex);
|
||||
}
|
||||
|
||||
_codePage = code;
|
||||
_currentParseIndex--;
|
||||
break;
|
||||
}
|
||||
|
||||
return '';
|
||||
}
|
||||
|
||||
- (void)_flushCurrentRun
|
||||
{
|
||||
var newOffset = 0;
|
||||
|
||||
if (_currentRun)
|
||||
{
|
||||
if ([_result length] == _currentRun._range.location)
|
||||
return;
|
||||
|
||||
_currentRun._range.length = [_result length] - _currentRun._range.location;
|
||||
newOffset = CPMaxRange(_currentRun._range);
|
||||
|
||||
var dict = [_currentRun dictionary];
|
||||
|
||||
[_result setAttributes:dict range:_currentRun._range]; // flush previous run
|
||||
_currentRun.fgColour = [CPColor blackColor];
|
||||
}
|
||||
else
|
||||
_currentRun = [_RTFAttribute new];
|
||||
|
||||
_currentRun._range = CPMakeRange(newOffset, 0); // open a new one
|
||||
}
|
||||
|
||||
- (CPString)_applyPropChange:sym parameter:param
|
||||
{
|
||||
//console.log("prop : " + sym[0] + " / param : " + param+ ' ');
|
||||
|
||||
switch (sym[0])
|
||||
{
|
||||
case "pard":
|
||||
[self _flushCurrentRun];
|
||||
break;
|
||||
|
||||
case "b": // bold
|
||||
if (param === 0)
|
||||
{
|
||||
if (_currentRun && _currentRun.bold)
|
||||
[self _flushCurrentRun];
|
||||
|
||||
_currentRun.bold = NO
|
||||
}
|
||||
else
|
||||
{
|
||||
if (_currentRun && !_currentRun.bold)
|
||||
[self _flushCurrentRun];
|
||||
|
||||
_currentRun.bold = YES;
|
||||
}
|
||||
|
||||
break;
|
||||
|
||||
case "i": // italic
|
||||
if (param === 0)
|
||||
{
|
||||
if (_currentRun && _currentRun.italic)
|
||||
[self _flushCurrentRun];
|
||||
|
||||
_currentRun.italic = NO
|
||||
}
|
||||
else
|
||||
{
|
||||
if (_currentRun && !_currentRun.italic)
|
||||
[self _flushCurrentRun];
|
||||
|
||||
_currentRun.italic = YES;
|
||||
}
|
||||
|
||||
break;
|
||||
|
||||
case "qc": // paragraph center
|
||||
[_currentRun.paragraph setAlignment:CPCenterTextAlignment];
|
||||
break;
|
||||
|
||||
case "paperw":
|
||||
_paper.width = param;
|
||||
break;
|
||||
|
||||
case "paperh":
|
||||
_paper.height = param;
|
||||
break;
|
||||
}
|
||||
|
||||
return '';
|
||||
}
|
||||
|
||||
|
||||
- (CPString)_changeDest:(CPArray)sym
|
||||
{
|
||||
switch (sym[0])
|
||||
{
|
||||
case "colortbl":
|
||||
_colorArray.push([CPColor blackColor]);
|
||||
break;
|
||||
|
||||
case "fonttbl":
|
||||
_parsingFontTable = YES;
|
||||
break;
|
||||
}
|
||||
|
||||
if (sym[4] == "destSkip")
|
||||
{
|
||||
CPLogConsole("Dest skip start : [" + sym[0] + "]");
|
||||
_curState++;
|
||||
}
|
||||
|
||||
return '';
|
||||
}
|
||||
|
||||
- (CPString)_translateKeyword:(CPString)keyword parameter:(CPString)param fParameter:(BOOL)fParam
|
||||
{
|
||||
if (kRgsymRtf[keyword] !== undefined)
|
||||
{
|
||||
var sym = kRgsymRtf[keyword];
|
||||
|
||||
switch (sym[3])
|
||||
{
|
||||
case kRTFParserType_prop:
|
||||
if (sym[2] || !fParam)
|
||||
param = sym[1];
|
||||
|
||||
return [self _applyPropChange:sym parameter:param];
|
||||
|
||||
case kRTFParserType_char:
|
||||
if((param + '') !== 'NaN' && (param + '') !== 'null' && (param + '').length)
|
||||
_currentParseIndex -= (param + '').length;
|
||||
|
||||
return [self _checkChar:sym parameter:param];
|
||||
|
||||
case kRTFParserType_dest:
|
||||
return [self _changeDest:sym];
|
||||
|
||||
case kRTFParserType_spec:
|
||||
return [self _parseSpec:sym parameter:param];
|
||||
|
||||
default:
|
||||
return '';
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
switch (keyword)
|
||||
{
|
||||
case "red":
|
||||
var oldColor = [_colorArray lastObject],
|
||||
green = [oldColor greenComponent],
|
||||
blue = [oldColor blueComponent];
|
||||
|
||||
_colorArray.pop();
|
||||
_colorArray.push([CPColor colorWithRed:parseInt(param) / 255 green:green blue:blue alpha:1.0]);
|
||||
break;
|
||||
|
||||
case "green":
|
||||
var oldColor = [_colorArray lastObject],
|
||||
red = [oldColor redComponent],
|
||||
blue = [oldColor blueComponent];
|
||||
|
||||
_colorArray.pop();
|
||||
_colorArray.push([CPColor colorWithRed:red green: parseInt(param) / 255 blue:blue alpha:1.0]);
|
||||
break;
|
||||
|
||||
case "blue":
|
||||
var oldColor = [_colorArray lastObject],
|
||||
green = [oldColor greenComponent],
|
||||
red = [oldColor redComponent];
|
||||
|
||||
_colorArray.pop();
|
||||
_colorArray.push([CPColor colorWithRed:red green:green blue:parseInt(param) / 255 alpha:1.0]);
|
||||
_colorArray.push([CPColor blackColor]); // placeholder for next color
|
||||
break;
|
||||
|
||||
case "cf": // change foreground color
|
||||
[self _flushCurrentRun];
|
||||
var fontIndex = parseInt(param) - 1;
|
||||
|
||||
if (_currentRun && fontIndex >= 0)
|
||||
_currentRun.fgColour = _colorArray[fontIndex];
|
||||
|
||||
break;
|
||||
|
||||
case "f": // change font
|
||||
[self _flushCurrentRun];
|
||||
var fontIndex = parseInt(param);
|
||||
|
||||
if (_currentRun && fontIndex >= 0 && fontIndex < _fontArray.length)
|
||||
_currentRun.fontName = _fontArray[fontIndex];
|
||||
break;
|
||||
|
||||
case "fs": // change font size
|
||||
[self _flushCurrentRun];
|
||||
_currentRun.fontSize = parseInt(param) / 2;
|
||||
break;
|
||||
|
||||
case "tx": // tabstop
|
||||
var location = parseInt(param) / 20;
|
||||
|
||||
if (_currentRun)
|
||||
[_currentRun addTab:location type:CPLeftTabStopType];
|
||||
|
||||
break;
|
||||
|
||||
default:
|
||||
CPLogConsole("skip : " + keyword + " param: " + param);
|
||||
|
||||
}
|
||||
|
||||
if (_states.length > 0)
|
||||
_curState = 1;
|
||||
|
||||
return '';
|
||||
}
|
||||
}
|
||||
|
||||
- (CPString)_parseKeyword:(CPString)rtf length:(unsigned)len
|
||||
{
|
||||
var ch = '',
|
||||
fParam = false,
|
||||
fNeg = false,
|
||||
keyword = '',
|
||||
param = '';
|
||||
|
||||
_rtf = rtf;
|
||||
|
||||
if (++_currentParseIndex >= len)
|
||||
return len;
|
||||
|
||||
ch = rtf.charAt(_currentParseIndex);
|
||||
|
||||
if (!/[a-zA-Z]/.test(ch))
|
||||
return [self _translateKeyword:ch parameter:nil fParameter:fParam];
|
||||
|
||||
while (new RegExp("[a-zA-Z]").test(ch))
|
||||
{
|
||||
keyword += ch;
|
||||
ch = rtf.charAt(++_currentParseIndex);
|
||||
}
|
||||
|
||||
if (ch == '-')
|
||||
{
|
||||
fNeg = true;
|
||||
ch = rtf.charAt(++_currentParseIndex);
|
||||
}
|
||||
|
||||
fParam = true;
|
||||
|
||||
while (new RegExp("[0-9]").test(ch))
|
||||
{
|
||||
param += (ch + '');
|
||||
ch = rtf.charAt(++_currentParseIndex);
|
||||
}
|
||||
|
||||
_currentParseIndex--;
|
||||
param = parseInt(param);
|
||||
|
||||
if (fNeg)
|
||||
param *= -1;
|
||||
|
||||
return [self _translateKeyword:keyword parameter:param fParameter:fParam];
|
||||
}
|
||||
|
||||
- (void)_appendPlainString:(CPString) aString
|
||||
{
|
||||
[_result replaceCharactersInRange:CPMakeRange([_result length], 0) withString:aString];
|
||||
|
||||
}
|
||||
- (CPAttributedString)parseRTF:(CPString)rtf
|
||||
{
|
||||
rtf = rtf.replace(/\\\n/g, "\\par\n");
|
||||
|
||||
if (rtf.length == 0)
|
||||
return '';
|
||||
|
||||
_currentParseIndex = -1;
|
||||
|
||||
var len = rtf.length,
|
||||
tmp = '',
|
||||
ch = '',
|
||||
hex = '',
|
||||
lastchar = 0;
|
||||
|
||||
while (_currentParseIndex < len)
|
||||
{
|
||||
tmp = rtf.charAt(++_currentParseIndex);
|
||||
|
||||
if (tmp !== "\\" && hex.length > 0)
|
||||
{
|
||||
[self _appendPlainString: String.fromCharCode(parseInt((hex), 16))];
|
||||
hex = '';
|
||||
}
|
||||
|
||||
switch (tmp)
|
||||
{
|
||||
case " ":
|
||||
if (lastchar == 1)
|
||||
{
|
||||
lastchar = 0;
|
||||
}
|
||||
else
|
||||
{
|
||||
_freename += tmp;
|
||||
[self _appendPlainString:tmp];
|
||||
}
|
||||
|
||||
break;
|
||||
|
||||
case "{":
|
||||
if ([self pushState])
|
||||
CPLogConsole("push");
|
||||
|
||||
break;
|
||||
|
||||
case "}":
|
||||
if ([self popState])
|
||||
CPLogConsole("pop");
|
||||
|
||||
if (_freename)
|
||||
{
|
||||
CPLogConsole(_freename);
|
||||
|
||||
if (_parsingFontTable)
|
||||
{
|
||||
_fontArray.push(_freename);
|
||||
_parsingFontTable = NO;
|
||||
}
|
||||
|
||||
_freename = "";
|
||||
}
|
||||
|
||||
[self _flushCurrentRun];
|
||||
break;
|
||||
|
||||
case "\\":
|
||||
_freename = '';
|
||||
ch = [self _parseKeyword:rtf length:len];
|
||||
|
||||
if (!_hexreturn && ch.length == 0)
|
||||
lastchar = 1;
|
||||
else
|
||||
lastchar = 0;
|
||||
|
||||
if (_hexreturn)
|
||||
{
|
||||
if (ch.length > 0)
|
||||
{
|
||||
if (parseInt(ch, 16) & 0x80)
|
||||
{
|
||||
hex += ch.toUpperCase();
|
||||
}
|
||||
else
|
||||
{
|
||||
[self _appendPlainString: String.fromCharCode(parseInt((hex + ch), 16))];
|
||||
hex = '';
|
||||
}
|
||||
|
||||
if (hex.length == 4)
|
||||
{
|
||||
var temp = parseInt(hex, 16);
|
||||
|
||||
if (hexTable && hexTable[hex.toUpperCase()] !== undefined)
|
||||
temp = parseInt(hexTable[hex.toUpperCase()], 16);
|
||||
|
||||
[self _appendPlainString: String.fromCharCode(temp)];
|
||||
hex = '';
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
CPLogConsole("hex skipped");
|
||||
}
|
||||
|
||||
_hexreturn = NO;
|
||||
}
|
||||
else if (ch !== undefined && _curState === 0)
|
||||
{
|
||||
[self _appendPlainString:ch];
|
||||
}
|
||||
|
||||
break;
|
||||
|
||||
case 0x0d:
|
||||
case 0x0a:
|
||||
case '\n':
|
||||
case '\r':
|
||||
break;
|
||||
|
||||
default:
|
||||
lastchar = 0;
|
||||
|
||||
if (_curState == 0)
|
||||
[self _appendPlainString:tmp];
|
||||
else if (tmp !== ';')
|
||||
_freename += tmp;
|
||||
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
return _result;
|
||||
}
|
||||
|
||||
@end
|
||||
@@ -0,0 +1,613 @@
|
||||
/*
|
||||
_CPRTFProducer.j
|
||||
|
||||
Serialize CPAttributedString to a RTF String
|
||||
|
||||
Copyright (C) 2014 Daniel Boehringer
|
||||
This file is based on the RTFProducer from GNUStep
|
||||
(which i co-authored with Fred Kiefer in 1999)
|
||||
|
||||
* This library is free software; you can redistribute it and/or
|
||||
* modify it under the terms of the GNU Lesser General Public
|
||||
* License as published by the Free Software Foundation; either
|
||||
* version 2.1 of the License, or (at your option) any later version.
|
||||
*
|
||||
* This library is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
|
||||
* Lesser General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU Lesser General Public
|
||||
* License along with this library; if not, write to the Free Software
|
||||
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
|
||||
*/
|
||||
|
||||
@import <Foundation/CPAttributedString.j>
|
||||
@import "CPParagraphStyle.j"
|
||||
@import "CPColor.j"
|
||||
@import "CPGraphics.j"
|
||||
@import "CPFontManager.j"
|
||||
|
||||
@global CPForegroundColorAttributeName
|
||||
@global CPBackgroundColorAttributeName
|
||||
@global CPUnderlineStyleAttributeName
|
||||
@global CPSuperscriptAttributeName
|
||||
@global CPBaselineOffsetAttributeName
|
||||
@global CPAttachmentAttributeName
|
||||
@global CPLigatureAttributeName
|
||||
@global CPKernAttributeName
|
||||
|
||||
@global CPLeftTextAlignment
|
||||
@global CPRightTextAlignment
|
||||
@global CPCenterTextAlignment
|
||||
@global CPJustifiedTextAlignment
|
||||
@global CPNaturalTextAlignment
|
||||
|
||||
var PAPERSIZE = @"PaperSize",
|
||||
LEFTMARGIN = @"LeftMargin",
|
||||
RIGHTMARGIN = @"RightMargin",
|
||||
TOPMARGIN = @"TopMargin",
|
||||
BUTTOMMARGIN = @"ButtomMargin";
|
||||
|
||||
function _points2twips(a) { return (a) * 20.0; }
|
||||
|
||||
@implementation _CPRTFProducer : CPObject
|
||||
{
|
||||
CPAttributedString text;
|
||||
CPMutableDictionary fontDict;
|
||||
CPMutableDictionary colorDict;
|
||||
CPDictionary docDict;
|
||||
CPMutableArray attachments;
|
||||
CPFont currentFont;
|
||||
CPColor fgColor;
|
||||
CPColor bgColor;
|
||||
CPColor ulColor;
|
||||
}
|
||||
|
||||
#pragma mark -
|
||||
#pragma mark Class methods
|
||||
|
||||
|
||||
+ (CPString)produceRTF:(CPAttributedString)aText documentAttributes:(CPDictionary)dict
|
||||
{
|
||||
var mynew = [self new];
|
||||
|
||||
return [mynew RTFDStringFromAttributedString:aText documentAttributes:dict];
|
||||
}
|
||||
|
||||
|
||||
#pragma mark -
|
||||
#pragma mark init methods
|
||||
|
||||
- (id)init
|
||||
{
|
||||
if (self = [super init])
|
||||
{
|
||||
// maintain a dictionary for the used colours
|
||||
// (for rtf-header generation)
|
||||
colorDict = [CPMutableDictionary new];
|
||||
|
||||
//maintain a dictionary for the used fonts
|
||||
//(for rtf-header generation)
|
||||
fontDict = [CPMutableDictionary new];
|
||||
|
||||
fgColor = [CPColor blackColor];
|
||||
bgColor= [CPColor whiteColor];
|
||||
}
|
||||
|
||||
return self;
|
||||
}
|
||||
|
||||
// private stuff follows
|
||||
- (CPString)fontTable
|
||||
{
|
||||
if (![fontDict count])
|
||||
return @"";
|
||||
|
||||
var fontlistString = "",
|
||||
fontEnum,
|
||||
currFont,
|
||||
keyArray;
|
||||
|
||||
keyArray = [fontDict allKeys];
|
||||
keyArray = [keyArray sortedArrayUsingSelector:@selector(compare:)];
|
||||
fontEnum = [keyArray objectEnumerator];
|
||||
|
||||
while ((currFont = [fontEnum nextObject]) !== nil)
|
||||
{
|
||||
var fontFamily,
|
||||
detail;
|
||||
|
||||
if ([currFont isEqualToString:@"Symbol"])
|
||||
fontFamily = @"tech";
|
||||
else if ([currFont isEqualToString:@"Helvetica"])
|
||||
fontFamily = @"swiss";
|
||||
else if ([currFont isEqualToString:@"Arial"])
|
||||
fontFamily = @"swiss";
|
||||
else if ([currFont isEqualToString:@"Courier"])
|
||||
fontFamily = @"modern";
|
||||
else if ([currFont isEqualToString:@"Times"])
|
||||
fontFamily = @"roman";
|
||||
else fontFamily = @"nil";
|
||||
|
||||
detail = [CPString stringWithFormat:@"%@\\f%@ %@;", [fontDict objectForKey:currFont], fontFamily, currFont];
|
||||
fontlistString += detail;
|
||||
}
|
||||
|
||||
return [CPString stringWithFormat:@"{\\fonttbl%@}\n", fontlistString];
|
||||
}
|
||||
|
||||
- (CPString)colorTable
|
||||
{
|
||||
if (![colorDict count])
|
||||
return @"";
|
||||
|
||||
var result,
|
||||
count = [colorDict count],
|
||||
list = [CPMutableArray arrayWithCapacity:count],
|
||||
keyEnum = [colorDict keyEnumerator],
|
||||
next,
|
||||
i;
|
||||
|
||||
while ((next = [keyEnum nextObject]) !== nil)
|
||||
{
|
||||
var cn = [colorDict objectForKey:next];
|
||||
[list insertObject:[CPColor colorWithCSSString:next] atIndex:[cn intValue]-1];
|
||||
}
|
||||
|
||||
result = [CPString stringWithString:@"{\\colortbl;"];
|
||||
|
||||
for (i = 0; i < count; i++)
|
||||
{
|
||||
var color = [[list objectAtIndex:i]
|
||||
colorUsingColorSpaceName:CPCalibratedRGBColorSpace];
|
||||
|
||||
result += [CPString stringWithFormat:@"\\red%d\\green%d\\blue%d;",
|
||||
([color redComponent] * 255),
|
||||
([color greenComponent] * 255),
|
||||
([color blueComponent] * 255)];
|
||||
}
|
||||
|
||||
result += @"}\n";
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
- (CPString)documentAttributes
|
||||
{
|
||||
if (!docDict)
|
||||
return @"";
|
||||
|
||||
var result,
|
||||
detail,
|
||||
val,
|
||||
num;
|
||||
|
||||
result = [CPString string];
|
||||
|
||||
val = [docDict objectForKey:PAPERSIZE];
|
||||
|
||||
if (val)
|
||||
{
|
||||
detail = [CPString stringWithFormat:@"\\paperw%d \\paperh%d",
|
||||
_points2twips(val.width),
|
||||
_points2twips(val.height)];
|
||||
|
||||
result += detail;
|
||||
}
|
||||
|
||||
num = [docDict objectForKey:LEFTMARGIN];
|
||||
|
||||
if (num)
|
||||
{
|
||||
var f = [num floatValue];
|
||||
|
||||
detail = [CPString stringWithFormat:@"\\margl%d", _points2twips(f)];
|
||||
result += detail;
|
||||
}
|
||||
|
||||
num = [docDict objectForKey:RIGHTMARGIN];
|
||||
|
||||
if (num)
|
||||
{
|
||||
var f = [num floatValue];
|
||||
|
||||
detail = [CPString stringWithFormat:@"\\margr%d", _points2twips(f)];
|
||||
result += detail;
|
||||
}
|
||||
|
||||
num = [docDict objectForKey:TOPMARGIN];
|
||||
|
||||
if (num)
|
||||
{
|
||||
var f = [num floatValue];
|
||||
|
||||
detail = [CPString stringWithFormat:@"\\margt%d", _points2twips(f)];
|
||||
result += detail;
|
||||
}
|
||||
|
||||
num = [docDict objectForKey:BUTTOMMARGIN];
|
||||
|
||||
if (num)
|
||||
{
|
||||
var f = [num floatValue];
|
||||
|
||||
detail = [CPString stringWithFormat:@"\\margb%d", _points2twips(f)];
|
||||
result += detail;
|
||||
}
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
- (CPString)headerString
|
||||
{
|
||||
var result;
|
||||
|
||||
result = [CPString stringWithString:@"{\\rtf1\\ansi"];
|
||||
result += [self fontTable];
|
||||
result += [self colorTable];
|
||||
result += [self documentAttributes];
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
- (CPString)trailerString
|
||||
{
|
||||
return @"}";
|
||||
}
|
||||
|
||||
- (CPString)fontToken:(CPString) fontName
|
||||
{
|
||||
var fCount = [fontDict objectForKey:fontName];
|
||||
|
||||
if (fCount == nil)
|
||||
{
|
||||
var count = [fontDict count];
|
||||
|
||||
fCount = [CPString stringWithFormat:@"\\f%d", count];
|
||||
[fontDict setObject:fCount forKey:fontName];
|
||||
}
|
||||
|
||||
return fCount;
|
||||
}
|
||||
|
||||
- (int)numberForColor:(CPColor)color
|
||||
{
|
||||
var num = [colorDict objectForKey:[color cssString]];
|
||||
|
||||
if (!num)
|
||||
[colorDict setObject:num = [CPNumber numberWithInt:[colorDict count] + 1]
|
||||
forKey:[color cssString]];
|
||||
|
||||
return [num intValue];
|
||||
}
|
||||
|
||||
- (CPString)paragraphStyle:(CPParagraphStyle)paraStyle
|
||||
{
|
||||
var headerString = [CPString stringWithString:@"\\pard"],
|
||||
twips;
|
||||
|
||||
if (paraStyle == nil)
|
||||
return headerString;
|
||||
|
||||
switch ([paraStyle alignment])
|
||||
{
|
||||
case CPRightTextAlignment:
|
||||
headerString += @"\\qr";
|
||||
break;
|
||||
|
||||
case CPCenterTextAlignment:
|
||||
headerString += @"\\qc";
|
||||
break;
|
||||
|
||||
case CPLeftTextAlignment:
|
||||
headerString += @"\\ql";
|
||||
break;
|
||||
|
||||
case CPJustifiedTextAlignment:
|
||||
headerString += @"\\qj";
|
||||
break;
|
||||
|
||||
default:
|
||||
headerString += @"\\ql";
|
||||
break;
|
||||
}
|
||||
|
||||
// write first line indent and left indent
|
||||
var twips = _points2twips([paraStyle firstLineHeadIndent]);
|
||||
|
||||
if (twips != 0.0)
|
||||
headerString += [CPString stringWithFormat:@"\\fi%d", twips];
|
||||
|
||||
twips = _points2twips([paraStyle headIndent]);
|
||||
|
||||
if (twips != 0.0)
|
||||
headerString += [CPString stringWithFormat:@"\\li%d", twips];
|
||||
|
||||
twips = _points2twips([paraStyle tailIndent]);
|
||||
|
||||
if (twips != 0.0)
|
||||
headerString += [CPString stringWithFormat:@"\\ri%d", twips];
|
||||
|
||||
twips = _points2twips([paraStyle paragraphSpacing]);
|
||||
|
||||
if (twips != 0.0)
|
||||
headerString += [CPString stringWithFormat:@"\\sa%d", twips];
|
||||
|
||||
twips = _points2twips([paraStyle minimumLineHeight]);
|
||||
|
||||
if (twips != 0.0)
|
||||
headerString += [CPString stringWithFormat:@"\\sl%d", twips];
|
||||
|
||||
twips = _points2twips([paraStyle maximumLineHeight]);
|
||||
|
||||
if (twips != 0.0)
|
||||
headerString += [CPString stringWithFormat:@"\\sl-%d", twips];
|
||||
|
||||
var enumerator,
|
||||
tab;
|
||||
|
||||
enumerator = [[paraStyle tabStops] objectEnumerator];
|
||||
|
||||
while ((tab = [enumerator nextObject]))
|
||||
{
|
||||
switch ([tab tabStopType])
|
||||
{
|
||||
case CPLeftTabStopType:
|
||||
// no tabkind emission needed
|
||||
break;
|
||||
/* case NSRightTabStopType:
|
||||
headerString += @"\\tqr";
|
||||
break;
|
||||
case NSCenterTabStopType:
|
||||
headerString += @"\\tqc";
|
||||
break;
|
||||
case NSDecimalTabStopType:
|
||||
headerString += @"\\tqdec";
|
||||
break;
|
||||
default:
|
||||
NSLog(@"Unknown tab stop type.");
|
||||
*/
|
||||
}
|
||||
|
||||
headerString += [CPString stringWithFormat:@"\\tx%d",_points2twips([tab location])];
|
||||
}
|
||||
|
||||
return headerString;
|
||||
}
|
||||
|
||||
- (CPString)runStringForString:(CPString) substring
|
||||
attributes:(CPDictionary) attributes
|
||||
paragraphStart:(BOOL) first
|
||||
{
|
||||
var result = "",
|
||||
headerString = "",
|
||||
trailerString = "",
|
||||
attribEnum,
|
||||
currAttrib;
|
||||
|
||||
if (first)
|
||||
{
|
||||
var paraStyle = [attributes objectForKey:CPParagraphStyleAttributeName];
|
||||
headerString += [self paragraphStyle:paraStyle];
|
||||
}
|
||||
|
||||
/*
|
||||
* analyze attributes of current run
|
||||
*
|
||||
* FIXME: All the character attributes should be output relative to the font
|
||||
* attributes of the paragraph. So if the paragraph has underline on it should
|
||||
* still be possible to switch it off for some characters, which currently is
|
||||
* not possible.
|
||||
*/
|
||||
attribEnum = [attributes keyEnumerator];
|
||||
|
||||
while ((currAttrib = [attribEnum nextObject]) != nil)
|
||||
{
|
||||
if ([currAttrib isEqualToString:CPFontAttributeName])
|
||||
{
|
||||
/*
|
||||
* handle fonts
|
||||
*/
|
||||
var font,
|
||||
fontName,
|
||||
traits;
|
||||
|
||||
font = [attributes objectForKey:CPFontAttributeName];
|
||||
fontName = [font familyName];
|
||||
traits = [[CPFontManager sharedFontManager] traitsOfFont:font];
|
||||
|
||||
/*
|
||||
* font name
|
||||
*/
|
||||
if (currentFont == nil || ![fontName isEqualToString:[currentFont familyName]])
|
||||
headerString += [self fontToken:fontName];
|
||||
|
||||
/*
|
||||
* font size
|
||||
*/
|
||||
if (currentFont == nil || [font size] != [currentFont size])
|
||||
{
|
||||
var points = [font size] * 2,
|
||||
pString;
|
||||
|
||||
pString = [CPString stringWithFormat:@"\\fs%d", points];
|
||||
headerString += pString;
|
||||
}
|
||||
/*
|
||||
* font attributes
|
||||
*/
|
||||
if (traits & CPItalicFontMask)
|
||||
{
|
||||
headerString += @"\\i";
|
||||
trailerString += @"\\i0";
|
||||
}
|
||||
|
||||
if (traits & CPBoldFontMask)
|
||||
{
|
||||
headerString += @"\\b";
|
||||
trailerString += @"\\b0";
|
||||
}
|
||||
|
||||
if (first)
|
||||
currentFont = font;
|
||||
}
|
||||
else if ([currAttrib isEqualToString:CPForegroundColorAttributeName])
|
||||
{
|
||||
var color = [attributes objectForKey:CPForegroundColorAttributeName];
|
||||
|
||||
if (![color isEqual:fgColor])
|
||||
{
|
||||
headerString += [CPString stringWithFormat:@"\\cf%d", [self numberForColor:color]];
|
||||
trailerString += @"\\cf0";
|
||||
}
|
||||
}
|
||||
else if ([currAttrib isEqualToString:CPBackgroundColorAttributeName])
|
||||
{
|
||||
var color = [attributes objectForKey:CPBackgroundColorAttributeName];
|
||||
|
||||
if (![color isEqual:bgColor])
|
||||
{
|
||||
headerString += [CPString stringWithFormat:@"\\cb%d", [self numberForColor:color]];
|
||||
trailerString += @"\\cb0";
|
||||
}
|
||||
}
|
||||
else if ([currAttrib isEqualToString:CPUnderlineStyleAttributeName])
|
||||
{
|
||||
headerString += @"\\ul";
|
||||
trailerString += @"\\ulnone";
|
||||
}
|
||||
else if ([currAttrib isEqualToString:CPSuperscriptAttributeName])
|
||||
{
|
||||
var value = [attributes objectForKey:CPSuperscriptAttributeName],
|
||||
svalue = [value intValue] * 6;
|
||||
|
||||
if (svalue > 0)
|
||||
{
|
||||
headerString += [CPString stringWithFormat:@"\\up%d", svalue];
|
||||
trailerString += @"\\up0";
|
||||
}
|
||||
else if (svalue < 0)
|
||||
{
|
||||
headerString += [CPString stringWithFormat:@"\\dn-%d", svalue];
|
||||
trailerString += @"\\dn0";
|
||||
}
|
||||
}
|
||||
else if ([currAttrib isEqualToString:CPBaselineOffsetAttributeName])
|
||||
{
|
||||
var value = [attributes objectForKey:CPBaselineOffsetAttributeName],
|
||||
svalue = [value floatValue] * 2;
|
||||
|
||||
if (svalue > 0)
|
||||
{
|
||||
headerString += [CPString stringWithFormat:@"\\up%d", svalue];
|
||||
trailerString += @"\\up0";
|
||||
}
|
||||
else if (svalue < 0)
|
||||
{
|
||||
headerString += [CPString stringWithFormat:@"\\dn-%d", svalue];
|
||||
trailerString += @"\\dn0";
|
||||
}
|
||||
}
|
||||
else if ([currAttrib isEqualToString:CPAttachmentAttributeName])
|
||||
{
|
||||
}
|
||||
else if ([currAttrib isEqualToString:CPLigatureAttributeName])
|
||||
{
|
||||
}
|
||||
else if ([currAttrib isEqualToString:CPKernAttributeName])
|
||||
{
|
||||
}
|
||||
}
|
||||
|
||||
substring = substring.replace(/\\/g, '\\\\');
|
||||
substring = substring.replace(/\n/g, '\\par\n');
|
||||
substring = substring.replace(/\t/g, '\\tab');
|
||||
substring = substring.replace(/{/g, '\\{');
|
||||
substring = substring.replace(/}/g, '\\}');
|
||||
// FIXME: All characters not in the standard encoding must be
|
||||
// replaced by \'xx
|
||||
|
||||
if (!first)
|
||||
{
|
||||
var braces;
|
||||
|
||||
if ([headerString length])
|
||||
braces = [CPString stringWithFormat:@"{%@ %@}", headerString, substring];
|
||||
else
|
||||
braces = substring;
|
||||
|
||||
result += braces;
|
||||
}
|
||||
else
|
||||
{
|
||||
var nobraces;
|
||||
|
||||
if ([headerString length])
|
||||
nobraces = [CPString stringWithFormat:@"%@ %@", headerString, substring];
|
||||
else
|
||||
nobraces = substring;
|
||||
|
||||
result += nobraces;
|
||||
}
|
||||
|
||||
return result + trailerString;
|
||||
}
|
||||
|
||||
- (CPString)bodyString
|
||||
{
|
||||
var string = [text string],
|
||||
result = "",
|
||||
loc = 0,
|
||||
length = [string length],
|
||||
currRange = CPMakeRange(loc, 0),
|
||||
completeRange = CPMakeRange(0, length),
|
||||
first = YES;
|
||||
|
||||
// FIXME <!> split along newline characters and run as outer loop
|
||||
while (CPMaxRange(currRange) < CPMaxRange(completeRange)) // save all "runs"
|
||||
{
|
||||
var attributes,
|
||||
substring,
|
||||
runString;
|
||||
|
||||
attributes = [text attributesAtIndex:CPMaxRange(currRange)
|
||||
longestEffectiveRange:currRange
|
||||
inRange:completeRange];
|
||||
substring = [string substringWithRange:currRange];
|
||||
runString = [self runStringForString:substring
|
||||
attributes:attributes
|
||||
paragraphStart:YES];
|
||||
|
||||
result += runString;
|
||||
first = NO;
|
||||
}
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
|
||||
- (CPString)RTFDStringFromAttributedString:(CPAttributedString)aText
|
||||
documentAttributes:(CPDictionary)dict
|
||||
{
|
||||
var output = [CPString string],
|
||||
headerString,
|
||||
trailerString,
|
||||
bodyString;
|
||||
|
||||
text = aText;
|
||||
docDict = dict;
|
||||
|
||||
/*
|
||||
* do not change order! (esp. body has to be generated first; builds context)
|
||||
*/
|
||||
bodyString = [self bodyString];
|
||||
trailerString = [self trailerString];
|
||||
headerString = [self headerString];
|
||||
|
||||
output += headerString;
|
||||
output += bodyString;
|
||||
output += trailerString;
|
||||
return output;
|
||||
}
|
||||
@end
|
||||
+70
-2
@@ -24,9 +24,12 @@
|
||||
@import <Foundation/CPMutableArray.j>
|
||||
@import <Foundation/CPString.j>
|
||||
@import <Foundation/CPKeyedUnarchiver.j>
|
||||
@import <Foundation/CPBundle.j>
|
||||
|
||||
@class CPView
|
||||
@class _CPThemeAttribute
|
||||
@class CPImage
|
||||
@class CPColor
|
||||
|
||||
var CPThemesByName = { },
|
||||
CPThemeDefaultTheme = nil,
|
||||
@@ -313,6 +316,71 @@ var CPThemeNameKey = @"CPThemeNameKey",
|
||||
|
||||
@end
|
||||
|
||||
#pragma mark -
|
||||
#pragma mark CSS Theming
|
||||
|
||||
// The code below adds support for CSS theming with 100% compatibility with current theming system.
|
||||
// The idea is to extend CPColor and CPImage with CSS components and adapt low level UI components to
|
||||
// support this new kind of CPColor/CPImage. See CPImageView, CPView and _CPImageAndTextView.
|
||||
//
|
||||
// To be considered and treated as CSS based, a theme must set to YES the attribute "css-based" for CPView
|
||||
// in the theDescriptor (default value is NO), like in the example below :
|
||||
//
|
||||
// + (CPView)themedView
|
||||
// {
|
||||
// var view = [[CPView alloc] initWithFrame:CGRectMake(0, 0, 100, 100)];
|
||||
//
|
||||
// [self registerThemeValues:[[@"css-based", YES]] forView:view];
|
||||
//
|
||||
// return view;
|
||||
// }
|
||||
//
|
||||
// You can use the method -(BOOL)isCSSBased on a theme to determine how to cope with it in your own code.
|
||||
//
|
||||
// The method -(void)setCSSResourcesPath is meant to be used only by CPThemeBlend during theme loading in order to
|
||||
// replace the special path "%%" in CSS components (like in url(%%packed.png) ) with the path to the theme blend resources folder.
|
||||
|
||||
@implementation CPTheme (CSSTheming)
|
||||
|
||||
- (void)setCSSResourcesPath:(CPString)pathToResources
|
||||
{
|
||||
[_attributes enumerateKeysAndObjectsUsingBlock:function(aKey, anObject, stop)
|
||||
{
|
||||
[anObject enumerateKeysAndObjectsUsingBlock:function(aKey2, anObject2, stop2)
|
||||
{
|
||||
// anObject2 is now a _CPThemeAttribute
|
||||
|
||||
[[anObject2 values] enumerateKeysAndObjectsUsingBlock:function(aKey3, anObject3, stop3)
|
||||
{
|
||||
if (anObject3.isa && ([anObject3 isKindOfClass:CPImage] || [anObject3 isKindOfClass:CPColor]) && [anObject3 cssDictionary])
|
||||
{
|
||||
// We have a CSS defined image or color
|
||||
[self _fixPathInCSSDictionary:[anObject3 cssDictionary] withPathToResources:pathToResources];
|
||||
[self _fixPathInCSSDictionary:[anObject3 cssBeforeDictionary] withPathToResources:pathToResources];
|
||||
[self _fixPathInCSSDictionary:[anObject3 cssAfterDictionary] withPathToResources:pathToResources];
|
||||
}
|
||||
}];
|
||||
}];
|
||||
}];
|
||||
}
|
||||
|
||||
- (void)_fixPathInCSSDictionary:(CPDictionary)aDictionary withPathToResources:(CPString)pathToResources
|
||||
{
|
||||
[aDictionary enumerateKeysAndObjectsUsingBlock:function(aKey, anObject, stop)
|
||||
{
|
||||
[aDictionary setObject:[anObject stringByReplacingOccurrencesOfString:@"%%" withString:pathToResources] forKey:aKey];
|
||||
}];
|
||||
}
|
||||
|
||||
- (BOOL)isCSSBased
|
||||
{
|
||||
return !![self valueForAttributeWithName:@"css-based" forClass:[CPView class]];
|
||||
}
|
||||
|
||||
@end
|
||||
|
||||
#pragma mark -
|
||||
|
||||
/*!
|
||||
* ThemeStates are immutable objects representing a particular ThemeState. Applications should never be creating
|
||||
* ThemeStates directly but should instead use the CPThemeState function.
|
||||
@@ -322,7 +390,7 @@ function ThemeState(stateNames)
|
||||
var stateNameKeys = [];
|
||||
this._stateNames = {};
|
||||
|
||||
for (key in stateNames)
|
||||
for (var key in stateNames)
|
||||
{
|
||||
if (!stateNames.hasOwnProperty(key))
|
||||
continue;
|
||||
@@ -769,7 +837,7 @@ var ParentAttributeForCoder = nil;
|
||||
if ([aCoder containsValueForKey:@"state"])
|
||||
state = [aCoder decodeObjectForKey:@"state"];
|
||||
else
|
||||
state = CPThemeStateNormalString
|
||||
state = CPThemeStateNormalString;
|
||||
|
||||
[_values setObject:[aCoder decodeObjectForKey:"value"] forKey:state];
|
||||
}
|
||||
|
||||
+15
-6
@@ -64,12 +64,10 @@
|
||||
*/
|
||||
- (CPArray)themeNames
|
||||
{
|
||||
var names = [];
|
||||
|
||||
for (var i = 0; i < _themes.length; ++i)
|
||||
names.push(_themes[i].substring(0, _themes[i].indexOf(".keyedtheme")));
|
||||
|
||||
return names;
|
||||
return [_themes arrayByApplyingBlock:function(theme)
|
||||
{
|
||||
return theme.substring(0, theme.indexOf(".keyedtheme"));
|
||||
}];
|
||||
}
|
||||
|
||||
- (void)loadWithDelegate:(id)aDelegate
|
||||
@@ -97,6 +95,17 @@
|
||||
[unarchiver finishDecoding];
|
||||
}
|
||||
|
||||
// CSS Theming
|
||||
|
||||
for (var i = 0, nb = [_themes count], allThemes = [self themeNames], aThemeName, aTheme; i < nb; i++)
|
||||
{
|
||||
aThemeName = allThemes[i];
|
||||
aTheme = [CPTheme themeNamed:aThemeName];
|
||||
|
||||
if ([aTheme isCSSBased])
|
||||
[aTheme setCSSResourcesPath:[aBundle resourcePath]];
|
||||
}
|
||||
|
||||
[_loadDelegate blendDidFinishLoading:self];
|
||||
}
|
||||
|
||||
|
||||
+14
-6
@@ -523,17 +523,25 @@ CPTokenFieldDeleteButtonType = 1;
|
||||
var element = [self _inputElement];
|
||||
|
||||
CPTokenFieldInputResigning = YES;
|
||||
element.blur();
|
||||
|
||||
if (CPTokenFieldInputIsActive)
|
||||
element.blur();
|
||||
|
||||
if (!CPTokenFieldInputDidBlur)
|
||||
CPTokenFieldBlurHandler();
|
||||
|
||||
CPTokenFieldInputDidBlur = NO;
|
||||
CPTokenFieldInputResigning = NO;
|
||||
|
||||
if (element.parentNode == [_tokenScrollView documentView]._DOMElement)
|
||||
element.parentNode.removeChild(element);
|
||||
|
||||
// Previosly, we unflagged CPTokenFieldInputDidBlur and CPTokenFieldInputResigning before
|
||||
// the call to removeChild. This may result in DOM exceptions in Chrome under certain conditions.
|
||||
// See https://stackoverflow.com/questions/21926083/failed-to-execute-removechild-on-node
|
||||
// for why we need to unflag CPTokenFieldInputDidBlur and CPTokenFieldInputResigning
|
||||
// only after removing the element.
|
||||
|
||||
CPTokenFieldInputDidBlur = NO;
|
||||
CPTokenFieldInputResigning = NO;
|
||||
|
||||
CPTokenFieldInputIsActive = NO;
|
||||
|
||||
if (document.attachEvent)
|
||||
@@ -541,7 +549,7 @@ CPTokenFieldDeleteButtonType = 1;
|
||||
CPTokenFieldCachedSelectStartFunction = nil;
|
||||
CPTokenFieldCachedDragFunction = nil;
|
||||
|
||||
document.body.ondrag = CPTokenFieldCachedDragFunction
|
||||
document.body.ondrag = CPTokenFieldCachedDragFunction;
|
||||
document.body.onselectstart = CPTokenFieldCachedSelectStartFunction
|
||||
}
|
||||
|
||||
@@ -1239,7 +1247,7 @@ CPTokenFieldDeleteButtonType = 1;
|
||||
var scrollToToken = _shouldScrollTo;
|
||||
|
||||
if (scrollToToken === CPScrollDestinationLeft)
|
||||
scrollToToken = tokens[_selectedRange.location]
|
||||
scrollToToken = tokens[_selectedRange.location];
|
||||
else if (scrollToToken === CPScrollDestinationRight)
|
||||
scrollToToken = tokens[MAX(0, CPMaxRange(_selectedRange) - 1)];
|
||||
[self _scrollTokenViewToVisible:scrollToToken];
|
||||
|
||||
+6
-7
@@ -153,7 +153,7 @@ var CPToolbarsByIdentifier = nil,
|
||||
|
||||
if (!toolbarsSharingIdentifier)
|
||||
{
|
||||
toolbarsSharingIdentifier = []
|
||||
toolbarsSharingIdentifier = [];
|
||||
[CPToolbarsByIdentifier setObject:toolbarsSharingIdentifier forKey:identifier];
|
||||
}
|
||||
|
||||
@@ -459,11 +459,10 @@ var CPToolbarsByIdentifier = nil,
|
||||
/* @ignore */
|
||||
- (id)_itemsWithIdentifiers:(CPArray)identifiers
|
||||
{
|
||||
var items = [];
|
||||
for (var i = 0; i < identifiers.length; i++)
|
||||
[items addObject:[self _itemForItemIdentifier:identifiers[i] willBeInsertedIntoToolbar:NO]];
|
||||
|
||||
return items;
|
||||
return [identifiers arrayByApplyingBlock:function(identifier)
|
||||
{
|
||||
return [self _itemForItemIdentifier:identifier willBeInsertedIntoToolbar:NO];
|
||||
}];
|
||||
}
|
||||
|
||||
/* @ignore */
|
||||
@@ -589,7 +588,7 @@ var CPToolbarIdentifierKey = @"CPToolbarIdentifierKey",
|
||||
[aCoder encodeBool:_showsBaselineSeparator forKey:CPToolbarShowsBaselineSeparatorKey];
|
||||
[aCoder encodeBool:_allowsUserCustomization forKey:CPToolbarAllowsUserCustomizationKey];
|
||||
[aCoder encodeBool:_isVisible forKey:CPToolbarIsVisibleKey];
|
||||
[aCoder encodeInt:_sizeMode forKey:CPToolbarSizeModeKey]
|
||||
[aCoder encodeInt:_sizeMode forKey:CPToolbarSizeModeKey];
|
||||
|
||||
[aCoder encodeObject:_identifiedItems forKey:CPToolbarIdentifiedItemsKey];
|
||||
[aCoder encodeObject:_defaultItems forKey:CPToolbarDefaultItemsKey];
|
||||
|
||||
+223
-121
@@ -112,6 +112,11 @@ CPViewHeightSizable = 16;
|
||||
*/
|
||||
CPViewMaxYMargin = 32;
|
||||
|
||||
_CPViewWillAppearNotification = @"CPViewWillAppearNotification";
|
||||
_CPViewDidAppearNotification = @"CPViewDidAppearNotification";
|
||||
_CPViewWillDisappearNotification = @"CPViewWillDisappearNotification";
|
||||
_CPViewDidDisappearNotification = @"CPViewDidDisappearNotification";
|
||||
|
||||
CPViewBoundsDidChangeNotification = @"CPViewBoundsDidChangeNotification";
|
||||
CPViewFrameDidChangeNotification = @"CPViewFrameDidChangeNotification";
|
||||
|
||||
@@ -124,7 +129,8 @@ var DOMElementPrototype = nil,
|
||||
BackgroundVerticalThreePartImage = 1,
|
||||
BackgroundHorizontalThreePartImage = 2,
|
||||
BackgroundNinePartImage = 3,
|
||||
BackgroundTransparentColor = 4;
|
||||
BackgroundTransparentColor = 4,
|
||||
BackgroundCSSStyling = 5;
|
||||
#endif
|
||||
|
||||
var CPViewFlags = { },
|
||||
@@ -172,6 +178,7 @@ var CPViewHighDPIDrawingEnabled = YES;
|
||||
CPArray _registeredDraggedTypesArray;
|
||||
|
||||
BOOL _isHidden;
|
||||
BOOL _isHiddenOrHasHiddenAncestor;
|
||||
BOOL _hitTests;
|
||||
BOOL _clipsToBounds;
|
||||
|
||||
@@ -189,6 +196,10 @@ var CPViewHighDPIDrawingEnabled = YES;
|
||||
CPArray _DOMImageSizes;
|
||||
|
||||
unsigned _backgroundType;
|
||||
|
||||
// CSS styling
|
||||
CPArray _cssStylePreviousState;
|
||||
DOMElement _cssStyleNode;
|
||||
#endif
|
||||
|
||||
CGRect _dirtyRect;
|
||||
@@ -243,6 +254,11 @@ var CPViewHighDPIDrawingEnabled = YES;
|
||||
|
||||
CPMutableArray _trackingAreas @accessors(getter=trackingAreas, copy);
|
||||
BOOL _inhibitUpdateTrackingAreas;
|
||||
|
||||
id _animator;
|
||||
CPDictionary _animationsDictionary;
|
||||
BOOL _inhibitDOMUpdates @accessors(setter=_setInhibitDOMUpdates);
|
||||
BOOL _forceUpdates @accessors(setter=_setForceUpdates);
|
||||
}
|
||||
|
||||
/*
|
||||
@@ -309,6 +325,18 @@ var CPViewHighDPIDrawingEnabled = YES;
|
||||
return nil;
|
||||
}
|
||||
|
||||
+ (CPString)defaultThemeClass
|
||||
{
|
||||
return @"view";
|
||||
}
|
||||
|
||||
+ (CPDictionary)themeAttributes
|
||||
{
|
||||
return @{
|
||||
@"css-based": NO
|
||||
};
|
||||
}
|
||||
|
||||
- (void)_setupViewFlags
|
||||
{
|
||||
var theClass = [self class],
|
||||
@@ -369,6 +397,7 @@ var CPViewHighDPIDrawingEnabled = YES;
|
||||
|
||||
_opacity = 1.0;
|
||||
_isHidden = NO;
|
||||
_isHiddenOrHasHiddenAncestor = NO;
|
||||
_hitTests = YES;
|
||||
|
||||
_hierarchyScaleSize = CGSizeMake(1.0 , 1.0);
|
||||
@@ -387,16 +416,28 @@ var CPViewHighDPIDrawingEnabled = YES;
|
||||
|
||||
_DOMImageParts = [];
|
||||
_DOMImageSizes = [];
|
||||
|
||||
_cssStylePreviousState = @[];
|
||||
#endif
|
||||
|
||||
_animator = nil;
|
||||
_animationsDictionary = @{};
|
||||
|
||||
[self _setupViewFlags];
|
||||
[self _loadThemeAttributes];
|
||||
}
|
||||
|
||||
_inhibitDOMUpdates = NO;
|
||||
_forceUpdates = NO;
|
||||
|
||||
// We force here an updateTrackingAreas in order to let the view install its own tracking areas
|
||||
// in the case where an externally owned tracking area is installed on this view before putting
|
||||
// it in the view hierarchy.
|
||||
[self updateTrackingAreas];
|
||||
}
|
||||
|
||||
return self;
|
||||
}
|
||||
|
||||
|
||||
/*!
|
||||
Sets the tooltip for the receiver.
|
||||
|
||||
@@ -404,7 +445,7 @@ var CPViewHighDPIDrawingEnabled = YES;
|
||||
*/
|
||||
- (void)setToolTip:(CPString)aToolTip
|
||||
{
|
||||
if (_toolTip == aToolTip)
|
||||
if (_toolTip === aToolTip)
|
||||
return;
|
||||
|
||||
if (aToolTip && ![aToolTip isKindOfClass:CPString])
|
||||
@@ -432,7 +473,7 @@ var CPViewHighDPIDrawingEnabled = YES;
|
||||
return;
|
||||
|
||||
if (!_toolTipFunctionIn)
|
||||
_toolTipFunctionIn = function(e) { [_CPToolTip scheduleToolTipForView:self]; }
|
||||
_toolTipFunctionIn = function(e) { [_CPToolTip scheduleToolTipForView:self]; };
|
||||
|
||||
if (!_toolTipFunctionOut)
|
||||
_toolTipFunctionOut = function(e) { [_CPToolTip invalidateCurrentToolTipIfNeeded]; };
|
||||
@@ -562,7 +603,7 @@ var CPViewHighDPIDrawingEnabled = YES;
|
||||
[[self window] _dirtyKeyViewLoop];
|
||||
|
||||
// If this is already one of our subviews, remove it.
|
||||
if (aSubview._superview == self)
|
||||
if (aSubview._superview === self)
|
||||
{
|
||||
var index = [_subviews indexOfObjectIdenticalTo:aSubview];
|
||||
|
||||
@@ -591,8 +632,9 @@ var CPViewHighDPIDrawingEnabled = YES;
|
||||
// Remove the view from its previous superview.
|
||||
[aSubview _removeFromSuperview];
|
||||
|
||||
[aSubview _postViewWillAppearNotification];
|
||||
// Set ourselves as the superview.
|
||||
aSubview._superview = self;
|
||||
[aSubview _setSuperview:self];
|
||||
}
|
||||
|
||||
if (anIndex === CPNotFound || anIndex >= count)
|
||||
@@ -617,11 +659,6 @@ var CPViewHighDPIDrawingEnabled = YES;
|
||||
[aSubview setNextResponder:self];
|
||||
[aSubview _scaleSizeUnitSquareToSize:[self _hierarchyScaleSize]];
|
||||
|
||||
// If the subview is not hidden and one of its ancestors is hidden,
|
||||
// notify the subview that it is now hidden.
|
||||
if (![aSubview isHidden] && [self isHiddenOrHasHiddenAncestor])
|
||||
[aSubview _notifyViewDidHide];
|
||||
|
||||
[aSubview viewDidMoveToSuperview];
|
||||
|
||||
// Set the subview's window to our own.
|
||||
@@ -682,6 +719,7 @@ var CPViewHighDPIDrawingEnabled = YES;
|
||||
[[self window] _dirtyKeyViewLoop];
|
||||
|
||||
[_superview willRemoveSubview:self];
|
||||
[self _postViewWillDisappearNotification];
|
||||
|
||||
[_superview._subviews removeObjectIdenticalTo:self];
|
||||
|
||||
@@ -691,13 +729,10 @@ var CPViewHighDPIDrawingEnabled = YES;
|
||||
|
||||
// If the view is not hidden and one of its ancestors is hidden,
|
||||
// notify the view that it is now unhidden.
|
||||
if (!_isHidden && [_superview isHiddenOrHasHiddenAncestor])
|
||||
[self _notifyViewDidUnhide];
|
||||
[self _setSuperview:nil];
|
||||
|
||||
[self _notifyWindowDidResignKey];
|
||||
[self _notifyViewDidResignFirstResponder];
|
||||
|
||||
_superview = nil;
|
||||
}
|
||||
|
||||
/*!
|
||||
@@ -805,13 +840,13 @@ var CPViewHighDPIDrawingEnabled = YES;
|
||||
[_window _noteUnregisteredDraggedTypes:_registeredDraggedTypes];
|
||||
[aWindow _noteRegisteredDraggedTypes:_registeredDraggedTypes];
|
||||
}
|
||||
|
||||
|
||||
// View must be removed from the current window viewsWithTrackingAreas
|
||||
if (_window && (_trackingAreas.length > 0))
|
||||
[_window _removeTrackingAreaView:self];
|
||||
|
||||
_window = aWindow;
|
||||
|
||||
|
||||
if (_window)
|
||||
{
|
||||
var owners;
|
||||
@@ -857,7 +892,7 @@ var CPViewHighDPIDrawingEnabled = YES;
|
||||
|
||||
do
|
||||
{
|
||||
if (view == aView)
|
||||
if (view === aView)
|
||||
return YES;
|
||||
} while(view = [view superview])
|
||||
|
||||
@@ -972,7 +1007,7 @@ var CPViewHighDPIDrawingEnabled = YES;
|
||||
|
||||
- (CPView)viewWithTag:(CPInteger)aTag
|
||||
{
|
||||
if ([self tag] == aTag)
|
||||
if ([self tag] === aTag)
|
||||
return self;
|
||||
|
||||
var index = 0,
|
||||
@@ -1007,7 +1042,7 @@ var CPViewHighDPIDrawingEnabled = YES;
|
||||
*/
|
||||
- (void)setFrame:(CGRect)aFrame
|
||||
{
|
||||
if (CGRectEqualToRect(_frame, aFrame))
|
||||
if (CGRectEqualToRect(_frame, aFrame) && !_forceUpdates)
|
||||
return;
|
||||
|
||||
_inhibitFrameAndBoundsChangedNotifications = YES;
|
||||
@@ -1024,7 +1059,7 @@ var CPViewHighDPIDrawingEnabled = YES;
|
||||
[[self superview] viewFrameChanged:[[CPNotification alloc] initWithName:CPViewFrameDidChangeNotification object:self userInfo:nil]];
|
||||
|
||||
if (!_inhibitUpdateTrackingAreas)
|
||||
[self _updateTrackingAreas];
|
||||
[self _updateTrackingAreasWithRecursion:YES];
|
||||
}
|
||||
|
||||
/*!
|
||||
@@ -1078,7 +1113,7 @@ var CPViewHighDPIDrawingEnabled = YES;
|
||||
{
|
||||
var origin = _frame.origin;
|
||||
|
||||
if (!aPoint || CGPointEqualToPoint(origin, aPoint))
|
||||
if (!aPoint || (CGPointEqualToPoint(origin, aPoint) && !_forceUpdates))
|
||||
return;
|
||||
|
||||
origin.x = aPoint.x;
|
||||
@@ -1091,13 +1126,16 @@ var CPViewHighDPIDrawingEnabled = YES;
|
||||
[[self superview] viewFrameChanged:[[CPNotification alloc] initWithName:CPViewFrameDidChangeNotification object:self userInfo:nil]];
|
||||
|
||||
#if PLATFORM(DOM)
|
||||
var transform = _superview ? _superview._boundsTransform : NULL;
|
||||
if (!_inhibitDOMUpdates)
|
||||
{
|
||||
var transform = _superview ? _superview._boundsTransform : NULL;
|
||||
|
||||
CPDOMDisplayServerSetStyleLeftTop(_DOMElement, transform, origin.x, origin.y);
|
||||
CPDOMDisplayServerSetStyleLeftTop(_DOMElement, transform, origin.x, origin.y);
|
||||
}
|
||||
#endif
|
||||
|
||||
if (!_inhibitUpdateTrackingAreas && !_inhibitFrameAndBoundsChangedNotifications)
|
||||
[self _updateTrackingAreas];
|
||||
[self _updateTrackingAreasWithRecursion:YES];
|
||||
}
|
||||
|
||||
/*!
|
||||
@@ -1110,7 +1148,7 @@ var CPViewHighDPIDrawingEnabled = YES;
|
||||
{
|
||||
var size = _frame.size;
|
||||
|
||||
if (!aSize || CGSizeEqualToSize(size, aSize))
|
||||
if (!aSize || (CGSizeEqualToSize(size, aSize) && !_forceUpdates))
|
||||
return;
|
||||
|
||||
var oldSize = CGSizeMakeCopy(size);
|
||||
@@ -1251,7 +1289,7 @@ var CPViewHighDPIDrawingEnabled = YES;
|
||||
[[self superview] viewFrameChanged:[[CPNotification alloc] initWithName:CPViewFrameDidChangeNotification object:self userInfo:nil]];
|
||||
|
||||
if (!_inhibitUpdateTrackingAreas && !_inhibitFrameAndBoundsChangedNotifications)
|
||||
[self _updateTrackingAreas];
|
||||
[self _updateTrackingAreasWithRecursion:!_autoresizesSubviews];
|
||||
}
|
||||
|
||||
/*!
|
||||
@@ -1264,7 +1302,8 @@ var CPViewHighDPIDrawingEnabled = YES;
|
||||
#if PLATFORM(DOM)
|
||||
var scale = [self scaleSize];
|
||||
|
||||
CPDOMDisplayServerSetStyleSize(_DOMElement, aSize.width * 1 / scale.width, aSize.height * 1 / scale.height);
|
||||
if (!_inhibitDOMUpdates)
|
||||
CPDOMDisplayServerSetStyleSize(_DOMElement, aSize.width * 1 / scale.width, aSize.height * 1 / scale.height);
|
||||
|
||||
if (_DOMContentsElement)
|
||||
{
|
||||
@@ -1300,7 +1339,7 @@ var CPViewHighDPIDrawingEnabled = YES;
|
||||
[[self superview] viewBoundsChanged:[[CPNotification alloc] initWithName:CPViewBoundsDidChangeNotification object:self userInfo:nil]];
|
||||
|
||||
if (!_inhibitUpdateTrackingAreas)
|
||||
[self _updateTrackingAreas];
|
||||
[self _updateTrackingAreasWithRecursion:YES];
|
||||
}
|
||||
|
||||
/*!
|
||||
@@ -1368,7 +1407,7 @@ var CPViewHighDPIDrawingEnabled = YES;
|
||||
[[self superview] viewBoundsChanged:[[CPNotification alloc] initWithName:CPViewBoundsDidChangeNotification object:self userInfo:nil]];
|
||||
|
||||
if (!_inhibitUpdateTrackingAreas && !_inhibitFrameAndBoundsChangedNotifications)
|
||||
[self _updateTrackingAreas];
|
||||
[self _updateTrackingAreasWithRecursion:YES];
|
||||
}
|
||||
|
||||
/*!
|
||||
@@ -1412,7 +1451,7 @@ var CPViewHighDPIDrawingEnabled = YES;
|
||||
[[self superview] viewBoundsChanged:[[CPNotification alloc] initWithName:CPViewBoundsDidChangeNotification object:self userInfo:nil]];
|
||||
|
||||
if (!_inhibitUpdateTrackingAreas && !_inhibitFrameAndBoundsChangedNotifications)
|
||||
[self _updateTrackingAreas];
|
||||
[self _updateTrackingAreasWithRecursion:YES];
|
||||
}
|
||||
|
||||
|
||||
@@ -1424,7 +1463,7 @@ var CPViewHighDPIDrawingEnabled = YES;
|
||||
{
|
||||
var mask = [self autoresizingMask];
|
||||
|
||||
if (mask == CPViewNotSizable)
|
||||
if (mask === CPViewNotSizable)
|
||||
return;
|
||||
|
||||
var frame = _superview._frame,
|
||||
@@ -1593,10 +1632,8 @@ var CPViewHighDPIDrawingEnabled = YES;
|
||||
|
||||
// FIXME: Should we return to visibility? This breaks in FireFox, Opera, and IE.
|
||||
// _DOMElement.style.visibility = (_isHidden = aFlag) ? "hidden" : "visible";
|
||||
_isHidden = aFlag;
|
||||
|
||||
#if PLATFORM(DOM)
|
||||
_DOMElement.style.display = _isHidden ? "none" : "block";
|
||||
_DOMElement.style.display = aFlag ? "none" : "block";
|
||||
#endif
|
||||
|
||||
if (aFlag)
|
||||
@@ -1607,7 +1644,7 @@ var CPViewHighDPIDrawingEnabled = YES;
|
||||
{
|
||||
do
|
||||
{
|
||||
if (self == view)
|
||||
if (self === view)
|
||||
{
|
||||
[_window makeFirstResponder:[self nextValidKeyView]];
|
||||
break;
|
||||
@@ -1616,33 +1653,89 @@ var CPViewHighDPIDrawingEnabled = YES;
|
||||
while (view = [view superview]);
|
||||
}
|
||||
|
||||
[self _notifyViewDidHide];
|
||||
[self _postViewWillDisappearNotification];
|
||||
[self _recursiveGainedHiddenAncestor];
|
||||
}
|
||||
else
|
||||
{
|
||||
[self setNeedsDisplay:YES];
|
||||
[self _notifyViewDidUnhide];
|
||||
|
||||
[self _postViewWillAppearNotification];
|
||||
[self _recursiveLostHiddenAncestor];
|
||||
}
|
||||
|
||||
_isHidden = aFlag;
|
||||
}
|
||||
|
||||
- (void)_notifyViewDidHide
|
||||
- (void)_postViewWillAppearNotification
|
||||
{
|
||||
[self viewDidHide];
|
||||
|
||||
var count = [_subviews count];
|
||||
|
||||
while (count--)
|
||||
[_subviews[count] _notifyViewDidHide];
|
||||
[[CPNotificationCenter defaultCenter] postNotificationName:_CPViewWillAppearNotification object:self userInfo:nil];
|
||||
}
|
||||
|
||||
- (void)_notifyViewDidUnhide
|
||||
- (void)_postViewDidAppearNotification
|
||||
{
|
||||
[self viewDidUnhide];
|
||||
[[CPNotificationCenter defaultCenter] postNotificationName:_CPViewDidAppearNotification object:self userInfo:nil];
|
||||
}
|
||||
|
||||
var count = [_subviews count];
|
||||
- (void)_postViewWillDisappearNotification
|
||||
{
|
||||
[[CPNotificationCenter defaultCenter] postNotificationName:_CPViewWillDisappearNotification object:self userInfo:nil];
|
||||
}
|
||||
|
||||
while (count--)
|
||||
[_subviews[count] _notifyViewDidUnhide];
|
||||
- (void)_postViewDidDisappearNotification
|
||||
{
|
||||
[[CPNotificationCenter defaultCenter] postNotificationName:_CPViewDidDisappearNotification object:self userInfo:nil];
|
||||
}
|
||||
|
||||
- (void)_setSuperview:(CPView)aSuperview
|
||||
{
|
||||
var hasOldSuperview = (_superview !== nil),
|
||||
hasNewSuperview = (aSuperview !== nil),
|
||||
oldSuperviewIsHidden = hasOldSuperview && [_superview isHiddenOrHasHiddenAncestor],
|
||||
newSuperviewIsHidden = hasNewSuperview && [aSuperview isHiddenOrHasHiddenAncestor];
|
||||
|
||||
if (!newSuperviewIsHidden && oldSuperviewIsHidden)
|
||||
[self _recursiveLostHiddenAncestor];
|
||||
|
||||
if (newSuperviewIsHidden && !oldSuperviewIsHidden)
|
||||
[self _recursiveGainedHiddenAncestor];
|
||||
|
||||
_superview = aSuperview;
|
||||
|
||||
if (hasOldSuperview)
|
||||
[self _postViewDidDisappearNotification];
|
||||
|
||||
if (hasNewSuperview)
|
||||
[self _postViewDidAppearNotification];
|
||||
}
|
||||
|
||||
- (void)_recursiveLostHiddenAncestor
|
||||
{
|
||||
if (_isHiddenOrHasHiddenAncestor)
|
||||
{
|
||||
_isHiddenOrHasHiddenAncestor = NO;
|
||||
[self viewDidUnhide];
|
||||
}
|
||||
|
||||
[_subviews enumerateObjectsUsingBlock:function(view, idx, stop)
|
||||
{
|
||||
[view _recursiveLostHiddenAncestor];
|
||||
}];
|
||||
}
|
||||
|
||||
- (void)_recursiveGainedHiddenAncestor
|
||||
{
|
||||
if (!_isHidden)
|
||||
{
|
||||
[self viewDidHide];
|
||||
}
|
||||
|
||||
_isHiddenOrHasHiddenAncestor = YES;
|
||||
|
||||
[_subviews enumerateObjectsUsingBlock:function(view, idx, stop)
|
||||
{
|
||||
[view _recursiveGainedHiddenAncestor];
|
||||
}];
|
||||
}
|
||||
|
||||
/*!
|
||||
@@ -1677,7 +1770,7 @@ var CPViewHighDPIDrawingEnabled = YES;
|
||||
*/
|
||||
- (void)setAlphaValue:(float)anAlphaValue
|
||||
{
|
||||
if (_opacity == anAlphaValue)
|
||||
if (_opacity === anAlphaValue)
|
||||
return;
|
||||
|
||||
_opacity = anAlphaValue;
|
||||
@@ -1712,12 +1805,7 @@ var CPViewHighDPIDrawingEnabled = YES;
|
||||
*/
|
||||
- (BOOL)isHiddenOrHasHiddenAncestor
|
||||
{
|
||||
var view = self;
|
||||
|
||||
while (view && ![view isHidden])
|
||||
view = [view superview];
|
||||
|
||||
return view !== nil;
|
||||
return _isHiddenOrHasHiddenAncestor;
|
||||
}
|
||||
|
||||
/*!
|
||||
@@ -1881,24 +1969,34 @@ var CPViewHighDPIDrawingEnabled = YES;
|
||||
*/
|
||||
- (void)setBackgroundColor:(CPColor)aColor
|
||||
{
|
||||
if (_backgroundColor == aColor)
|
||||
if (_backgroundColor === aColor)
|
||||
return;
|
||||
|
||||
if (aColor == [CPNull null])
|
||||
if (aColor === [CPNull null])
|
||||
aColor = nil;
|
||||
|
||||
_backgroundColor = aColor;
|
||||
|
||||
#if PLATFORM(DOM)
|
||||
if (_backgroundType === BackgroundCSSStyling)
|
||||
[_backgroundColor restorePreviousCSSState:@ref(_cssStylePreviousState) forDOMElement:_DOMElement];
|
||||
|
||||
var patternImage = [_backgroundColor patternImage],
|
||||
colorExists = _backgroundColor && ([_backgroundColor patternImage] || [_backgroundColor alphaComponent] > 0.0),
|
||||
colorHasAlpha = colorExists && [_backgroundColor alphaComponent] < 1.0,
|
||||
supportsRGBA = CPFeatureIsCompatible(CPCSSRGBAFeature),
|
||||
colorNeedsDOMElement = colorHasAlpha && !supportsRGBA,
|
||||
amount = 0,
|
||||
slices;
|
||||
slices,
|
||||
// For CSS theming
|
||||
isCSSBasedColor = [_backgroundColor isCSSBased];
|
||||
|
||||
if ([patternImage isThreePartImage])
|
||||
if (isCSSBasedColor)
|
||||
{
|
||||
_backgroundType = BackgroundCSSStyling;
|
||||
amount = -_DOMImageParts.length;
|
||||
}
|
||||
else if ([patternImage isThreePartImage])
|
||||
{
|
||||
_backgroundType = [patternImage isVertical] ? BackgroundVerticalThreePartImage : BackgroundHorizontalThreePartImage;
|
||||
amount = 3;
|
||||
@@ -1927,7 +2025,7 @@ var CPViewHighDPIDrawingEnabled = YES;
|
||||
var image = slices[i],
|
||||
size = [image size];
|
||||
|
||||
if (!size || (size.width == 0 && size.height == 0))
|
||||
if (!size || (size.width === 0 && size.height === 0))
|
||||
size = nil;
|
||||
|
||||
_DOMImageSizes[i] = size;
|
||||
@@ -1961,7 +2059,14 @@ var CPViewHighDPIDrawingEnabled = YES;
|
||||
_DOMElement.removeChild(_DOMImageParts.pop());
|
||||
}
|
||||
|
||||
if (_backgroundType === BackgroundTrivialColor || _backgroundType === BackgroundTransparentColor)
|
||||
if (_backgroundType === BackgroundCSSStyling)
|
||||
{
|
||||
_cssStyleNode = [_backgroundColor applyCSSColorForView:self
|
||||
onDOMElement:_DOMElement
|
||||
styleNode:_cssStyleNode
|
||||
previousState:@ref(_cssStylePreviousState)];
|
||||
}
|
||||
else if (_backgroundType === BackgroundTrivialColor || _backgroundType === BackgroundTransparentColor)
|
||||
{
|
||||
var colorCSS = colorExists ? [_backgroundColor cssString] : "";
|
||||
|
||||
@@ -1982,10 +2087,12 @@ var CPViewHighDPIDrawingEnabled = YES;
|
||||
CPDOMDisplayServerSetStyleSize(_DOMImageParts[0], size.width, size.height);
|
||||
}
|
||||
else
|
||||
{
|
||||
_DOMElement.style.background = colorCSS;
|
||||
|
||||
if (patternImage)
|
||||
CPDOMDisplayServerSetStyleBackgroundSize(_DOMElement, [patternImage size].width + "px", [patternImage size].height + "px");
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
@@ -2019,7 +2126,7 @@ var CPViewHighDPIDrawingEnabled = YES;
|
||||
partIndex++;
|
||||
}
|
||||
|
||||
if (_backgroundType == BackgroundNinePartImage)
|
||||
if (_backgroundType === BackgroundNinePartImage)
|
||||
{
|
||||
var left = _DOMImageSizes[0] ? _DOMImageSizes[0].width : 0,
|
||||
right = _DOMImageSizes[2] ? _DOMImageSizes[2].width : 0,
|
||||
@@ -2080,7 +2187,7 @@ var CPViewHighDPIDrawingEnabled = YES;
|
||||
CPDOMDisplayServerSetStyleRightBottom(_DOMImageParts[partIndex], NULL, 0.0, 0.0);
|
||||
}
|
||||
}
|
||||
else if (_backgroundType == BackgroundVerticalThreePartImage)
|
||||
else if (_backgroundType === BackgroundVerticalThreePartImage)
|
||||
{
|
||||
var top = _DOMImageSizes[0] ? _DOMImageSizes[0].height : 0,
|
||||
bottom = _DOMImageSizes[2] ? _DOMImageSizes[2].height : 0;
|
||||
@@ -2112,7 +2219,7 @@ var CPViewHighDPIDrawingEnabled = YES;
|
||||
CPDOMDisplayServerSetStyleSize(_DOMImageParts[partIndex], frameSize.width, bottom);
|
||||
}
|
||||
}
|
||||
else if (_backgroundType == BackgroundHorizontalThreePartImage)
|
||||
else if (_backgroundType === BackgroundHorizontalThreePartImage)
|
||||
{
|
||||
var left = _DOMImageSizes[0] ? _DOMImageSizes[0].width : 0,
|
||||
right = _DOMImageSizes[2] ? _DOMImageSizes[2].width : 0;
|
||||
@@ -2851,8 +2958,8 @@ setBoundsOrigin:
|
||||
return YES;
|
||||
}
|
||||
|
||||
/*
|
||||
FIXME Not yet implemented
|
||||
/*!
|
||||
Scrolls the view’s CPClipView in the direction of a mouse event that occurs outside of it.
|
||||
*/
|
||||
- (BOOL)autoscroll:(CPEvent)anEvent
|
||||
{
|
||||
@@ -2917,7 +3024,7 @@ setBoundsOrigin:
|
||||
*/
|
||||
- (BOOL)inLiveResize
|
||||
{
|
||||
return _inLiveResize;
|
||||
return [self window] && [[self window] _inLiveResize];
|
||||
}
|
||||
|
||||
/*!
|
||||
@@ -3074,7 +3181,7 @@ setBoundsOrigin:
|
||||
*/
|
||||
- (void)setLayer:(CALayer)aLayer
|
||||
{
|
||||
if (_layer == aLayer)
|
||||
if (_layer === aLayer)
|
||||
return;
|
||||
|
||||
if (_layer)
|
||||
@@ -3368,6 +3475,17 @@ setBoundsOrigin:
|
||||
|
||||
@end
|
||||
|
||||
@implementation CPView (CSSTheming)
|
||||
|
||||
- (void)setDOMClassName:(CPString)aClassName
|
||||
{
|
||||
#if PLATFORM(DOM)
|
||||
_DOMElement.className = aClassName;
|
||||
#endif
|
||||
}
|
||||
|
||||
@end
|
||||
|
||||
|
||||
@implementation CPView (Appearance)
|
||||
|
||||
@@ -3459,16 +3577,16 @@ setBoundsOrigin:
|
||||
// Consistency check
|
||||
if (!trackingArea || [_trackingAreas containsObjectIdenticalTo:trackingArea])
|
||||
return;
|
||||
|
||||
|
||||
if ([trackingArea view])
|
||||
[CPException raise:CPInternalInconsistencyException reason:"Tracking area has already been added to another view."];
|
||||
|
||||
[_trackingAreas addObject:trackingArea];
|
||||
[trackingArea setView:self];
|
||||
|
||||
|
||||
if (_window)
|
||||
[_window _addTrackingArea:trackingArea];
|
||||
|
||||
|
||||
[trackingArea _updateWindowRect];
|
||||
}
|
||||
|
||||
@@ -3477,25 +3595,26 @@ setBoundsOrigin:
|
||||
// Consistency check
|
||||
if (!trackingArea)
|
||||
return;
|
||||
|
||||
|
||||
if (![_trackingAreas containsObjectIdenticalTo:trackingArea])
|
||||
[CPException raise:CPInternalInconsistencyException reason:"Trying to remove unreferenced trackingArea"];
|
||||
|
||||
[self _removeTrackingArea:trackingArea];
|
||||
}
|
||||
|
||||
/*!
|
||||
/*!
|
||||
Invoked automatically when the view’s geometry changes such that its tracking areas need to be recalculated.
|
||||
|
||||
You should override this method to remove out of date tracking areas and add recomputed tracking areas;
|
||||
|
||||
You should override this method to remove out of date tracking areas, add recomputed tracking areas and then call super;
|
||||
|
||||
Cocoa calls this on every view, whereas they have tracking area(s) or not.
|
||||
Cappuccino behaves differently :
|
||||
- updateTrackingAreas is called when placing a view in the view hierarchy (that is in a window)
|
||||
- updateTrackingAreas is called during initWithFrame
|
||||
- updateTrackingAreas is also called when placing a view in the view hierarchy (that is in a window)
|
||||
- if you have only CPTrackingInVisibleRect tracking areas attached to a view, it will not be called again (until you move the view in the hierarchy)
|
||||
- if you have at least one non-CPTrackingInVisibleRect tracking area attached, it will be called every time the view geometry could be modified
|
||||
You don't have to touch to CPTrackingInVisibleRect tracking areas, they will be automatically updated
|
||||
|
||||
|
||||
Please note that it is the owner of a tracking area who is called for updateTrackingAreas.
|
||||
But, if a view without any tracking area is inserted in the view hierarchy (that is, in a window), the view is called for updateTrackingAreas.
|
||||
This enables you to use updateTrackingArea to initially attach your tracking areas to the view.
|
||||
@@ -3505,51 +3624,32 @@ setBoundsOrigin:
|
||||
|
||||
}
|
||||
|
||||
/*!
|
||||
This utility method is intended for CPView subclasses overriding updateTrackingAreas
|
||||
|
||||
Typical use would be :
|
||||
|
||||
- (void)updateTrackingAreas
|
||||
{
|
||||
[self removeAllTrackingAreas];
|
||||
|
||||
... add your specific updated tracking areas ...
|
||||
}
|
||||
|
||||
*/
|
||||
- (void)removeAllTrackingAreas
|
||||
{
|
||||
while (_trackingAreas.length > 0)
|
||||
[self _removeTrackingArea:_trackingAreas[0]];
|
||||
}
|
||||
|
||||
// Internal methods
|
||||
|
||||
- (void)_removeTrackingArea:(CPTrackingArea)trackingArea
|
||||
{
|
||||
if (_window)
|
||||
[_window _removeTrackingArea:trackingArea];
|
||||
|
||||
|
||||
[trackingArea setView:nil];
|
||||
[_trackingAreas removeObjectIdenticalTo:trackingArea];
|
||||
}
|
||||
|
||||
- (void)_updateTrackingAreas
|
||||
- (void)_updateTrackingAreasWithRecursion:(BOOL)shouldCallRecursively
|
||||
{
|
||||
_inhibitUpdateTrackingAreas = YES;
|
||||
|
||||
[self _recursivelyUpdateTrackingAreas];
|
||||
|
||||
_inhibitUpdateTrackingAreas = NO;
|
||||
}
|
||||
|
||||
- (void)_recursivelyUpdateTrackingAreas
|
||||
{
|
||||
[self _updateTrackingAreasForOwners:[self _calcTrackingAreaOwners]];
|
||||
|
||||
for (var i = 0; i < _subviews.length; i++)
|
||||
[_subviews[i] _recursivelyUpdateTrackingAreas];
|
||||
|
||||
if (shouldCallRecursively)
|
||||
{
|
||||
// Now, call _updateTrackingAreasWithRecursion on subviews
|
||||
|
||||
for (var i = 0; i < _subviews.length; i++)
|
||||
[_subviews[i] _updateTrackingAreasWithRecursion:YES];
|
||||
}
|
||||
|
||||
_inhibitUpdateTrackingAreas = NO;
|
||||
}
|
||||
|
||||
- (CPArray)_calcTrackingAreaOwners
|
||||
@@ -3557,25 +3657,25 @@ setBoundsOrigin:
|
||||
// First search all owners that must be notified
|
||||
// Remark: 99.99% of time, the only owner will be the view itself
|
||||
// In the same time, update the rects of InVisibleRect tracking areas
|
||||
|
||||
|
||||
var owners = [];
|
||||
|
||||
|
||||
for (var i = 0; i < _trackingAreas.length; i++)
|
||||
{
|
||||
var trackingArea = _trackingAreas[i];
|
||||
|
||||
|
||||
if ([trackingArea options] & CPTrackingInVisibleRect)
|
||||
[trackingArea _updateWindowRect];
|
||||
|
||||
|
||||
else
|
||||
{
|
||||
var owner = [trackingArea owner];
|
||||
|
||||
|
||||
if (![owners containsObjectIdenticalTo:owner])
|
||||
[owners addObject:owner];
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
return owners;
|
||||
}
|
||||
|
||||
@@ -3606,7 +3706,7 @@ var CPViewAutoresizingMaskKey = @"CPViewAutoresizingMask",
|
||||
CPViewScaleKey = @"CPViewScaleKey",
|
||||
CPViewSizeScaleKey = @"CPViewSizeScaleKey",
|
||||
CPViewIsScaledKey = @"CPViewIsScaledKey",
|
||||
CPViewAppearanceKey = @"CPViewAppearanceKey";
|
||||
CPViewAppearanceKey = @"CPViewAppearanceKey",
|
||||
CPViewTrackingAreasKey = @"CPViewTrackingAreasKey";
|
||||
|
||||
@implementation CPView (CPCoding)
|
||||
@@ -3636,17 +3736,16 @@ var CPViewAutoresizingMaskKey = @"CPViewAutoresizingMask",
|
||||
if (self)
|
||||
{
|
||||
_trackingAreas = [aCoder decodeObjectForKey:CPViewTrackingAreasKey];
|
||||
|
||||
|
||||
if (!_trackingAreas)
|
||||
_trackingAreas = [];
|
||||
|
||||
|
||||
// We have to manually check because it may be 0, so we can't use ||
|
||||
_tag = [aCoder containsValueForKey:CPViewTagKey] ? [aCoder decodeIntForKey:CPViewTagKey] : -1;
|
||||
_identifier = [aCoder decodeObjectForKey:CPReuseIdentifierKey];
|
||||
|
||||
_window = [aCoder decodeObjectForKey:CPViewWindowKey];
|
||||
_superview = [aCoder decodeObjectForKey:CPViewSuperviewKey];
|
||||
|
||||
// We have to manually add the subviews so that they will receive
|
||||
// viewWillMoveToSuperview: and viewDidMoveToSuperview:
|
||||
_subviews = [];
|
||||
@@ -3684,6 +3783,8 @@ var CPViewAutoresizingMaskKey = @"CPViewAutoresizingMask",
|
||||
|
||||
// DOM SETUP
|
||||
#if PLATFORM(DOM)
|
||||
_cssStylePreviousState = @[];
|
||||
|
||||
_DOMImageParts = [];
|
||||
_DOMImageSizes = [];
|
||||
|
||||
@@ -3701,6 +3802,7 @@ var CPViewAutoresizingMaskKey = @"CPViewAutoresizingMask",
|
||||
#endif
|
||||
|
||||
[self setHidden:[aCoder decodeBoolForKey:CPViewIsHiddenKey]];
|
||||
_isHiddenOrHasHiddenAncestor = NO;
|
||||
|
||||
if ([aCoder containsValueForKey:CPViewOpacityKey])
|
||||
[self setAlphaValue:[aCoder decodeIntForKey:CPViewOpacityKey]];
|
||||
|
||||
+174
-2
@@ -149,9 +149,10 @@ var CPViewControllerCachedCibs;
|
||||
|
||||
If you use Interface Builder to create your views, and you initialize the
|
||||
controller using the initWithCibName:bundle: methods, then you MUST NOT override
|
||||
this method. The consequences risk shattering the space-time continuum.
|
||||
this method.
|
||||
|
||||
Note: The cib loading system is currently synchronous.
|
||||
@note When using this method, the cib loading system is synchronous.
|
||||
See the loadViewWithCompletionHandler: method for an asynchronous loading.
|
||||
*/
|
||||
- (void)loadView
|
||||
{
|
||||
@@ -176,6 +177,67 @@ var CPViewControllerCachedCibs;
|
||||
_view = [CPView new];
|
||||
}
|
||||
|
||||
/*!
|
||||
Asynchronously load the cib and create the view that the controller manages.
|
||||
|
||||
@param aHandler a function which will be passed the loaded view as the first
|
||||
argument and a network error or nil as the second argument: function(view, error).
|
||||
|
||||
@note If the view has already been loaded, the completion handler is run immediatly
|
||||
and the process is synchronous.
|
||||
*/
|
||||
- (void)loadViewWithCompletionHandler:(Function/*(view, error)*/)aHandler
|
||||
{
|
||||
if (_view)
|
||||
return;
|
||||
|
||||
if (_cibName)
|
||||
{
|
||||
// check if a cib is already cached for the current _cibName
|
||||
var cib = [CPViewControllerCachedCibs objectForKey:_cibName];
|
||||
|
||||
if (!cib)
|
||||
{
|
||||
var cibName = _cibName;
|
||||
|
||||
if (![cibName hasSuffix:@".cib"])
|
||||
cibName = [cibName stringByAppendingString:@".cib"];
|
||||
|
||||
// If aBundle is nil, use mainBundle, but ONLY for searching for the nib, not for resources later.
|
||||
var bundle = _cibBundle || [CPBundle mainBundle],
|
||||
url = [bundle _cibPathForResource:cibName];
|
||||
|
||||
// if the cib isn't cached yet : fetch it and cache it
|
||||
[CPURLConnection sendAsynchronousRequest:[CPURLRequest requestWithURL:url] queue:[CPOperationQueue mainQueue] completionHandler:function(aResponse, aData, anError)
|
||||
{
|
||||
if (anError == nil)
|
||||
{
|
||||
var data = [CPData dataWithRawString:aData],
|
||||
aCib = [[CPCib alloc] _initWithData:data bundle:_cibBundle cibName:_cibName];
|
||||
|
||||
[CPViewControllerCachedCibs setObject:aCib forKey:_cibName];
|
||||
[aCib instantiateCibWithExternalNameTable:_cibExternalNameTable];
|
||||
[self _viewDidLoadWithCompletionHandler:aHandler];
|
||||
}
|
||||
else
|
||||
{
|
||||
aHandler(nil, anError);
|
||||
}
|
||||
}];
|
||||
}
|
||||
else
|
||||
{
|
||||
[cib instantiateCibWithExternalNameTable:_cibExternalNameTable];
|
||||
[self _viewDidLoadWithCompletionHandler:aHandler];
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
_view = [CPView new];
|
||||
[self _viewDidLoadWithCompletionHandler:aHandler];
|
||||
}
|
||||
}
|
||||
|
||||
/*!
|
||||
Returns the view that the controller manages.
|
||||
|
||||
@@ -230,6 +292,16 @@ var CPViewControllerCachedCibs;
|
||||
[self didChangeValueForKey:"isViewLoaded"];
|
||||
}
|
||||
|
||||
- (void)_viewDidLoadWithCompletionHandler:(Function)aHandler
|
||||
{
|
||||
[self _registerOrUnregister:YES notificationsForView:_view];
|
||||
|
||||
[self willChangeValueForKey:"isViewLoaded"];
|
||||
aHandler(_view, nil);
|
||||
_isViewLoaded = YES;
|
||||
[self didChangeValueForKey:"isViewLoaded"];
|
||||
}
|
||||
|
||||
/*!
|
||||
This method is called after the view controller has loaded its associated views into memory.
|
||||
|
||||
@@ -243,6 +315,79 @@ var CPViewControllerCachedCibs;
|
||||
|
||||
}
|
||||
|
||||
/*!
|
||||
Called after the view controller’s view has been loaded into memory is about to be added to the
|
||||
view hierarchy in the window.
|
||||
|
||||
@discussion You can override this method to perform tasks prior to a view controller’s view
|
||||
getting added to view hierarchy, such as setting the view’s highlight color. This method is called when:
|
||||
|
||||
• The view is about to be added to the view hierarchy of the view controller
|
||||
|
||||
If you override this method, call this method on super at some point in your implementation in case
|
||||
a superclass also overrides this method.
|
||||
|
||||
The default implementation of this method does nothing.
|
||||
*/
|
||||
- (void)viewWillAppear
|
||||
{
|
||||
|
||||
}
|
||||
|
||||
/*!
|
||||
Called when the view controller’s view is fully transitioned onto the screen.
|
||||
|
||||
@discussion This method is called after the completion of any drawing and animations
|
||||
involved in the initial appearance of the view. You can override this method to
|
||||
perform tasks appropriate for that time, such as work that should not interfere
|
||||
with the presentation animation, or starting an animation that you want to begin
|
||||
after the view appears.
|
||||
|
||||
If you override this method, call this method on super at some point in your
|
||||
implementation in case a superclass also overrides this method.
|
||||
|
||||
The default implementation of this method does nothing.
|
||||
*/
|
||||
- (void)viewDidAppear
|
||||
{
|
||||
|
||||
}
|
||||
|
||||
/*!
|
||||
Called when the view controller’s view is about to be removed from the view hierarchy in the window.
|
||||
|
||||
@discussion You can override this method to perform tasks that are to precede the disappearance
|
||||
of the view controller’s view, such as stopping a continuous animation that you
|
||||
started in response to the viewDidAppear method call. This method is called when:
|
||||
|
||||
• The view is about to be removed from the view hierarchy of the window
|
||||
|
||||
If you override this method, call this method on super at some point in your
|
||||
implementation in case a superclass also overrides this method.
|
||||
|
||||
The default implementation of this method does nothing.
|
||||
*/
|
||||
- (void)viewWillDisappear
|
||||
{
|
||||
|
||||
}
|
||||
|
||||
/*!
|
||||
Called after the view controller’s view is removed from the view hierarchy in a window.
|
||||
|
||||
@discussion You can override this method to perform tasks associated with removing the view
|
||||
controller’s view from the window’s view hierarchy, such as releasing resources
|
||||
not needed when the view is not visible or no longer part of the window.
|
||||
|
||||
If you override this method, call this method on super at some point in your
|
||||
implementation in case a superclass also overrides this method.
|
||||
|
||||
The default implementation of this method does nothing.
|
||||
*/
|
||||
- (void)viewDidDisappear
|
||||
{
|
||||
|
||||
}
|
||||
|
||||
/*!
|
||||
Manually sets the view that the controller manages.
|
||||
@@ -256,6 +401,9 @@ var CPViewControllerCachedCibs;
|
||||
{
|
||||
var willChangeIsViewLoaded = (_isViewLoaded == NO && aView != nil) || (_isViewLoaded == YES && aView == nil);
|
||||
|
||||
[self _registerOrUnregister:NO notificationsForView:_view];
|
||||
[self _registerOrUnregister:YES notificationsForView:aView];
|
||||
|
||||
if (willChangeIsViewLoaded)
|
||||
[self willChangeValueForKey:"isViewLoaded"];
|
||||
|
||||
@@ -271,6 +419,30 @@ var CPViewControllerCachedCibs;
|
||||
return NO;
|
||||
}
|
||||
|
||||
- (void)_registerOrUnregister:(BOOL)shouldRegister notificationsForView:(CPView)aView
|
||||
{
|
||||
if (aView === nil)
|
||||
return;
|
||||
|
||||
var center = [CPNotificationCenter defaultCenter],
|
||||
notifs_to_sel = @{_CPViewWillAppearNotification : @"viewWillAppear",
|
||||
_CPViewDidAppearNotification : @"viewDidAppear",
|
||||
_CPViewWillDisappearNotification : @"viewWillDisappear",
|
||||
_CPViewDidDisappearNotification : @"viewDidDisappear"};
|
||||
|
||||
[notifs_to_sel enumerateKeysAndObjectsUsingBlock:function(notif, selString, stop)
|
||||
{
|
||||
var selector = CPSelectorFromString(selString);
|
||||
if ([self implementsSelector:selector])
|
||||
{
|
||||
if (shouldRegister)
|
||||
[center addObserver:self selector:selector name:notif object:aView];
|
||||
else
|
||||
[center removeObserver:self name:notif object:aView];
|
||||
}
|
||||
}];
|
||||
}
|
||||
|
||||
@end
|
||||
|
||||
|
||||
|
||||
+315
-174
@@ -54,6 +54,7 @@
|
||||
@class CPToolbar
|
||||
@class CPWindowController
|
||||
@class _CPWindowFrameAnimation
|
||||
@class _CPWindowFrameAnimationDelegate
|
||||
|
||||
@global CPApp
|
||||
@global _CPPlatformWindowWillCloseNotification
|
||||
@@ -178,7 +179,7 @@ var CPWindowActionMessageKeys = [
|
||||
BOOL _isVisible;
|
||||
BOOL _hasBeenOrderedIn @accessors;
|
||||
BOOL _isMiniaturized;
|
||||
BOOL _isAnimating;
|
||||
BOOL _isAnimating @accessors(setter=_setAnimating:);
|
||||
BOOL _hasShadow;
|
||||
BOOL _isMovableByWindowBackground;
|
||||
BOOL _isMovable;
|
||||
@@ -198,8 +199,13 @@ var CPWindowActionMessageKeys = [
|
||||
CPView _contentView;
|
||||
CPView _toolbarView;
|
||||
|
||||
BOOL _handlingTrackingAreaEvent;
|
||||
BOOL _restartHandlingTrackingAreaEvent;
|
||||
CPArray _previousMouseEnteredStack;
|
||||
CPArray _previousCursorUpdateStack;
|
||||
CPArray _mouseEnteredStack;
|
||||
CPArray _cursorUpdateStack;
|
||||
CPArray _queuedEvents;
|
||||
CPArray _trackingAreaViews;
|
||||
id _activeCursorTrackingArea;
|
||||
CPArray _queuedTrackingEvents;
|
||||
@@ -256,6 +262,7 @@ var CPWindowActionMessageKeys = [
|
||||
CPWindow _parentView;
|
||||
BOOL _isSheet;
|
||||
_CPWindowFrameAnimation _frameAnimation;
|
||||
_CPWindowFrameAnimationDelegate _frameAnimationDelegate;
|
||||
}
|
||||
|
||||
+ (Class)_binderClassForBinding:(CPString)aBinding
|
||||
@@ -303,7 +310,7 @@ CPTexturedBackgroundWindowMask
|
||||
[self setPlatformWindow:[CPPlatformWindow primaryPlatformWindow]];
|
||||
else
|
||||
{
|
||||
// give zero sized borderless bridge windows a default size if we're not in the browser so they show up in NativeHost.
|
||||
// give zero sized borderless bridge windows a default size.
|
||||
if ((aStyleMask & CPBorderlessBridgeWindowMask) && aContentRect.size.width === 0 && aContentRect.size.height === 0)
|
||||
{
|
||||
var visibleFrame = [[[CPScreen alloc] init] visibleFrame];
|
||||
@@ -331,6 +338,8 @@ CPTexturedBackgroundWindowMask
|
||||
_isSheet = NO;
|
||||
_sheetContext = nil;
|
||||
_parentView = nil;
|
||||
_frameAnimation = nil;
|
||||
_frameAnimationDelegate = nil;
|
||||
|
||||
// Set up our window number.
|
||||
_windowNumber = [CPApp._windows count];
|
||||
@@ -340,9 +349,14 @@ CPTexturedBackgroundWindowMask
|
||||
|
||||
[self setLevel:CPNormalWindowLevel];
|
||||
|
||||
_handlingTrackingAreaEvent = NO;
|
||||
_restartHandlingTrackingAreaEvent = NO;
|
||||
_trackingAreaViews = [];
|
||||
_previousMouseEnteredStack = [];
|
||||
_previousCursorUpdateStack = [];
|
||||
_mouseEnteredStack = [];
|
||||
_cursorUpdateStack = [];
|
||||
_queuedEvents = [];
|
||||
_queuedTrackingEvents = [];
|
||||
_activeCursorTrackingArea = nil;
|
||||
|
||||
@@ -645,7 +659,6 @@ CPTexturedBackgroundWindowMask
|
||||
CPBorderlessWindowMask
|
||||
CPTitledWindowMask
|
||||
CPClosableWindowMask
|
||||
CPMiniaturizableWindowMask (NOTE: only available in NativeHost)
|
||||
CPResizableWindowMask
|
||||
CPTexturedBackgroundWindowMask
|
||||
CPBorderlessBridgeWindowMask
|
||||
@@ -733,7 +746,7 @@ CPTexturedBackgroundWindowMask
|
||||
{
|
||||
[_frameAnimation stopAnimation];
|
||||
_frameAnimation = [[_CPWindowFrameAnimation alloc] initWithWindow:self targetFrame:frame];
|
||||
|
||||
[_frameAnimation setDelegate:[self _frameAnimationDelegate]];
|
||||
[_frameAnimation startAnimation];
|
||||
}
|
||||
else
|
||||
@@ -793,6 +806,13 @@ CPTexturedBackgroundWindowMask
|
||||
[_platformWindow setContentRect:aFrame];
|
||||
}
|
||||
|
||||
- (id)_frameAnimationDelegate
|
||||
{
|
||||
if (_frameAnimationDelegate == nil)
|
||||
_frameAnimationDelegate = [[_CPWindowFrameAnimationDelegate alloc] initWithWindow:self];
|
||||
|
||||
return _frameAnimationDelegate;
|
||||
}
|
||||
/*
|
||||
Constrain a frame so that the window remains at least partially visible on screen,
|
||||
moving or resizing the frame as necessary.
|
||||
@@ -1831,16 +1851,12 @@ CPTexturedBackgroundWindowMask
|
||||
// CPLeftMouseDown is needed for window moving and resizing to work.
|
||||
// CPMouseMoved is needed for rollover effects on title bar buttons.
|
||||
|
||||
if (sheet)
|
||||
if (sheet && _sheetContext["isAttached"])
|
||||
{
|
||||
switch (type)
|
||||
{
|
||||
case CPLeftMouseDown:
|
||||
|
||||
// This is needed when a doubleClick occurs when the sheet is closing or opening
|
||||
if (!_parentWindow)
|
||||
return;
|
||||
|
||||
[_windowView mouseDown:anEvent];
|
||||
|
||||
// -dw- if the window is clicked, the sheet should come to front, and become key,
|
||||
@@ -1883,7 +1899,7 @@ CPTexturedBackgroundWindowMask
|
||||
// Make sure the browser doesn't try to do its own tab handling.
|
||||
// This is important or the browser might blur the shared text field or token field input field,
|
||||
// even that we just moved it to a new first responder.
|
||||
[[[anEvent window] platformWindow] _propagateCurrentDOMEvent:NO]
|
||||
[[[anEvent window] platformWindow] _propagateCurrentDOMEvent:NO];
|
||||
#endif
|
||||
return;
|
||||
}
|
||||
@@ -1897,7 +1913,7 @@ CPTexturedBackgroundWindowMask
|
||||
// Make sure the browser doesn't try to do its own tab handling.
|
||||
// This is important or the browser might blur the shared text field or token field input field,
|
||||
// even that we just moved it to a new first responder.
|
||||
[[[anEvent window] platformWindow] _propagateCurrentDOMEvent:NO]
|
||||
[[[anEvent window] platformWindow] _propagateCurrentDOMEvent:NO];
|
||||
#endif
|
||||
|
||||
}
|
||||
@@ -1980,7 +1996,7 @@ CPTexturedBackgroundWindowMask
|
||||
// First, we search for any tracking area requesting CPTrackingEnabledDuringMouseDrag.
|
||||
// At the same time, we update the entered stack.
|
||||
[self _handleTrackingAreaEvent:anEvent];
|
||||
|
||||
|
||||
// Normal mouseDragged workflow
|
||||
if (!_leftMouseDownView)
|
||||
return [[_windowView hitTest:point] mouseDragged:anEvent];
|
||||
@@ -1989,13 +2005,13 @@ CPTexturedBackgroundWindowMask
|
||||
|
||||
if (type == CPRightMouseDragged)
|
||||
{
|
||||
selector = @selector(rightMouseDragged:)
|
||||
selector = @selector(rightMouseDragged:);
|
||||
if (![_leftMouseDownView respondsToSelector:selector])
|
||||
selector = nil;
|
||||
}
|
||||
|
||||
if (!selector)
|
||||
selector = @selector(mouseDragged:)
|
||||
selector = @selector(mouseDragged:);
|
||||
|
||||
return [_leftMouseDownView performSelector:selector withObject:anEvent];
|
||||
|
||||
@@ -2004,11 +2020,30 @@ CPTexturedBackgroundWindowMask
|
||||
// Ignore mouse moves for parents of sheets
|
||||
if (!_acceptsMouseMovedEvents || sheet)
|
||||
return;
|
||||
|
||||
|
||||
[self _handleTrackingAreaEvent:anEvent];
|
||||
}
|
||||
}
|
||||
|
||||
- (void)_startLiveResize
|
||||
{
|
||||
[[CPNotificationCenter defaultCenter]
|
||||
postNotificationName:CPWindowWillStartLiveResizeNotification
|
||||
object:self];
|
||||
}
|
||||
|
||||
- (void)_endLiveResize
|
||||
{
|
||||
[[CPNotificationCenter defaultCenter]
|
||||
postNotificationName:CPWindowDidEndLiveResizeNotification
|
||||
object:self];
|
||||
}
|
||||
|
||||
- (BOOL)_inLiveResize
|
||||
{
|
||||
return [_windowView _isTracking];
|
||||
}
|
||||
|
||||
/*!
|
||||
Returns the window's number in the desktop's screen list
|
||||
*/
|
||||
@@ -3759,13 +3794,6 @@ var interpolate = function(fromValue, toValue, progress)
|
||||
return self;
|
||||
}
|
||||
|
||||
- (void)startAnimation
|
||||
{
|
||||
[super startAnimation];
|
||||
|
||||
_window._isAnimating = YES;
|
||||
}
|
||||
|
||||
- (void)setCurrentProgress:(float)aProgress
|
||||
{
|
||||
[super setCurrentProgress:aProgress];
|
||||
@@ -3773,7 +3801,7 @@ var interpolate = function(fromValue, toValue, progress)
|
||||
var value = [self currentValue];
|
||||
|
||||
if (value == 1.0)
|
||||
_window._isAnimating = NO;
|
||||
[_window _setAnimating:NO];
|
||||
|
||||
var newFrame = CGRectMake(
|
||||
interpolate(CGRectGetMinX(_startFrame), CGRectGetMinX(_targetFrame), value),
|
||||
@@ -3786,6 +3814,41 @@ var interpolate = function(fromValue, toValue, progress)
|
||||
|
||||
@end
|
||||
|
||||
@implementation _CPWindowFrameAnimationDelegate : CPObject
|
||||
{
|
||||
CPWindow _window;
|
||||
}
|
||||
|
||||
- (id)initWithWindow:(CPWindow)aWindow
|
||||
{
|
||||
self = [super init];
|
||||
|
||||
_window = aWindow;
|
||||
|
||||
return self;
|
||||
}
|
||||
|
||||
- (BOOL)animationShouldStart:(CPAnimation)animation
|
||||
{
|
||||
[_window _setAnimating:YES];
|
||||
[_window _startLiveResize];
|
||||
|
||||
return YES;
|
||||
}
|
||||
|
||||
- (void)animationDidStop:(CPAnimation)animation
|
||||
{
|
||||
[_window _setAnimating:NO];
|
||||
[_window _endLiveResize];
|
||||
}
|
||||
|
||||
- (void)animationDidEnd:(CPAnimation)animation
|
||||
{
|
||||
[_window _setAnimating:NO];
|
||||
[_window _endLiveResize];
|
||||
}
|
||||
|
||||
@end
|
||||
|
||||
@implementation CPWindow (CPDraggingAdditions)
|
||||
|
||||
@@ -3824,7 +3887,7 @@ var interpolate = function(fromValue, toValue, progress)
|
||||
- (void)_addTrackingAreaView:(CPView)aView
|
||||
{
|
||||
var trackingAreas = [aView trackingAreas];
|
||||
|
||||
|
||||
for (var i = 0; i < trackingAreas.length; i++)
|
||||
[self _addTrackingArea:trackingAreas[i]];
|
||||
}
|
||||
@@ -3832,7 +3895,7 @@ var interpolate = function(fromValue, toValue, progress)
|
||||
- (void)_removeTrackingAreaView:(CPView)aView
|
||||
{
|
||||
var trackingAreas = [aView trackingAreas];
|
||||
|
||||
|
||||
for (var i = 0; i < trackingAreas.length; i++)
|
||||
[self _removeTrackingArea:trackingAreas[i]];
|
||||
}
|
||||
@@ -3840,63 +3903,143 @@ var interpolate = function(fromValue, toValue, progress)
|
||||
- (void)_addTrackingArea:(CPTrackingArea)trackingArea
|
||||
{
|
||||
var trackingAreaView = [trackingArea view];
|
||||
|
||||
|
||||
if (![_trackingAreaViews containsObjectIdenticalTo:trackingAreaView])
|
||||
[_trackingAreaViews addObject:trackingAreaView];
|
||||
|
||||
// If CPTrackingAssumeInside option is set, put the tracking area in the _mouseEnteredStack
|
||||
|
||||
if ([trackingArea options] & CPTrackingAssumeInside)
|
||||
[_mouseEnteredStack addObject:trackingArea];
|
||||
|
||||
// If CPTrackingAssumeInside option is set, insert the tracking area in the events management system
|
||||
// in order to have the first event sent only when mouse leaves the tracking area
|
||||
|
||||
[self _insertTrackingArea:trackingArea assumeInside:([trackingArea options] & CPTrackingAssumeInside)];
|
||||
}
|
||||
|
||||
- (void)_removeTrackingArea:(CPTrackingArea)trackingArea
|
||||
{
|
||||
// If mouse is in the tracking area, we remove it from the stack to avoid to fire a future mouseExited event
|
||||
|
||||
[_mouseEnteredStack removeObjectIdenticalTo:trackingArea];
|
||||
|
||||
|
||||
[self _purgeTrackingArea:trackingArea];
|
||||
|
||||
var trackingAreaView = [trackingArea view];
|
||||
|
||||
|
||||
[_trackingAreaViews removeObjectIdenticalTo:trackingAreaView];
|
||||
}
|
||||
|
||||
- (void)_insertTrackingArea:(CPTrackingArea)trackingArea assumeInside:(BOOL)assumeInside
|
||||
{
|
||||
if (_handlingTrackingAreaEvent)
|
||||
_restartHandlingTrackingAreaEvent = YES;
|
||||
|
||||
if (assumeInside)
|
||||
{
|
||||
if (_handlingTrackingAreaEvent)
|
||||
[_mouseEnteredStack addObject:trackingArea];
|
||||
else
|
||||
[_previousMouseEnteredStack addObject:trackingArea];
|
||||
}
|
||||
}
|
||||
|
||||
- (void)_purgeTrackingArea:(CPTrackingArea)trackingArea
|
||||
{
|
||||
if (_handlingTrackingAreaEvent)
|
||||
{
|
||||
[_mouseEnteredStack removeObjectIdenticalTo:trackingArea];
|
||||
|
||||
var i = _queuedEvents.length;
|
||||
|
||||
while (i--)
|
||||
if ([_queuedEvents[i] trackingArea] === trackingArea)
|
||||
[_queuedEvents removeObjectAtIndex:i];
|
||||
|
||||
_cursorUpdateStack = [];
|
||||
_activeCursorTrackingArea = nil;
|
||||
}
|
||||
else
|
||||
{
|
||||
[_previousMouseEnteredStack removeObjectIdenticalTo:trackingArea];
|
||||
[_previousCursorUpdateStack removeObjectIdenticalTo:trackingArea];
|
||||
}
|
||||
}
|
||||
|
||||
- (void)_handleTrackingAreaEvent:(CPEvent)anEvent
|
||||
{
|
||||
var mouseEnteredStack = [],
|
||||
cursorUpdateStack = [],
|
||||
point = [anEvent locationInWindow],
|
||||
dragging = ([anEvent type] !== CPMouseMoved);
|
||||
_handlingTrackingAreaEvent = YES;
|
||||
|
||||
// Handle mouse entering tracking areas (and calc mouseEnteredStack and cursorUpdateStack)
|
||||
|
||||
[self _handleMouseMovedAndEnteredEventsForEvent:anEvent atPoint:point dragging:dragging mouseEnteredStack:mouseEnteredStack cursorUpdateStack:cursorUpdateStack];
|
||||
var point = [anEvent locationInWindow],
|
||||
dragging = ([anEvent type] !== CPMouseMoved);
|
||||
|
||||
// Handle mouse exiting tracking areas
|
||||
|
||||
[self _handleMouseExitedEventsForEvent:anEvent atPoint:point dragging:dragging mouseEnteredStack:mouseEnteredStack];
|
||||
|
||||
// Cursor update
|
||||
|
||||
if (cursorUpdateStack.length > 0)
|
||||
do
|
||||
{
|
||||
[self _handleCursorUpdateEventsForEvent:anEvent atPoint:point dragging:dragging cursorUpdateStack:cursorUpdateStack];
|
||||
}
|
||||
else if (!dragging)
|
||||
{
|
||||
// Here, we are outsite the window content view tracking area, so let _windowView set the cursor (resize cursor, ...)
|
||||
// Initialize this run
|
||||
_restartHandlingTrackingAreaEvent = NO;
|
||||
|
||||
[_windowView setCursorForLocation:point resizing:NO];
|
||||
_activeCursorTrackingArea = nil;
|
||||
}
|
||||
_mouseEnteredStack = [];
|
||||
_cursorUpdateStack = [];
|
||||
|
||||
// Prepare for next call
|
||||
|
||||
_mouseEnteredStack = mouseEnteredStack;
|
||||
_cursorUpdateStack = cursorUpdateStack;
|
||||
// Important remark: we must queue events to avoid running conditions when a view uses a mouse event to modify view hierarchy
|
||||
|
||||
_queuedEvents = [];
|
||||
|
||||
// Handle mouse entering tracking areas (and calc _mouseEnteredStack and _cursorUpdateStack)
|
||||
|
||||
[self _handleMouseMovedAndEnteredEventsForEvent:anEvent atPoint:point dragging:dragging];
|
||||
|
||||
// Handle mouse exiting tracking areas
|
||||
|
||||
[self _handleMouseExitedEventsForEvent:anEvent atPoint:point dragging:dragging];
|
||||
|
||||
// Cursor update
|
||||
|
||||
if (_cursorUpdateStack.length > 0)
|
||||
{
|
||||
[self _handleCursorUpdateEventsForEvent:anEvent atPoint:point dragging:dragging];
|
||||
}
|
||||
else if (!dragging)
|
||||
{
|
||||
// Here, we are outside the window content view tracking area, so let _windowView set the cursor (resize cursor, ...)
|
||||
|
||||
[_windowView setCursorForLocation:point resizing:NO];
|
||||
_activeCursorTrackingArea = nil;
|
||||
}
|
||||
|
||||
// Send all queued events
|
||||
// Important remark : as an event can modify the view hierarchy, the events queue could be modified while processing it
|
||||
|
||||
while (_queuedEvents.length > 0)
|
||||
{
|
||||
var queuedEvent = _queuedEvents[0],
|
||||
trackingArea = [queuedEvent trackingArea],
|
||||
trackingOwner = [trackingArea owner];
|
||||
|
||||
switch ([queuedEvent type])
|
||||
{
|
||||
case CPMouseEntered:
|
||||
[trackingOwner mouseEntered:queuedEvent];
|
||||
break;
|
||||
|
||||
case CPMouseExited:
|
||||
[trackingOwner mouseExited:queuedEvent];
|
||||
break;
|
||||
|
||||
case CPCursorUpdate:
|
||||
[trackingOwner cursorUpdate:queuedEvent];
|
||||
break;
|
||||
}
|
||||
|
||||
if (queuedEvent === _queuedEvents[0])
|
||||
[_queuedEvents removeObjectAtIndex:0];
|
||||
}
|
||||
|
||||
// Prepare for next call
|
||||
|
||||
_previousMouseEnteredStack = _mouseEnteredStack;
|
||||
_previousCursorUpdateStack = _cursorUpdateStack;
|
||||
}
|
||||
while (_restartHandlingTrackingAreaEvent)
|
||||
|
||||
_handlingTrackingAreaEvent = NO;
|
||||
}
|
||||
|
||||
- (void)_handleMouseMovedAndEnteredEventsForEvent:(CPEvent)anEvent atPoint:(CGPoint)point dragging:(BOOL)dragging mouseEnteredStack:(CPArray)mouseEnteredStack cursorUpdateStack:(CPArray)cursorUpdateStack
|
||||
- (void)_handleMouseMovedAndEnteredEventsForEvent:(CPEvent)anEvent atPoint:(CGPoint)point dragging:(BOOL)dragging
|
||||
{
|
||||
var isKeyWindow = [self isKeyWindow];
|
||||
|
||||
@@ -3923,9 +4066,9 @@ var interpolate = function(fromValue, toValue, progress)
|
||||
continue;
|
||||
}
|
||||
|
||||
[mouseEnteredStack addObject:aTrackingArea];
|
||||
[_mouseEnteredStack addObject:aTrackingArea];
|
||||
|
||||
if ([_mouseEnteredStack containsObjectIdenticalTo:aTrackingArea])
|
||||
if ([_previousMouseEnteredStack containsObjectIdenticalTo:aTrackingArea])
|
||||
{
|
||||
// Mouse was already in this rect so it's a mouseMoved
|
||||
|
||||
@@ -3946,32 +4089,33 @@ var interpolate = function(fromValue, toValue, progress)
|
||||
if (dragging && !(trackingOptions & CPTrackingEnabledDuringMouseDrag))
|
||||
[self _queueTrackingEvent:mouseEnteredEvent];
|
||||
else
|
||||
[[aTrackingArea owner] mouseEntered:mouseEnteredEvent];
|
||||
[_queuedEvents addObject:mouseEnteredEvent];
|
||||
}
|
||||
|
||||
if ((trackingOptions & CPTrackingCursorUpdate) && (trackingImplementedMethods & CPTrackingOwnerImplementsCursorUpdate))
|
||||
[cursorUpdateStack addObject:aTrackingArea];
|
||||
[_cursorUpdateStack addObject:aTrackingArea];
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
- (void)_handleMouseExitedEventsForEvent:(CPEvent)anEvent atPoint:(CGPoint)point dragging:(BOOL)dragging mouseEnteredStack:(CPArray)mouseEnteredStack
|
||||
- (void)_handleMouseExitedEventsForEvent:(CPEvent)anEvent atPoint:(CGPoint)point dragging:(BOOL)dragging
|
||||
{
|
||||
// Search for exited views (were in _mouseEnteredStack but no more in mouseEnteredStack)
|
||||
// Search for exited views (were in _previousMouseEnteredStack but no more in _mouseEnteredStack)
|
||||
|
||||
for (var i = 0; i < _mouseEnteredStack.length; i++)
|
||||
for (var i = 0; i < _previousMouseEnteredStack.length; i++)
|
||||
{
|
||||
var aTrackingArea = _mouseEnteredStack[i],
|
||||
var aTrackingArea = _previousMouseEnteredStack[i],
|
||||
trackingOptions = [aTrackingArea options];
|
||||
|
||||
if ([mouseEnteredStack containsObjectIdenticalTo:aTrackingArea])
|
||||
if ([_mouseEnteredStack containsObjectIdenticalTo:aTrackingArea])
|
||||
continue;
|
||||
|
||||
// Mouse is no more in this area so it's a mouseExited
|
||||
|
||||
if ((trackingOptions & CPTrackingMouseEnteredAndExited) && ([aTrackingArea implementedOwnerMethods] & CPTrackingOwnerImplementsMouseExited))
|
||||
{
|
||||
var mouseExitedEvent = [CPEvent enterExitEventWithType:CPMouseExited
|
||||
var theView = [aTrackingArea owner],
|
||||
mouseExitedEvent = [CPEvent enterExitEventWithType:CPMouseExited
|
||||
location:point
|
||||
modifierFlags:[anEvent modifierFlags]
|
||||
timestamp:[anEvent timestamp]
|
||||
@@ -3983,132 +4127,130 @@ var interpolate = function(fromValue, toValue, progress)
|
||||
if (dragging && !(trackingOptions & CPTrackingEnabledDuringMouseDrag))
|
||||
[self _queueTrackingEvent:mouseExitedEvent];
|
||||
else
|
||||
[[aTrackingArea owner] mouseExited:mouseExitedEvent];
|
||||
[_queuedEvents addObject:mouseExitedEvent];
|
||||
}
|
||||
|
||||
// If this is the active cursor area, we reset _cursorUpdateStack so a new active area will be computed
|
||||
// If this is the active cursor area, we reset _previousCursorUpdateStack so a new active area will be computed
|
||||
|
||||
if (aTrackingArea === _activeCursorTrackingArea)
|
||||
{
|
||||
_cursorUpdateStack = [];
|
||||
_previousCursorUpdateStack = [];
|
||||
_activeCursorTrackingArea = nil;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
- (void)_handleCursorUpdateEventsForEvent:(CPEvent)anEvent atPoint:(CGPoint)point dragging:(BOOL)dragging cursorUpdateStack:(CPArray)cursorUpdateStack
|
||||
- (void)_handleCursorUpdateEventsForEvent:(CPEvent)anEvent atPoint:(CGPoint)point dragging:(BOOL)dragging
|
||||
{
|
||||
var overlappingTrackingAreas = [];
|
||||
|
||||
for (var i = 0; i < cursorUpdateStack.length; i++)
|
||||
for (var i = 0; i < _cursorUpdateStack.length; i++)
|
||||
{
|
||||
var aTrackingArea = cursorUpdateStack[i];
|
||||
var aTrackingArea = _cursorUpdateStack[i];
|
||||
|
||||
if ((![_cursorUpdateStack containsObjectIdenticalTo:aTrackingArea]) || (aTrackingArea === _activeCursorTrackingArea))
|
||||
if ((![_previousCursorUpdateStack containsObjectIdenticalTo:aTrackingArea]) || (aTrackingArea === _activeCursorTrackingArea))
|
||||
[overlappingTrackingAreas addObject:aTrackingArea];
|
||||
}
|
||||
|
||||
var nbOverlappingTrackingAreas = overlappingTrackingAreas.length;
|
||||
if (overlappingTrackingAreas.length === 0)
|
||||
return;
|
||||
|
||||
if (nbOverlappingTrackingAreas > 0)
|
||||
var frontmostTrackingArea = overlappingTrackingAreas[0],
|
||||
frontmostView = [frontmostTrackingArea view];
|
||||
|
||||
for (var i = 1; i < overlappingTrackingAreas.length; i++)
|
||||
{
|
||||
var frontmostTrackingArea = overlappingTrackingAreas[0],
|
||||
frontmostView = [frontmostTrackingArea view];
|
||||
var aTrackingArea = overlappingTrackingAreas[i],
|
||||
aView = [aTrackingArea view];
|
||||
|
||||
for (var i = 1; i < nbOverlappingTrackingAreas; i++)
|
||||
// First, if aView is _windowView, skip to next overlapping tracking area
|
||||
// as _windowView can't be the frontmost view if there's multiple overlapping tracking areas.
|
||||
|
||||
if (aView === _windowView)
|
||||
continue;
|
||||
|
||||
// Then, if frontmostView is _windowView, aView must become frontmostView
|
||||
|
||||
if (frontmostView === _windowView)
|
||||
{
|
||||
var aTrackingArea = overlappingTrackingAreas[i],
|
||||
aView = [aTrackingArea view];
|
||||
frontmostTrackingArea = aTrackingArea;
|
||||
frontmostView = aView;
|
||||
|
||||
// First, if aView is _windowView, skip to next overlapping tracking area
|
||||
// as _windowView can't be the frontmost view if there's multiple overlapping tracking areas.
|
||||
|
||||
if (aView === _windowView)
|
||||
continue;
|
||||
|
||||
// Then, if frontmostView is _windowView, aView must become frontmostView
|
||||
|
||||
if (frontmostView === _windowView)
|
||||
{
|
||||
frontmostTrackingArea = aTrackingArea;
|
||||
frontmostView = aView;
|
||||
|
||||
continue;
|
||||
}
|
||||
|
||||
// Next verify if aView is a subview of frontmostView
|
||||
// If so, it's our new frontmost view
|
||||
|
||||
var searchingView = aView;
|
||||
|
||||
while ((searchingView !== _contentView) && ([searchingView superview] !== frontmostView))
|
||||
searchingView = [searchingView superview];
|
||||
|
||||
if (searchingView !== _contentView)
|
||||
{
|
||||
frontmostTrackingArea = aTrackingArea;
|
||||
frontmostView = aView;
|
||||
|
||||
continue;
|
||||
}
|
||||
|
||||
// aView is not a subview of frontmostView
|
||||
// Search in view hierarchy which one will be over the other
|
||||
// (this is done by comparing their draw order)
|
||||
|
||||
var firstView = frontmostView,
|
||||
firstSuperview = [firstView superview];
|
||||
|
||||
while (firstView !== _contentView)
|
||||
{
|
||||
var secondView = aView,
|
||||
secondSuperview = [secondView superview];
|
||||
|
||||
while ((secondSuperview !== _contentView) && (firstSuperview !== secondSuperview))
|
||||
{
|
||||
secondView = secondSuperview;
|
||||
secondSuperview = [secondView superview];
|
||||
}
|
||||
|
||||
if (firstSuperview === secondSuperview)
|
||||
break;
|
||||
|
||||
firstView = firstSuperview;
|
||||
firstSuperview = [firstView superview];
|
||||
}
|
||||
|
||||
if (firstSuperview !== secondSuperview)
|
||||
[CPException raise:CPInternalInconsistencyException reason:"Problem with view hierarchy"];
|
||||
|
||||
var firstSuperviewSubviews = [firstSuperview subviews],
|
||||
firstViewIndex = [firstSuperviewSubviews indexOfObject:firstView],
|
||||
secondViewIndex = [firstSuperviewSubviews indexOfObject:secondView];
|
||||
|
||||
if (secondViewIndex > firstViewIndex)
|
||||
{
|
||||
frontmostTrackingArea = aTrackingArea;
|
||||
frontmostView = aView;
|
||||
}
|
||||
continue;
|
||||
}
|
||||
|
||||
if (frontmostTrackingArea !== _activeCursorTrackingArea)
|
||||
// Next verify if aView is a subview of frontmostView
|
||||
// If so, it's our new frontmost view
|
||||
|
||||
var searchingView = aView;
|
||||
|
||||
while ((searchingView !== _contentView) && ([searchingView superview] !== frontmostView))
|
||||
searchingView = [searchingView superview];
|
||||
|
||||
if (searchingView !== _contentView)
|
||||
{
|
||||
var cursorUpdateEvent = [CPEvent enterExitEventWithType:CPCursorUpdate
|
||||
location:point
|
||||
modifierFlags:[anEvent modifierFlags]
|
||||
timestamp:[anEvent timestamp]
|
||||
windowNumber:_windowNumber
|
||||
context:nil
|
||||
eventNumber:-1
|
||||
trackingArea:frontmostTrackingArea];
|
||||
frontmostTrackingArea = aTrackingArea;
|
||||
frontmostView = aView;
|
||||
|
||||
if (dragging)
|
||||
[self _queueTrackingEvent:cursorUpdateEvent];
|
||||
else
|
||||
[[frontmostTrackingArea owner] cursorUpdate:cursorUpdateEvent];
|
||||
|
||||
_activeCursorTrackingArea = frontmostTrackingArea;
|
||||
continue;
|
||||
}
|
||||
|
||||
// aView is not a subview of frontmostView
|
||||
// Search in view hierarchy which one will be over the other
|
||||
// (this is done by comparing their draw order)
|
||||
|
||||
var firstView = frontmostView,
|
||||
firstSuperview = [firstView superview];
|
||||
|
||||
while (firstView !== _contentView)
|
||||
{
|
||||
var secondView = aView,
|
||||
secondSuperview = [secondView superview];
|
||||
|
||||
while ((secondSuperview !== _contentView) && (firstSuperview !== secondSuperview))
|
||||
{
|
||||
secondView = secondSuperview;
|
||||
secondSuperview = [secondView superview];
|
||||
}
|
||||
|
||||
if (firstSuperview === secondSuperview)
|
||||
break;
|
||||
|
||||
firstView = firstSuperview;
|
||||
firstSuperview = [firstView superview];
|
||||
}
|
||||
|
||||
if (firstSuperview !== secondSuperview)
|
||||
[CPException raise:CPInternalInconsistencyException reason:"Problem with view hierarchy"];
|
||||
|
||||
var firstSuperviewSubviews = [firstSuperview subviews],
|
||||
firstViewIndex = [firstSuperviewSubviews indexOfObject:firstView],
|
||||
secondViewIndex = [firstSuperviewSubviews indexOfObject:secondView];
|
||||
|
||||
if (secondViewIndex > firstViewIndex)
|
||||
{
|
||||
frontmostTrackingArea = aTrackingArea;
|
||||
frontmostView = aView;
|
||||
}
|
||||
}
|
||||
|
||||
if (frontmostTrackingArea !== _activeCursorTrackingArea)
|
||||
{
|
||||
var cursorUpdateEvent = [CPEvent enterExitEventWithType:CPCursorUpdate
|
||||
location:point
|
||||
modifierFlags:[anEvent modifierFlags]
|
||||
timestamp:[anEvent timestamp]
|
||||
windowNumber:_windowNumber
|
||||
context:nil
|
||||
eventNumber:-1
|
||||
trackingArea:frontmostTrackingArea];
|
||||
|
||||
if (dragging)
|
||||
[self _queueTrackingEvent:cursorUpdateEvent];
|
||||
else
|
||||
[_queuedEvents addObject:cursorUpdateEvent];
|
||||
|
||||
_activeCursorTrackingArea = frontmostTrackingArea;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -4116,12 +4258,11 @@ var interpolate = function(fromValue, toValue, progress)
|
||||
{
|
||||
// This will put a tracking event in the _queuedTrackingEvents queue.
|
||||
//
|
||||
// We optimize this queue with this policy :
|
||||
// We optimize this queue with this policy:
|
||||
// - if mouseEntered, search if queue contains a previous mouseExited for the same tracking area. If so, discard both.
|
||||
// - if mouseExited, search if queue contains a previous mouseEntered for the same tracking area. If so, discard both.
|
||||
//
|
||||
// This is not Cocoa way of doing as it would send every event.
|
||||
// But final result should be the same.
|
||||
// This is not the Cocoa way of doing as it would send every event, but the final result should be the same.
|
||||
|
||||
var eventType = [anEvent type],
|
||||
trackingArea = [anEvent trackingArea];
|
||||
@@ -4181,7 +4322,7 @@ var interpolate = function(fromValue, toValue, progress)
|
||||
case CPMouseExited:
|
||||
[trackingOwner mouseExited:queuedEvent];
|
||||
break;
|
||||
|
||||
|
||||
case CPCursorUpdate:
|
||||
[trackingOwner updateTrackingAreas];
|
||||
|
||||
|
||||
@@ -176,6 +176,8 @@ CPWindowDidEndSheetNotification = @"CPWindowDidEndSheetNotificat
|
||||
CPWindowDidMiniaturizeNotification = @"CPWindowDidMiniaturizeNotification";
|
||||
CPWindowWillMiniaturizeNotification = @"CPWindowWillMiniaturizeNotification";
|
||||
CPWindowDidDeminiaturizeNotification = @"CPWindowDidDeminiaturizeNotification";
|
||||
CPWindowWillStartLiveResizeNotification = @"CPWindowWillStartLiveResizeNotification";
|
||||
CPWindowDidEndLiveResizeNotification = @"CPWindowDidEndLiveResizeNotification";
|
||||
|
||||
_CPWindowDidChangeFirstResponderNotification = @"_CPWindowDidChangeFirstResponderNotification";
|
||||
|
||||
|
||||
@@ -29,7 +29,7 @@
|
||||
|
||||
+ (CPString)defaultThemeClass
|
||||
{
|
||||
return @"bordeless-bridge-window-view";
|
||||
return @"borderless-bridge-window-view";
|
||||
}
|
||||
|
||||
+ (CPDictionary)themeAttributes
|
||||
|
||||
@@ -183,7 +183,7 @@
|
||||
if (_styleMask & CPClosableWindowMask)
|
||||
{
|
||||
[_closeButton setFrameOrigin:[self valueForThemeAttribute:@"close-image-origin"]];
|
||||
[_closeButton setFrameSize:[self valueForThemeAttribute:@"close-image-size"]]
|
||||
[_closeButton setFrameSize:[self valueForThemeAttribute:@"close-image-size"]];
|
||||
[_closeButton setImage:[self valueForThemeAttribute:@"close-image"]];
|
||||
[_closeButton setAlternateImage:[self valueForThemeAttribute:@"close-active-image"]];
|
||||
}
|
||||
|
||||
@@ -64,6 +64,8 @@ _CPWindowViewResizeSlop = 3;
|
||||
CGRect _cachedScreenFrame;
|
||||
|
||||
CPView _sheetShadowView;
|
||||
|
||||
BOOL _isTracking @accessors(getter=_isTracking);
|
||||
}
|
||||
|
||||
+ (CGRect)contentRectForFrameRect:(CGRect)aFrameRect
|
||||
@@ -148,6 +150,7 @@ _CPWindowViewResizeSlop = 3;
|
||||
_styleMask = aStyleMask;
|
||||
_resizeIndicatorOffset = CGSizeMakeZero();
|
||||
_toolbarOffset = CGSizeMakeZero();
|
||||
_isTracking = NO;
|
||||
}
|
||||
|
||||
return self;
|
||||
@@ -458,6 +461,8 @@ _CPWindowViewResizeSlop = 3;
|
||||
if (type === CPLeftMouseUp)
|
||||
{
|
||||
_cachedScreenFrame = nil;
|
||||
_isTracking = NO;
|
||||
[_window _endLiveResize];
|
||||
return;
|
||||
}
|
||||
|
||||
@@ -474,6 +479,12 @@ _CPWindowViewResizeSlop = 3;
|
||||
}
|
||||
else if (type === CPLeftMouseDragged)
|
||||
{
|
||||
if (!_isTracking)
|
||||
{
|
||||
_isTracking = YES;
|
||||
[_window _startLiveResize];
|
||||
}
|
||||
|
||||
var deltaX = globalLocation.x - _mouseDraggedPoint.x,
|
||||
deltaY = globalLocation.y - _mouseDraggedPoint.y,
|
||||
startX = CGRectGetMinX(_cachedFrame),
|
||||
@@ -973,16 +984,22 @@ _CPWindowViewResizeSlop = 3;
|
||||
@end
|
||||
|
||||
@implementation _CPWindowView (TrackingAreaAdditions)
|
||||
{
|
||||
CPTrackingArea _windowViewTrackingArea;
|
||||
}
|
||||
|
||||
- (void)updateTrackingAreas
|
||||
{
|
||||
[self removeAllTrackingAreas];
|
||||
if (_windowViewTrackingArea)
|
||||
[self removeTrackingArea:_windowViewTrackingArea];
|
||||
|
||||
[self addTrackingArea:[[CPTrackingArea alloc] initWithRect:[self contentRectForFrameRect:[self frame]]
|
||||
options:CPTrackingCursorUpdate | CPTrackingActiveInActiveApp
|
||||
owner:self
|
||||
userInfo:nil]];
|
||||
_windowViewTrackingArea = [[CPTrackingArea alloc] initWithRect:[self contentRectForFrameRect:[self frame]]
|
||||
options:CPTrackingCursorUpdate | CPTrackingActiveInActiveApp
|
||||
owner:self
|
||||
userInfo:nil];
|
||||
|
||||
[self addTrackingArea:_windowViewTrackingArea];
|
||||
[super updateTrackingAreas];
|
||||
}
|
||||
|
||||
@end
|
||||
|
||||
|
||||
@@ -364,7 +364,7 @@
|
||||
{
|
||||
[viewControllerView removeFromSuperview];
|
||||
[viewControllerView setFrame:[contentView frame]];
|
||||
[viewControllerView setAutoresizingMask:[contentView autoresizingMask]]
|
||||
[viewControllerView setAutoresizingMask:[contentView autoresizingMask]];
|
||||
[[self window] setContentView:viewControllerView];
|
||||
}
|
||||
else
|
||||
|
||||
@@ -55,6 +55,18 @@ var CPCibObjectDataKey = @"CPCibObjectDataKey";
|
||||
id _loadDelegate;
|
||||
}
|
||||
|
||||
- (id)_initWithData:(CPData)data bundle:(CPBundle)aBundle cibName:(CPString)aCibName
|
||||
{
|
||||
self = [super init];
|
||||
|
||||
_data = data;
|
||||
_cibName = aCibName;
|
||||
_bundle = aBundle;
|
||||
_awakenCustomResources = YES;
|
||||
|
||||
return self;
|
||||
}
|
||||
|
||||
- (id)initWithContentsOfURL:(CPURL)aURL
|
||||
{
|
||||
self = [super init];
|
||||
|
||||
@@ -222,3 +222,28 @@ var _CPCibCustomResourceClassNameKey = @"_CPCibCustomResourceClassNameKey",
|
||||
}
|
||||
|
||||
@end
|
||||
|
||||
@implementation _CPCibCustomResource (CSSTheming)
|
||||
|
||||
- (BOOL)isCSSBased
|
||||
{
|
||||
return NO;
|
||||
}
|
||||
|
||||
- (BOOL)hasCSSDictionary
|
||||
{
|
||||
return NO;
|
||||
}
|
||||
|
||||
- (BOOL)hasCSSBeforeDictionary
|
||||
{
|
||||
return NO;
|
||||
}
|
||||
|
||||
- (BOOL)hasCSSAfterDictionary
|
||||
{
|
||||
return NO;
|
||||
}
|
||||
|
||||
@end
|
||||
|
||||
|
||||
@@ -230,7 +230,7 @@ var _CPCibObjectDataNamesKeysKey = @"_CPCibObjectDataNamesKeysKey
|
||||
|
||||
if ([object respondsToSelector:@selector(_cibInstantiate)])
|
||||
{
|
||||
var instantiatedObject = [object _cibInstantiate];
|
||||
instantiatedObject = [object _cibInstantiate];
|
||||
|
||||
if (instantiatedObject !== object)
|
||||
{
|
||||
|
||||
@@ -30,8 +30,10 @@
|
||||
*/
|
||||
@implementation CAAnimation : CPObject
|
||||
{
|
||||
BOOL _isRemovedOnCompletion;
|
||||
id _delegate;
|
||||
BOOL _isRemovedOnCompletion;
|
||||
id _delegate;
|
||||
CAMediaTimingFunction _timingFunction @accessors(property=timingFunction);
|
||||
double _duration @accessors(property=duration);
|
||||
}
|
||||
|
||||
/*!
|
||||
@@ -47,8 +49,10 @@
|
||||
{
|
||||
self = [super init];
|
||||
|
||||
if (self)
|
||||
_isRemovedOnCompletion = YES;
|
||||
_isRemovedOnCompletion = YES;
|
||||
_timingFunction = nil;
|
||||
_duration = 0.0;
|
||||
_delegate = nil;
|
||||
|
||||
return self;
|
||||
}
|
||||
@@ -102,7 +106,7 @@
|
||||
- (CAMediaTimingFunction)timingFunction
|
||||
{
|
||||
// Linear Pacing
|
||||
return nil;
|
||||
return _timingFunction;
|
||||
}
|
||||
|
||||
/*!
|
||||
@@ -127,131 +131,4 @@
|
||||
[anObject addAnimation:self forKey:aKey];
|
||||
}
|
||||
|
||||
@end
|
||||
|
||||
/*
|
||||
|
||||
*/
|
||||
@implementation CAPropertyAnimation : CAAnimation
|
||||
{
|
||||
CPString _keyPath;
|
||||
|
||||
BOOL _isCumulative;
|
||||
BOOL _isAdditive;
|
||||
}
|
||||
|
||||
+ (id)animationWithKeyPath:(CPString)aKeyPath
|
||||
{
|
||||
var animation = [self animation];
|
||||
|
||||
[animation setKeyPath:aKeyPath];
|
||||
|
||||
return animation;
|
||||
}
|
||||
|
||||
- (void)setKeyPath:(CPString)aKeyPath
|
||||
{
|
||||
_keyPath = aKeyPath;
|
||||
}
|
||||
|
||||
- (CPString)keyPath
|
||||
{
|
||||
return _keyPath;
|
||||
}
|
||||
|
||||
- (void)setCumulative:(BOOL)isCumulative
|
||||
{
|
||||
_isCumulative = isCumulative;
|
||||
}
|
||||
|
||||
- (BOOL)cumulative
|
||||
{
|
||||
return _isCumulative;
|
||||
}
|
||||
|
||||
- (BOOL)isCumulative
|
||||
{
|
||||
return _isCumulative;
|
||||
}
|
||||
|
||||
- (void)setAdditive:(BOOL)isAdditive
|
||||
{
|
||||
_isAdditive = isAdditive;
|
||||
}
|
||||
|
||||
- (BOOL)additive
|
||||
{
|
||||
return _isAdditive;
|
||||
}
|
||||
|
||||
- (BOOL)isAdditive
|
||||
{
|
||||
return _isAdditive;
|
||||
}
|
||||
|
||||
@end
|
||||
|
||||
/*!
|
||||
A CABasicAnimation is a simple animation that moves a
|
||||
CALayer from one point to another over a specified
|
||||
period of time.
|
||||
*/
|
||||
@implementation CABasicAnimation : CAPropertyAnimation
|
||||
{
|
||||
id _fromValue;
|
||||
id _toValue;
|
||||
id _byValue;
|
||||
}
|
||||
|
||||
/*!
|
||||
Sets the starting position for the animation.
|
||||
@param aValue the animation starting position
|
||||
*/
|
||||
- (void)setFromValue:(id)aValue
|
||||
{
|
||||
_fromValue = aValue;
|
||||
}
|
||||
|
||||
/*!
|
||||
Returns the animation's starting position.
|
||||
*/
|
||||
- (id)fromValue
|
||||
{
|
||||
return _fromValue;
|
||||
}
|
||||
|
||||
/*!
|
||||
Sets the ending position for the animation.
|
||||
@param aValue the animation ending position
|
||||
*/
|
||||
- (void)setToValue:(id)aValue
|
||||
{
|
||||
_toValue = aValue;
|
||||
}
|
||||
|
||||
/*!
|
||||
Returns the animation's ending position.
|
||||
*/
|
||||
- (id)toValue
|
||||
{
|
||||
return _toValue;
|
||||
}
|
||||
|
||||
/*!
|
||||
Sets the optional byValue for animation interpolation.
|
||||
@param aValue the byValue
|
||||
*/
|
||||
- (void)setByValue:(id)aValue
|
||||
{
|
||||
_byValue = aValue;
|
||||
}
|
||||
|
||||
/*!
|
||||
Returns the animation's byValue.
|
||||
*/
|
||||
- (id)byValue
|
||||
{
|
||||
return _byValue;
|
||||
}
|
||||
|
||||
@end
|
||||
@end
|
||||
@@ -0,0 +1,84 @@
|
||||
@import <Foundation/CPObject.j>
|
||||
|
||||
@import "CAPropertyAnimation.j"
|
||||
|
||||
/*!
|
||||
A CABasicAnimation is a simple animation that moves a
|
||||
CALayer from one point to another over a specified
|
||||
period of time.
|
||||
*/
|
||||
/*!
|
||||
A CABasicAnimation is a simple animation that moves a
|
||||
CALayer from one point to another over a specified
|
||||
period of time.
|
||||
*/
|
||||
@implementation CABasicAnimation : CAPropertyAnimation
|
||||
{
|
||||
id _fromValue;
|
||||
id _toValue;
|
||||
id _byValue;
|
||||
}
|
||||
|
||||
- (id)init
|
||||
{
|
||||
self = [super init];
|
||||
|
||||
_fromValue = nil;
|
||||
_toValue = nil;
|
||||
_byValue = nil;
|
||||
|
||||
return self;
|
||||
}
|
||||
|
||||
/*!
|
||||
Sets the starting position for the animation.
|
||||
@param aValue the animation starting position
|
||||
*/
|
||||
- (void)setFromValue:(id)aValue
|
||||
{
|
||||
_fromValue = aValue;
|
||||
}
|
||||
|
||||
/*!
|
||||
Returns the animation's starting position.
|
||||
*/
|
||||
- (id)fromValue
|
||||
{
|
||||
return _fromValue;
|
||||
}
|
||||
|
||||
/*!
|
||||
Sets the ending position for the animation.
|
||||
@param aValue the animation ending position
|
||||
*/
|
||||
- (void)setToValue:(id)aValue
|
||||
{
|
||||
_toValue = aValue;
|
||||
}
|
||||
|
||||
/*!
|
||||
Returns the animation's ending position.
|
||||
*/
|
||||
- (id)toValue
|
||||
{
|
||||
return _toValue;
|
||||
}
|
||||
|
||||
/*!
|
||||
Sets the optional byValue for animation interpolation.
|
||||
@param aValue the byValue
|
||||
*/
|
||||
- (void)setByValue:(id)aValue
|
||||
{
|
||||
_byValue = aValue;
|
||||
}
|
||||
|
||||
/*!
|
||||
Returns the animation's byValue.
|
||||
*/
|
||||
- (id)byValue
|
||||
{
|
||||
return _byValue;
|
||||
}
|
||||
|
||||
@end
|
||||
@@ -0,0 +1,23 @@
|
||||
@import <Foundation/CPObject.j>
|
||||
|
||||
@import "CAPropertyAnimation.j"
|
||||
|
||||
@implementation CAKeyframeAnimation : CAPropertyAnimation
|
||||
{
|
||||
CPArray _values @accessors(property=values);
|
||||
CPArray _keyTimes @accessors(property=keyTimes);
|
||||
CPArray _timingFunctions @accessors(property=timingFunctions);
|
||||
}
|
||||
|
||||
- (id)init
|
||||
{
|
||||
self = [super init];
|
||||
|
||||
_values = [CPArray array];
|
||||
_keyTimes = [CPArray array];
|
||||
_timingFunctions = [CPArray array];
|
||||
|
||||
return self;
|
||||
}
|
||||
|
||||
@end
|
||||
@@ -522,7 +522,7 @@ var CALayerRegisteredRunLoopUpdates = nil;
|
||||
if (USE_BUFFER)
|
||||
{
|
||||
if (_delegateRespondsToDisplayLayerSelector)
|
||||
return [_delegate displayInLayer:self];
|
||||
return [_delegate displayLayer:self];
|
||||
|
||||
if (CGRectGetWidth(_backingStoreFrame) == 0.0 || CGRectGetHeight(_backingStoreFrame) == 0.0)
|
||||
return;
|
||||
|
||||
@@ -0,0 +1,74 @@
|
||||
|
||||
@import <Foundation/CPObject.j>
|
||||
|
||||
@import "CAAnimation.j"
|
||||
|
||||
@implementation CAPropertyAnimation : CAAnimation
|
||||
{
|
||||
CPString _keyPath;
|
||||
|
||||
BOOL _isCumulative;
|
||||
BOOL _isAdditive;
|
||||
}
|
||||
|
||||
- (id)init
|
||||
{
|
||||
self = [super init];
|
||||
|
||||
_keyPath = nil;
|
||||
_isCumulative = NO;
|
||||
_isAdditive = NO;
|
||||
|
||||
return self;
|
||||
}
|
||||
|
||||
+ (id)animationWithKeyPath:(CPString)aKeyPath
|
||||
{
|
||||
var animation = [self animation];
|
||||
|
||||
[animation setKeyPath:aKeyPath];
|
||||
|
||||
return animation;
|
||||
}
|
||||
|
||||
- (void)setKeyPath:(CPString)aKeyPath
|
||||
{
|
||||
_keyPath = aKeyPath;
|
||||
}
|
||||
|
||||
- (CPString)keyPath
|
||||
{
|
||||
return _keyPath;
|
||||
}
|
||||
|
||||
- (void)setCumulative:(BOOL)isCumulative
|
||||
{
|
||||
_isCumulative = isCumulative;
|
||||
}
|
||||
|
||||
- (BOOL)cumulative
|
||||
{
|
||||
return _isCumulative;
|
||||
}
|
||||
|
||||
- (BOOL)isCumulative
|
||||
{
|
||||
return _isCumulative;
|
||||
}
|
||||
|
||||
- (void)setAdditive:(BOOL)isAdditive
|
||||
{
|
||||
_isAdditive = isAdditive;
|
||||
}
|
||||
|
||||
- (BOOL)additive
|
||||
{
|
||||
return _isAdditive;
|
||||
}
|
||||
|
||||
- (BOOL)isAdditive
|
||||
{
|
||||
return _isAdditive;
|
||||
}
|
||||
|
||||
@end
|
||||
@@ -0,0 +1,560 @@
|
||||
@import "CABasicAnimation.j"
|
||||
@import "CAKeyframeAnimation.j"
|
||||
@import "CPView.j"
|
||||
|
||||
@import <Foundation/CPTimer.j>
|
||||
@import <Foundation/CPRunLoop.j>
|
||||
|
||||
@typedef Map;
|
||||
|
||||
var _CPAnimationContextStack = nil,
|
||||
_animationFlushingObserver = nil;
|
||||
|
||||
@implementation CPAnimationContext : CPObject
|
||||
{
|
||||
double _duration @accessors(property=duration);
|
||||
CAMediaTimingFunction _timingFunction @accessors(property=timingFunction);
|
||||
Function _completionHandlerAgent;
|
||||
Map _animationsByObject;
|
||||
}
|
||||
|
||||
+ (id)currentContext
|
||||
{
|
||||
var contextStack = [self contextStack],
|
||||
context = [contextStack lastObject];
|
||||
|
||||
if (!context)
|
||||
{
|
||||
context = [[CPAnimationContext alloc] init];
|
||||
|
||||
[contextStack addObject:context];
|
||||
[self _scheduleAnimationContextStackFlush];
|
||||
}
|
||||
|
||||
return context;
|
||||
}
|
||||
|
||||
+ (CPArray)contextStack
|
||||
{
|
||||
if (!_CPAnimationContextStack)
|
||||
_CPAnimationContextStack = [CPArray array];
|
||||
|
||||
return _CPAnimationContextStack;
|
||||
}
|
||||
|
||||
+ (void)runAnimationGroup:(Function/*(CPAnimationContext context)*/)animationsBlock completionHandler:(Function)aCompletionHandler
|
||||
{
|
||||
[CPAnimationContext beginGrouping];
|
||||
|
||||
var context = [CPAnimationContext currentContext];
|
||||
[context setCompletionHandler:aCompletionHandler];
|
||||
|
||||
animationsBlock(context);
|
||||
|
||||
[CPAnimationContext endGrouping];
|
||||
}
|
||||
|
||||
- (id)init
|
||||
{
|
||||
self = [super init];
|
||||
|
||||
_duration = 0.0;
|
||||
_timingFunction = [CAMediaTimingFunction functionWithName:kCAMediaTimingFunctionLinear];
|
||||
_completionHandlerAgent = nil;
|
||||
_animationsByObject = new Map();
|
||||
|
||||
return self;
|
||||
}
|
||||
|
||||
+ (void)_scheduleAnimationContextStackFlush
|
||||
{
|
||||
if (!_animationFlushingObserver)
|
||||
{
|
||||
#if (DEBUG)
|
||||
CPLog.debug("create new observer");
|
||||
#endif
|
||||
_animationFlushingObserver = CFRunLoopObserverCreate(2, true, 0, _animationFlushingObserverCallback, 0);
|
||||
CFRunLoopAddObserver([CPRunLoop mainRunLoop], _animationFlushingObserver);
|
||||
}
|
||||
}
|
||||
|
||||
+ (void)beginGrouping
|
||||
{
|
||||
var newContext = [[CPAnimationContext alloc] init];
|
||||
|
||||
if ([_CPAnimationContextStack count])
|
||||
{
|
||||
var currentContext = [_CPAnimationContextStack lastObject];
|
||||
[newContext setDuration:[currentContext duration]];
|
||||
[newContext setTimingFunction:[currentContext timingFunction]];
|
||||
}
|
||||
|
||||
[_CPAnimationContextStack addObject:newContext];
|
||||
}
|
||||
|
||||
+ (BOOL)endGrouping
|
||||
{
|
||||
if (![_CPAnimationContextStack count])
|
||||
return NO;
|
||||
|
||||
var context = [_CPAnimationContextStack lastObject];
|
||||
[context _flushAnimations];
|
||||
[_CPAnimationContextStack removeLastObject];
|
||||
|
||||
#if (DEBUG)
|
||||
CPLog.debug(_cmd + "context stack =" + _CPAnimationContextStack);
|
||||
#endif
|
||||
return YES;
|
||||
}
|
||||
|
||||
- (void)_enqueueActionForObject:(id)anObject keyPath:(id)aKeyPath targetValue:(id)aTargetValue animationCompletion:(id)animationCompletion
|
||||
{
|
||||
var resolvedAction = [self _actionForObject:anObject keyPath:aKeyPath targetValue:aTargetValue animationCompletion:animationCompletion];
|
||||
|
||||
if (!resolvedAction)
|
||||
return;
|
||||
|
||||
var animByKeyPath = _animationsByObject.get(anObject);
|
||||
|
||||
if (!animByKeyPath)
|
||||
{
|
||||
var newAnimByKeyPath = @{aKeyPath:resolvedAction};
|
||||
_animationsByObject.set(anObject, newAnimByKeyPath);
|
||||
}
|
||||
else
|
||||
[animByKeyPath setObject:resolvedAction forKey:aKeyPath];
|
||||
}
|
||||
|
||||
- (Object)_actionForObject:(id)anObject keyPath:(CPString)aKeyPath targetValue:(id)aTargetValue animationCompletion:(Function)animationCompletion
|
||||
{
|
||||
var animation,
|
||||
duration,
|
||||
animatedKeyPath,
|
||||
values,
|
||||
keyTimes,
|
||||
timingFunctions,
|
||||
needsPeriodicFrameUpdates,
|
||||
objectId = [anObject UID];
|
||||
|
||||
if (!aKeyPath || !anObject || !(animation = [anObject animationForKey:aKeyPath]) || ![animation isKindOfClass:[CAAnimation class]])
|
||||
return nil;
|
||||
|
||||
duration = [animation duration] || [self duration];
|
||||
needsPeriodicFrameUpdates = [[anObject animator] needsPeriodicFrameUpdatesForKeyPath:aKeyPath];
|
||||
|
||||
var animatorClass = [[anObject class] animatorClass];
|
||||
|
||||
var completionFunction = function()
|
||||
{
|
||||
if (needsPeriodicFrameUpdates)
|
||||
[animatorClass stopUpdaterWithIdentifier:objectId];
|
||||
|
||||
if (animationCompletion)
|
||||
animationCompletion();
|
||||
|
||||
if (needsPeriodicFrameUpdates || animationCompletion)
|
||||
[[CPRunLoop currentRunLoop] performSelectors];
|
||||
|
||||
if (_completionHandlerAgent)
|
||||
_completionHandlerAgent.decrement();
|
||||
};
|
||||
|
||||
if (![animation isKindOfClass:[CAPropertyAnimation class]] || !(animatedKeyPath = [animation keyPath]))
|
||||
animatedKeyPath = aKeyPath;
|
||||
|
||||
if ([animation isKindOfClass:[CAKeyframeAnimation class]])
|
||||
{
|
||||
values = [animation values];
|
||||
keyTimes = [animation keyTimes];
|
||||
timingFunctions = [animation timingFunctionsControlPoints];
|
||||
}
|
||||
else
|
||||
{
|
||||
var isBasicAnimation = [animation isKindOfClass:[CABasicAnimation class]],
|
||||
fromValue,
|
||||
toValue;
|
||||
|
||||
if (!isBasicAnimation || (fromValue = [animation fromValue]) == nil)
|
||||
fromValue = [anObject valueForKey:animatedKeyPath];
|
||||
|
||||
if (!isBasicAnimation || (toValue = [animation toValue]) == nil)
|
||||
toValue = aTargetValue;
|
||||
|
||||
values = [fromValue, toValue];
|
||||
keyTimes = [0, 1];
|
||||
timingFunctions = isBasicAnimation ? [animation timingFunctionControlPoints] : [_timingFunction controlPoints];
|
||||
}
|
||||
|
||||
return {
|
||||
object:anObject,
|
||||
root:anObject,
|
||||
keypath:animatedKeyPath,
|
||||
values:values,
|
||||
keytimes:keyTimes,
|
||||
duration:duration,
|
||||
timingfunctions:timingFunctions,
|
||||
completion:completionFunction
|
||||
};
|
||||
}
|
||||
|
||||
- (void)_flushAnimations
|
||||
{
|
||||
if (![_CPAnimationContextStack count])
|
||||
return;
|
||||
|
||||
if (_animationsByObject.size == 0)
|
||||
{
|
||||
if (_completionHandlerAgent)
|
||||
{
|
||||
#if (DEBUG)
|
||||
CPLog.debug("No animations are scheduled. Firing completion handler");
|
||||
#endif
|
||||
_completionHandlerAgent.fire();
|
||||
}
|
||||
}
|
||||
else
|
||||
[self _startAnimations];
|
||||
}
|
||||
|
||||
- (void)_startAnimations
|
||||
{
|
||||
var cssAnimations = [],
|
||||
timers = [];
|
||||
|
||||
_animationsByObject.forEach(function(animByKeyPath, targetView)
|
||||
{
|
||||
[animByKeyPath enumerateKeysAndObjectsUsingBlock:function(aKey, anAction, stop)
|
||||
{
|
||||
[self getAnimations:cssAnimations getTimers:timers usingAction:anAction cssAnimate:YES];
|
||||
}];
|
||||
});
|
||||
|
||||
_animationsByObject.clear();
|
||||
|
||||
var k = timers.length,
|
||||
n = cssAnimations.length;
|
||||
|
||||
if (_completionHandlerAgent)
|
||||
{
|
||||
if (n == 0)
|
||||
{
|
||||
#if (DEBUG)
|
||||
CPLog.debug("Animations are not needed. Firing completion handler");
|
||||
#endif
|
||||
_completionHandlerAgent.fire();
|
||||
}
|
||||
else
|
||||
_completionHandlerAgent.increment(n);
|
||||
}
|
||||
|
||||
// start timers
|
||||
while(k--)
|
||||
{
|
||||
#if (DEBUG)
|
||||
CPLog.debug("START TIMER " + timers[k].description());
|
||||
#endif
|
||||
timers[k].start();
|
||||
}
|
||||
|
||||
// start css animations
|
||||
while(n--)
|
||||
{
|
||||
#if (DEBUG)
|
||||
CPLog.debug("START ANIMATION " + cssAnimations[n].description());
|
||||
#endif
|
||||
cssAnimations[n].start();
|
||||
}
|
||||
}
|
||||
|
||||
- (void)getAnimations:(CPArray)cssAnimations getTimers:(CPArray)timers usingAction:(Object)anAction cssAnimate:(BOOL)needsCSSAnimation
|
||||
{
|
||||
var values = anAction.values;
|
||||
|
||||
if (values.length == 2)
|
||||
{
|
||||
var start = values[0],
|
||||
end = values[1];
|
||||
|
||||
if (anAction.keypath == @"frame" && CGRectEqualToRect(start, end)
|
||||
|| anAction.keypath == @"frameSize" && CGSizeEqualToSize(start, end)
|
||||
|| anAction.keypath == @"frameOrigin" && CGPointEqualToPoint(start, end))
|
||||
return;
|
||||
}
|
||||
|
||||
var targetView = anAction.object,
|
||||
keyPath = anAction.keypath,
|
||||
isFrameKeyPath = (keyPath == @"frame" || keyPath == @"frameSize"),
|
||||
customLayout = [targetView hasCustomLayoutSubviews],
|
||||
customDrawing = [targetView hasCustomDrawRect],
|
||||
declarative_subviews_layout = (!customLayout || [targetView implementsSelector:@selector(frameRectOfView:inSuperviewSize:)]),
|
||||
needsPeriodicFrameUpdates = [[targetView animator] needsPeriodicFrameUpdatesForKeyPath:keyPath],
|
||||
timer = nil,
|
||||
animatorClass = [[targetView class] animatorClass];
|
||||
|
||||
if (needsCSSAnimation)
|
||||
{
|
||||
[animatorClass addAnimations:cssAnimations forAction:anAction];
|
||||
}
|
||||
|
||||
if (needsPeriodicFrameUpdates)
|
||||
{
|
||||
[animatorClass addFrameUpdaters:timers forAction:anAction];
|
||||
}
|
||||
|
||||
var subviews = [targetView subviews],
|
||||
count = [subviews count];
|
||||
|
||||
if (count && isFrameKeyPath)
|
||||
{
|
||||
[subviews enumerateObjectsUsingBlock:function(aSubview, idx, stop)
|
||||
{
|
||||
if (!declarative_subviews_layout && [aSubview autoresizingMask] == 0)
|
||||
return;
|
||||
|
||||
var action = [self actionFromAction:anAction forAnimatedSubview:aSubview],
|
||||
targetFrame = [action.values lastObject];
|
||||
|
||||
if (CGRectEqualToRect([aSubview frame], targetFrame))
|
||||
return;
|
||||
|
||||
if ([aSubview hasCustomDrawRect])
|
||||
{
|
||||
action.completion = function()
|
||||
{
|
||||
[aSubview setFrame:targetFrame];
|
||||
#if (DEBUG)
|
||||
CPLog.debug(aSubview + " setFrame: " + CPStringFromRect(targetFrame));
|
||||
#endif
|
||||
if (idx == count - 1)
|
||||
[animatorClass stopUpdaterWithIdentifier:[anAction.root UID]];
|
||||
};
|
||||
}
|
||||
|
||||
var animate = !needsPeriodicFrameUpdates;
|
||||
[self getAnimations:cssAnimations getTimers:timers usingAction:action cssAnimate:animate];
|
||||
}];
|
||||
}
|
||||
}
|
||||
|
||||
- (Object)actionFromAction:(Object)anAction forAnimatedSubview:(CPView)aView
|
||||
{
|
||||
var targetValue = [anAction.values lastObject],
|
||||
startFrame = [aView frame],
|
||||
endFrame,
|
||||
values;
|
||||
|
||||
if (anAction.keypath == "frame")
|
||||
targetValue = targetValue.size;
|
||||
|
||||
endFrame = [[aView superview] frameRectOfView:aView inSuperviewSize:targetValue];
|
||||
values = [startFrame, endFrame];
|
||||
|
||||
return {
|
||||
object:aView,
|
||||
root:anAction.root,
|
||||
keypath:"frame",
|
||||
values:values,
|
||||
keytimes:[0, 1],
|
||||
duration:anAction.duration,
|
||||
timingfunctions:anAction.timingfunctions
|
||||
};
|
||||
}
|
||||
|
||||
- (void)setCompletionHandler:(Function)aCompletionHandler
|
||||
{
|
||||
if (_completionHandlerAgent)
|
||||
{
|
||||
if (aCompletionHandler === _completionHandlerAgent._completionHandler)
|
||||
return;
|
||||
|
||||
_completionHandlerAgent.invalidate();
|
||||
}
|
||||
|
||||
if (aCompletionHandler)
|
||||
{
|
||||
_completionHandlerAgent = new CompletionHandlerAgent(aCompletionHandler);
|
||||
#if (DEBUG)
|
||||
CPLog.debug("created a new completion Agent with id " + _completionHandlerAgent.id);
|
||||
#endif
|
||||
}
|
||||
else
|
||||
{
|
||||
_completionHandlerAgent = nil;
|
||||
}
|
||||
}
|
||||
|
||||
- (void)completionHandler
|
||||
{
|
||||
if (!_completionHandlerAgent)
|
||||
return nil;
|
||||
|
||||
return _completionHandlerAgent.completionHandler();
|
||||
}
|
||||
|
||||
@end
|
||||
|
||||
@implementation CPView (CPAnimationContext)
|
||||
|
||||
- (CGRect)frameRectOfView:(CPView)aView inSuperviewSize:(CGSize)aSize
|
||||
{
|
||||
return [aView frameWithNewSuperviewSize:aSize];
|
||||
}
|
||||
|
||||
- (CGRect)frameWithNewSuperviewSize:(CGSize)newSize
|
||||
{
|
||||
var mask = [self autoresizingMask];
|
||||
|
||||
if (mask == CPViewNotSizable)
|
||||
return _frame;
|
||||
|
||||
var oldSize = _superview._frame.size,
|
||||
newFrame = CGRectMakeCopy(_frame),
|
||||
dX = newSize.width - oldSize.width,
|
||||
dY = newSize.height - oldSize.height,
|
||||
evenFractionX = 1.0 / ((mask & CPViewMinXMargin ? 1 : 0) + (mask & CPViewWidthSizable ? 1 : 0) + (mask & CPViewMaxXMargin ? 1 : 0)),
|
||||
evenFractionY = 1.0 / ((mask & CPViewMinYMargin ? 1 : 0) + (mask & CPViewHeightSizable ? 1 : 0) + (mask & CPViewMaxYMargin ? 1 : 0)),
|
||||
baseX = (mask & CPViewMinXMargin ? _frame.origin.x : 0) +
|
||||
(mask & CPViewWidthSizable ? _frame.size.width : 0) +
|
||||
(mask & CPViewMaxXMargin ? oldSize.width - _frame.size.width - _frame.origin.x : 0),
|
||||
baseY = (mask & CPViewMinYMargin ? _frame.origin.y : 0) +
|
||||
(mask & CPViewHeightSizable ? _frame.size.height : 0) +
|
||||
(mask & CPViewMaxYMargin ? oldSize.height - _frame.size.height - _frame.origin.y : 0);
|
||||
|
||||
|
||||
if (mask & CPViewMinXMargin)
|
||||
newFrame.origin.x += dX * (baseX > 0 ? _frame.origin.x / baseX : evenFractionX);
|
||||
if (mask & CPViewWidthSizable)
|
||||
newFrame.size.width += dX * (baseX > 0 ? _frame.size.width / baseX : evenFractionX);
|
||||
|
||||
if (mask & CPViewMinYMargin)
|
||||
newFrame.origin.y += dY * (baseY > 0 ? _frame.origin.y / baseY : evenFractionY);
|
||||
if (mask & CPViewHeightSizable)
|
||||
newFrame.size.height += dY * (baseY > 0 ? _frame.size.height / baseY : evenFractionY);
|
||||
|
||||
return newFrame;
|
||||
}
|
||||
|
||||
- (BOOL)hasCustomDrawRect
|
||||
{
|
||||
return self._viewClassFlags & 1;
|
||||
}
|
||||
|
||||
- (BOOL)hasCustomLayoutSubviews
|
||||
{
|
||||
return self._viewClassFlags & 2;
|
||||
}
|
||||
|
||||
@end
|
||||
|
||||
@implementation CAMediaTimingFunction (Additions)
|
||||
|
||||
- (CPArray)controlPoints
|
||||
{
|
||||
return [_c1x, _c1y, _c2x, _c2y];
|
||||
}
|
||||
|
||||
@end
|
||||
|
||||
@implementation CAAnimation (Additions)
|
||||
|
||||
- (CPArray)timingFunctionControlPoints
|
||||
{
|
||||
if (_timingFunction)
|
||||
return [_timingFunction controlPoints];
|
||||
|
||||
return [0, 0, 1, 1];
|
||||
}
|
||||
|
||||
@end
|
||||
|
||||
@implementation CAKeyframeAnimation (Additions)
|
||||
|
||||
- (CPArray)timingFunctionsControlPoints
|
||||
{
|
||||
var result = [CPArray array];
|
||||
|
||||
[_timingFunctions enumerateObjectsUsingBlock:function(timingFunction, idx)
|
||||
{
|
||||
[result addObject:[timingFunction controlPoints]];
|
||||
}];
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
@end
|
||||
|
||||
var COMPLETION_AGENT_ID = 0;
|
||||
|
||||
var CompletionHandlerAgent = function(aCompletionHandler)
|
||||
{
|
||||
this._completionHandler = aCompletionHandler;
|
||||
this.total = 0;
|
||||
this.valid = true;
|
||||
this.id = COMPLETION_AGENT_ID++;
|
||||
};
|
||||
|
||||
CompletionHandlerAgent.prototype.completionHandler = function()
|
||||
{
|
||||
return this._completionHandler;
|
||||
};
|
||||
|
||||
CompletionHandlerAgent.prototype.fire = function()
|
||||
{
|
||||
if (this.valid)
|
||||
{
|
||||
this._completionHandler();
|
||||
this.valid = false;
|
||||
this.total = 0;
|
||||
}
|
||||
};
|
||||
|
||||
CompletionHandlerAgent.prototype.increment = function(inc)
|
||||
{
|
||||
this.total += inc;
|
||||
};
|
||||
|
||||
CompletionHandlerAgent.prototype.decrement = function()
|
||||
{
|
||||
if (this.total <= 0)
|
||||
return;
|
||||
|
||||
this.total--;
|
||||
|
||||
if (this.valid && this.total == 0)
|
||||
{
|
||||
this.fire();
|
||||
}
|
||||
};
|
||||
|
||||
CompletionHandlerAgent.prototype.invalidate = function()
|
||||
{
|
||||
this.valid = false;
|
||||
this.total = 0;
|
||||
this._completionHandler = null;
|
||||
};
|
||||
|
||||
var _animationFlushingObserverCallback = function()
|
||||
{
|
||||
#if (DEBUG)
|
||||
CPLog.debug("_animationFlushingObserverCallback");
|
||||
#endif
|
||||
if ([_CPAnimationContextStack count] == 1)
|
||||
{
|
||||
var context = [_CPAnimationContextStack lastObject];
|
||||
[context _flushAnimations];
|
||||
[_CPAnimationContextStack removeLastObject];
|
||||
}
|
||||
|
||||
#if (DEBUG)
|
||||
CPLog.debug("_animationFlushingObserver "+_animationFlushingObserver+" stack:" + [_CPAnimationContextStack count]);
|
||||
#endif
|
||||
|
||||
if (_animationFlushingObserver && ![_CPAnimationContextStack count])
|
||||
{
|
||||
#if (DEBUG)
|
||||
CPLog.debug("removeObserver");
|
||||
#endif
|
||||
CFRunLoopObserverInvalidate([CPRunLoop mainRunLoop], _animationFlushingObserver);
|
||||
_animationFlushingObserver = nil;
|
||||
}
|
||||
};
|
||||
@@ -0,0 +1,440 @@
|
||||
|
||||
@import "_CPObjectAnimator.j"
|
||||
@import "CPView.j"
|
||||
@import "CPCompatibility.j"
|
||||
@import "CSSAnimation.j"
|
||||
|
||||
var DEFAULT_CSS_PROPERTIES = nil,
|
||||
FRAME_UPDATERS = {};
|
||||
|
||||
@implementation CPViewAnimator : _CPObjectAnimator
|
||||
{
|
||||
BOOL _wantsPeriodicFrameUpdates @accessors(property=wantsPeriodicFrameUpdates);
|
||||
}
|
||||
|
||||
- (id)initWithTarget:(id)aTarget
|
||||
{
|
||||
self = [super initWithTarget:aTarget];
|
||||
|
||||
_wantsPeriodicFrameUpdates = NO;
|
||||
|
||||
return self;
|
||||
}
|
||||
|
||||
- (void)removeFromSuperview
|
||||
{
|
||||
[self _setTargetValue:nil withKeyPath:@"CPAnimationTriggerOrderOut" setter:_cmd];
|
||||
}
|
||||
|
||||
- (void)setHidden:(BOOL)shouldHide
|
||||
{
|
||||
if ([_target isHidden] == shouldHide)
|
||||
return;
|
||||
|
||||
if (shouldHide == NO)
|
||||
return [_target setHidden:NO];
|
||||
|
||||
[self _setTargetValue:YES withKeyPath:@"CPAnimationTriggerOrderOut" setter:_cmd];
|
||||
}
|
||||
|
||||
- (void)setAlphaValue:(CGPoint)alphaValue
|
||||
{
|
||||
[self _setTargetValue:alphaValue withKeyPath:@"alphaValue" setter:_cmd];
|
||||
}
|
||||
|
||||
- (void)setBackgroundColor:(CPColor)aColor
|
||||
{
|
||||
[self _setTargetValue:aColor withKeyPath:@"backgroundColor" setter:_cmd];
|
||||
}
|
||||
|
||||
- (void)setFrameOrigin:(CGPoint)aFrameOrigin
|
||||
{
|
||||
[self _setTargetValue:aFrameOrigin withKeyPath:@"frameOrigin" setter:_cmd];
|
||||
}
|
||||
|
||||
- (void)setFrame:(CGRect)aFrame
|
||||
{
|
||||
[self _setTargetValue:aFrame withKeyPath:@"frame" setter:_cmd];
|
||||
}
|
||||
|
||||
- (void)setFrameSize:(CGSize)aFrameSize
|
||||
{
|
||||
[self _setTargetValue:aFrameSize withKeyPath:@"frameSize" setter:_cmd];
|
||||
}
|
||||
|
||||
// Convenience method for the common case where the setter has zero or one argument
|
||||
- (void)_setTargetValue:(id)aTargetValue withKeyPath:(CPString)aKeyPath setter:(SEL)aSelector
|
||||
{
|
||||
var handler = function()
|
||||
{
|
||||
[_target _setForceUpdates:YES];
|
||||
[_target performSelector:aSelector withObject:aTargetValue];
|
||||
[_target _setForceUpdates:NO];
|
||||
};
|
||||
|
||||
[self _setTargetValue:aTargetValue withKeyPath:aKeyPath fallback:handler completion:handler];
|
||||
}
|
||||
|
||||
- (void)_setTargetValue:(id)aTargetValue withKeyPath:(CPString)aKeyPath fallback:(Function)fallback completion:(Function)completion
|
||||
{
|
||||
var animation = [_target animationForKey:aKeyPath],
|
||||
context = [CPAnimationContext currentContext];
|
||||
|
||||
if (!animation || ![animation isKindOfClass:[CAAnimation class]] || (![context duration] && ![animation duration]) || !CPFeatureIsCompatible(CPCSSAnimationFeature))
|
||||
{
|
||||
if (fallback)
|
||||
fallback();
|
||||
}
|
||||
else
|
||||
{
|
||||
[context _enqueueActionForObject:_target keyPath:aKeyPath targetValue:aTargetValue animationCompletion:completion];
|
||||
}
|
||||
}
|
||||
|
||||
+ (CPDictionary)_defaultCSSProperties
|
||||
{
|
||||
if (DEFAULT_CSS_PROPERTIES == nil)
|
||||
{
|
||||
var transformProperty = "transform";
|
||||
|
||||
DEFAULT_CSS_PROPERTIES = @{
|
||||
"backgroundColor" : [@{"property":"background", "value":function(sv, val){return [val cssString];}}],
|
||||
"alphaValue" : [@{"property":"opacity"}],
|
||||
"frame" : [@{"property":transformProperty, "value":frameToCSSTranslationTransformMatrix},
|
||||
@{"property":"width", "value":transformFrameToWidth},
|
||||
@{"property":"height", "value":transformFrameToHeight}],
|
||||
"frameOrigin" : [@{"property":transformProperty, "value":frameOriginToCSSTransformMatrix}],
|
||||
"frameSize" : [@{"property":"width", "value":transformSizeToWidth},
|
||||
@{"property":"height", "value":transformSizeToHeight}]
|
||||
};
|
||||
}
|
||||
|
||||
return DEFAULT_CSS_PROPERTIES;
|
||||
}
|
||||
|
||||
+ (void)addAnimations:(CPArray)animations forAction:(id)anAction
|
||||
{
|
||||
var target = anAction.object;
|
||||
|
||||
return [self _addAnimations:animations forAction:anAction domElement:[target _DOMElement] identifier:[target UID]];
|
||||
}
|
||||
|
||||
+ (void)_addAnimations:(CPArray)animations forAction:(id)anAction domElement:(Object)aDomElement identifier:(CPString)anIdentifier
|
||||
{
|
||||
var animation = [animations objectPassingTest:function(anim, idx, stop)
|
||||
{
|
||||
return anim.identifier == anIdentifier;
|
||||
}];
|
||||
|
||||
if (animation == nil)
|
||||
{
|
||||
animation = new CSSAnimation(aDomElement, anIdentifier, [anAction.object debug_description]);
|
||||
[animations addObject:animation];
|
||||
}
|
||||
|
||||
var css_mapping = [self _cssPropertiesForKeyPath:anAction.keypath];
|
||||
|
||||
[css_mapping enumerateObjectsUsingBlock:function(aDict, anIndex, stop)
|
||||
{
|
||||
var completionFunction = (anIndex == 0) ? anAction.completion : null,
|
||||
property = [aDict objectForKey:@"property"],
|
||||
getter = [aDict objectForKey:@"value"];
|
||||
|
||||
animation.addPropertyAnimation(property, getter, anAction.duration, anAction.keytimes, anAction.values, anAction.timingfunctions, completionFunction);
|
||||
}];
|
||||
}
|
||||
|
||||
+ (CPArray)_cssPropertiesForKeyPath:(CPString)aKeyPath
|
||||
{
|
||||
return [[self _defaultCSSProperties] objectForKey:aKeyPath];
|
||||
}
|
||||
|
||||
+ (void)addFrameUpdaters:(CPArray)frameUpdaters forAction:(id)anAction
|
||||
{
|
||||
var rootIdentifier = [anAction.root UID];
|
||||
|
||||
var frameUpdater = [frameUpdaters objectPassingTest:function(updater, idx, stop)
|
||||
{
|
||||
// There is one timer, linked to the top view, that updates the whole hierarchy.
|
||||
return updater.identifier() == rootIdentifier;
|
||||
}];
|
||||
|
||||
if (frameUpdater == nil)
|
||||
{
|
||||
frameUpdater = new FrameUpdater(rootIdentifier);
|
||||
[frameUpdaters addObject:frameUpdater];
|
||||
FRAME_UPDATERS[rootIdentifier] = frameUpdater;
|
||||
}
|
||||
|
||||
frameUpdater.addTarget(anAction.object, anAction.keypath, anAction.duration);
|
||||
}
|
||||
|
||||
+ (void)stopUpdaterWithIdentifier:(CPString)anIdentifier
|
||||
{
|
||||
var frameUpdater = FRAME_UPDATERS[anIdentifier];
|
||||
|
||||
if (frameUpdater)
|
||||
{
|
||||
frameUpdater.stop();
|
||||
delete FRAME_UPDATERS[anIdentifier];
|
||||
}
|
||||
else
|
||||
CPLog.warn("Could not find FrameUpdater with identifier " + anIdentifier);
|
||||
}
|
||||
|
||||
- (BOOL)needsPeriodicFrameUpdatesForKeyPath:(CPString)aKeyPath
|
||||
{
|
||||
return ((aKeyPath == @"frame" || aKeyPath == @"frameSize") &&
|
||||
(([_target hasCustomLayoutSubviews] && ![_target implementsSelector:@selector(frameRectOfView:inSuperviewSize:)])
|
||||
|| [_target hasCustomDrawRect]))
|
||||
|| [self wantsPeriodicFrameUpdates];
|
||||
}
|
||||
|
||||
@end
|
||||
|
||||
var transformFrameToWidth = function(start, current)
|
||||
{
|
||||
return current.size.width + "px";
|
||||
};
|
||||
|
||||
var transformFrameToHeight = function(start, current)
|
||||
{
|
||||
return current.size.height + "px";
|
||||
};
|
||||
|
||||
var transformSizeToWidth = function(start, current)
|
||||
{
|
||||
return current.width + "px";
|
||||
};
|
||||
|
||||
var transformSizeToHeight = function(start, current)
|
||||
{
|
||||
return current.height + "px";
|
||||
};
|
||||
|
||||
var CSSStringFromCGAffineTransform = function(anAffineTransform)
|
||||
{
|
||||
return [CPString stringWithFormat:@"matrix(%d,%d,%d,%d,%d,%d)", anAffineTransform.a, anAffineTransform.b, anAffineTransform.c, anAffineTransform.d, anAffineTransform.tx, anAffineTransform.ty];
|
||||
};
|
||||
|
||||
var frameOriginToCSSTransformMatrix = function(start, current)
|
||||
{
|
||||
var affine = CGAffineTransformMakeTranslation(current.x - start.x, current.y - start.y);
|
||||
|
||||
return CSSStringFromCGAffineTransform(affine);
|
||||
};
|
||||
|
||||
var frameToCSSTranslationTransformMatrix = function(start, current)
|
||||
{
|
||||
var affine = CGAffineTransformMakeTranslation(current.origin.x - start.origin.x, current.origin.y - start.origin.y);
|
||||
|
||||
return CSSStringFromCGAffineTransform(affine);
|
||||
};
|
||||
|
||||
@implementation CPView (CPAnimatablePropertyContainer)
|
||||
|
||||
+ (Class)animatorClass
|
||||
{
|
||||
var anim_class = CPClassFromString(CPStringFromClass(self) + "Animator");
|
||||
|
||||
if (anim_class)
|
||||
return anim_class;
|
||||
|
||||
return [[self superclass] animatorClass];
|
||||
}
|
||||
|
||||
- (id)animator
|
||||
{
|
||||
if (!_animator)
|
||||
_animator = [[[[self class] animatorClass] alloc] initWithTarget:self];
|
||||
|
||||
return _animator;
|
||||
}
|
||||
|
||||
+ (CAAnimation)defaultAnimationForKey:(CPString)aKey
|
||||
{
|
||||
// TODO: remove when supported.
|
||||
if (aKey == @"CPAnimationTriggerOrderIn")
|
||||
{
|
||||
CPLog.warn("CPView animated key path CPAnimationTriggerOrderIn is not supported yet.");
|
||||
return nil;
|
||||
}
|
||||
|
||||
if ([[self animatorClass] _cssPropertiesForKeyPath:aKey] !== nil)
|
||||
return [CAAnimation animation];
|
||||
|
||||
return nil;
|
||||
}
|
||||
|
||||
- (CAAnimation)animationForKey:(CPString)aKey
|
||||
{
|
||||
var animations = [self animations],
|
||||
animation = nil;
|
||||
|
||||
if (!animations || !(animation = [animations objectForKey:aKey]))
|
||||
{
|
||||
animation = [[self class] defaultAnimationForKey:aKey];
|
||||
}
|
||||
|
||||
return animation;
|
||||
}
|
||||
|
||||
- (CPDictionary)animations
|
||||
{
|
||||
return _animationsDictionary;
|
||||
}
|
||||
|
||||
- (void)setAnimations:(CPDictionary)animationsDict
|
||||
{
|
||||
_animationsDictionary = [animationsDict copy];
|
||||
}
|
||||
|
||||
- (Object)_DOMElement
|
||||
{
|
||||
return _DOMElement;
|
||||
}
|
||||
|
||||
- (CPString)debug_description
|
||||
{
|
||||
return [self identifier] || [self className];
|
||||
}
|
||||
|
||||
@end
|
||||
|
||||
@implementation CPArray (Additions)
|
||||
|
||||
- (CPArray)objectPassingTest:(Function)aFunction
|
||||
{
|
||||
var idx = [self indexOfObjectPassingTest:aFunction];
|
||||
|
||||
if (idx !== CPNotFound)
|
||||
return [self objectAtIndex:idx];
|
||||
|
||||
return nil;
|
||||
}
|
||||
@end
|
||||
|
||||
var FrameUpdater = function(anIdentifier)
|
||||
{
|
||||
this._identifier = anIdentifier;
|
||||
this._requestId = null;
|
||||
this._duration = 0;
|
||||
this._stop = false;
|
||||
this._targets = [];
|
||||
this._callbacks = [];
|
||||
|
||||
var frameUpdater = this;
|
||||
|
||||
this._updateFunction = function(timestamp)
|
||||
{
|
||||
if (frameUpdater._startDate == null)
|
||||
frameUpdater._startDate = timestamp;
|
||||
|
||||
if (frameUpdater._stop)
|
||||
return;
|
||||
|
||||
for (var i = 0; i < frameUpdater._callbacks.length; i++)
|
||||
frameUpdater._callbacks[i]();
|
||||
|
||||
if (timestamp - frameUpdater._startDate < frameUpdater._duration * 1000)
|
||||
window.requestAnimationFrame(frameUpdater._updateFunction);
|
||||
};
|
||||
};
|
||||
|
||||
FrameUpdater.prototype.start = function()
|
||||
{
|
||||
this._requestId = window.requestAnimationFrame(this._updateFunction);
|
||||
};
|
||||
|
||||
FrameUpdater.prototype.stop = function()
|
||||
{
|
||||
CPLog.warn("STOP FrameUpdater" + this._identifier);
|
||||
|
||||
// window.cancelAnimationFrame support is Chrome 24, Firefox 23, IE 10, Opera 15, Safari 6.1
|
||||
if (window.cancelAnimationFrame)
|
||||
window.cancelAnimationFrame(this._requestId);
|
||||
|
||||
this._stop = true;
|
||||
};
|
||||
|
||||
FrameUpdater.prototype.updateFunction = function()
|
||||
{
|
||||
return this._updateFunction;
|
||||
};
|
||||
|
||||
FrameUpdater.prototype.identifier = function()
|
||||
{
|
||||
return this._identifier;
|
||||
};
|
||||
|
||||
FrameUpdater.prototype.description = function()
|
||||
{
|
||||
return "<timer " + this._identifier + " " + this._targets.map(function(t){return [t debug_description];}) + ">";
|
||||
};
|
||||
|
||||
FrameUpdater.prototype.addTarget = function(target, keyPath, duration)
|
||||
{
|
||||
var callback = createUpdateFrame(target, keyPath);
|
||||
|
||||
if (callback)
|
||||
{
|
||||
this._duration = MAX(this._duration, duration);
|
||||
this._targets.push(target);
|
||||
this._callbacks.push(callback);
|
||||
}
|
||||
};
|
||||
|
||||
var createUpdateFrame = function(aView, aKeyPath)
|
||||
{
|
||||
if (aKeyPath !== "frame" && aKeyPath !== "frameSize" && aKeyPath !== "frameOrigin")
|
||||
return nil;
|
||||
|
||||
var style = getComputedStyle([aView _DOMElement]),
|
||||
getCSSPropertyValue = function(prop) {
|
||||
return ROUND(parseFloat(style.getPropertyValue(prop)));
|
||||
},
|
||||
initialOrigin = CGPointMakeCopy([aView frameOrigin]),
|
||||
transformProperty = CPBrowserStyleProperty("transform"),
|
||||
updateFrame = function(timestamp)
|
||||
{
|
||||
if (aKeyPath === "frameSize")
|
||||
{
|
||||
var width = getCSSPropertyValue("width"),
|
||||
height = getCSSPropertyValue("height");
|
||||
|
||||
[aView setFrameSize:CGSizeMake(width, height)];
|
||||
}
|
||||
else
|
||||
{
|
||||
[aView _setInhibitDOMUpdates:YES];
|
||||
|
||||
var matrix_value = style[transformProperty];
|
||||
|
||||
if (matrix_value && matrix_value !== 'none')
|
||||
{
|
||||
var matrix_array = matrix_value.split('(')[1].split(')')[0].split(','),
|
||||
x = ROUND(initialOrigin.x + parseFloat(matrix_array[4])),
|
||||
y = ROUND(initialOrigin.y + parseFloat(matrix_array[5]));
|
||||
|
||||
if (aKeyPath === "frame")
|
||||
{
|
||||
var width = getCSSPropertyValue("width"),
|
||||
height = getCSSPropertyValue("height");
|
||||
|
||||
[aView setFrame:CGRectMake(x, y, width, height)];
|
||||
}
|
||||
else
|
||||
{
|
||||
[aView setFrameOrigin:CGPointMake(x, y)];
|
||||
}
|
||||
}
|
||||
|
||||
[aView _setInhibitDOMUpdates:NO];
|
||||
}
|
||||
|
||||
[[CPRunLoop currentRunLoop] performSelectors];
|
||||
// CPLog.debug("update " + [aView debug_description]);
|
||||
};
|
||||
|
||||
return updateFrame;
|
||||
};
|
||||
@@ -0,0 +1,308 @@
|
||||
|
||||
var ANIMATIONS_GLOBAL_ID = 0,
|
||||
CURRENT_ANIMATIONS = {},
|
||||
|
||||
ANIMATION_END_EVENT_NAME,
|
||||
ANIMATION_PROPERTY,
|
||||
ANIMATION_NAME_PROPERTY,
|
||||
ANIMATION_DURATION_PROPERTY,
|
||||
ANIMATION_TIMING_FUNCTION_PROPERTY,
|
||||
ANIMATION_FILL_MODE_PROPERTY,
|
||||
ANIMATION_KEYFRAMES_RULE;
|
||||
|
||||
var defineCSSProperties = function()
|
||||
{
|
||||
if (this.done)
|
||||
return;
|
||||
|
||||
ANIMATION_END_EVENT_NAME = CPBrowserStyleProperty("animationend");
|
||||
ANIMATION_PROPERTY = CPBrowserCSSProperty("animation");
|
||||
ANIMATION_NAME_PROPERTY = CPBrowserCSSProperty("animation-name");
|
||||
ANIMATION_DURATION_PROPERTY = CPBrowserCSSProperty("animation-duration");
|
||||
ANIMATION_TIMING_FUNCTION_PROPERTY = CPBrowserCSSProperty("animation-timing-function");
|
||||
ANIMATION_FILL_MODE_PROPERTY = CPBrowserCSSProperty("animation-fill-mode");
|
||||
ANIMATION_KEYFRAMES_RULE = "@" + ANIMATION_PROPERTY.substring(0, ANIMATION_PROPERTY.indexOf("animation")) + "keyframes";
|
||||
|
||||
this.done = true;
|
||||
};
|
||||
|
||||
var removeFromParent = function(aNode)
|
||||
{
|
||||
var parentNode = aNode.parentNode;
|
||||
|
||||
if (parentNode)
|
||||
parentNode.removeChild(aNode);
|
||||
};
|
||||
|
||||
CSSAnimation = function(aTarget/* DOM Element */, anIdentifier, aTargetName)
|
||||
{
|
||||
defineCSSProperties();
|
||||
|
||||
if (!anIdentifier)
|
||||
anIdentifier = ANIMATIONS_GLOBAL_ID++;
|
||||
|
||||
var animationName = "anim_" + anIdentifier,
|
||||
animation = CURRENT_ANIMATIONS[anIdentifier];
|
||||
|
||||
if (animation)
|
||||
{
|
||||
console.warn("Animation " + anIdentifier + " is already in use. Ignoring.");
|
||||
}
|
||||
else
|
||||
{
|
||||
this.target = aTarget;
|
||||
this.identifier = anIdentifier;
|
||||
this.targetName = aTargetName;
|
||||
this.animationName = animationName;
|
||||
this.listener = null;
|
||||
this.styleElement = null;
|
||||
this.propertyanimations = [];
|
||||
this.animationsnames = [];
|
||||
this.animationstimingfunctions = [];
|
||||
this.animationsdurations = [];
|
||||
this.islive = false;
|
||||
this.didBuildDOMElements = false;
|
||||
|
||||
animation = this;
|
||||
CURRENT_ANIMATIONS[anIdentifier] = animation;
|
||||
}
|
||||
|
||||
return animation;
|
||||
};
|
||||
|
||||
CSSAnimation.prototype.description = function()
|
||||
{
|
||||
return "<animation " + this.identifier + " target=" + this.targetName + " properties=" + this.propertyanimations.map(function(anim){return anim.property;}).join(",") + " >";
|
||||
}
|
||||
|
||||
CSSAnimation.prototype.addPropertyAnimation = function(propertyName/* String */, valueFunction/* Function */, aDuration/* float */, aKeyTimes/* (d, [d]) */, aValues/* Array */, aTimingFunctions/* [d,d,d,d], [[d,d,d,d]] */, aCompletionfunction/* Function */)
|
||||
{
|
||||
if (this.islive)
|
||||
return false;
|
||||
// TODO: If a property already exist, replace its values & valueFunctions.
|
||||
|
||||
var fullAnimationName = this.animationName + "_" + propertyName;
|
||||
|
||||
var animation = {
|
||||
name: fullAnimationName,
|
||||
property: propertyName,
|
||||
valuefunction: valueFunction,
|
||||
keytimes: aKeyTimes,
|
||||
values: aValues,
|
||||
duration: aDuration,
|
||||
completionfunction: aCompletionfunction };
|
||||
|
||||
var animationTimingFunction;
|
||||
|
||||
if (aTimingFunctions && (aTimingFunctions[0] instanceof Array))
|
||||
{
|
||||
animation.keyframestimingFunctions = aTimingFunctions;
|
||||
// dummy timing function overriden by keyframes timings functions
|
||||
animationTimingFunction = "linear";
|
||||
}
|
||||
else
|
||||
{
|
||||
animationTimingFunction = "cubic-bezier(" + aTimingFunctions + ")";
|
||||
}
|
||||
|
||||
this.animationstimingfunctions.push(animationTimingFunction);
|
||||
|
||||
this.propertyanimations.push(animation);
|
||||
this.animationsnames.push(fullAnimationName);
|
||||
this.animationsdurations.push(aDuration + "s");
|
||||
|
||||
return true;
|
||||
};
|
||||
|
||||
CSSAnimation.prototype.keyFrames = function()
|
||||
{
|
||||
var keyframesRules = [];
|
||||
|
||||
var count = this.propertyanimations.length;
|
||||
|
||||
for (var i = 0; i < count; i++)
|
||||
{
|
||||
var animation = this.propertyanimations[i],
|
||||
property = animation.property,
|
||||
valuefunction = animation.valuefunction,
|
||||
keytimes = animation.keytimes,
|
||||
values = animation.values,
|
||||
timingFunctions = animation.keyframestimingFunctions;
|
||||
|
||||
var keyframes = [],
|
||||
keytimescount = keytimes.length,
|
||||
start_value = values[0];
|
||||
|
||||
for (var j = 0; j < keytimescount; j++)
|
||||
{
|
||||
var keytime = keytimes[j],
|
||||
value = values[j],
|
||||
timingFunction;
|
||||
|
||||
if (valuefunction !== null)
|
||||
value = valuefunction(start_value, value);
|
||||
|
||||
var keyframeContent = property + ": " + value + ";";
|
||||
|
||||
if (timingFunctions && timingFunctions.length && (timingFunction = timingFunctions[j]))
|
||||
{
|
||||
keyframeContent += ANIMATION_TIMING_FUNCTION_PROPERTY + ":cubic-bezier(" + timingFunction + ");";
|
||||
}
|
||||
|
||||
var keyframe = "\t" + Math.round(keytime * 100) + "% {\n\t\t" + keyframeContent + "\n\t}\n";
|
||||
|
||||
keyframes.push(keyframe);
|
||||
}
|
||||
// TODO ! Add keyframe rule to CPCompatibility
|
||||
var rule = ANIMATION_KEYFRAMES_RULE + " " + animation.name + " {\n" + keyframes.join(" ") + "}\n";
|
||||
|
||||
keyframesRules.push(rule);
|
||||
}
|
||||
|
||||
return keyframesRules.join("\n");
|
||||
};
|
||||
|
||||
CSSAnimation.prototype.appendKeyFramesRule = function()
|
||||
{
|
||||
var styleElement = this.createKeyFramesStyleElement(),
|
||||
keyframesText = this.keyFrames(),
|
||||
nodeText = document.createTextNode(keyframesText);
|
||||
|
||||
styleElement.appendChild(nodeText);
|
||||
document.head.appendChild(styleElement);
|
||||
};
|
||||
|
||||
CSSAnimation.prototype.createKeyFramesStyleElement = function()
|
||||
{
|
||||
if (!this.styleElement)
|
||||
{
|
||||
var styleElement = document.createElement("style");
|
||||
|
||||
styleElement.setAttribute("type", "text/css");
|
||||
|
||||
this.styleElement = styleElement;
|
||||
}
|
||||
|
||||
return this.styleElement;
|
||||
};
|
||||
|
||||
CSSAnimation.prototype.endEventListener = function()
|
||||
{
|
||||
var animation = this,
|
||||
animationsNames = this.animationsnames,
|
||||
inFlightAnimationsNames = animationsNames.slice();
|
||||
|
||||
if (!animation.listener)
|
||||
{
|
||||
var AnimationEndListener = function(event)
|
||||
{
|
||||
var idx = inFlightAnimationsNames.indexOf(event.animationName);
|
||||
|
||||
if (idx !== -1)
|
||||
inFlightAnimationsNames.splice(idx, 1);
|
||||
|
||||
if (inFlightAnimationsNames.length > 0)
|
||||
return;
|
||||
|
||||
for (var i = 0; i < animationsNames.length; i++)
|
||||
{
|
||||
var completion = animation.completionFunctionForAnimationName(animationsNames[i]);
|
||||
|
||||
if (completion)
|
||||
completion();
|
||||
}
|
||||
|
||||
var eventTarget = event.target,
|
||||
style = eventTarget.style;
|
||||
|
||||
try
|
||||
{
|
||||
style.removeProperty(ANIMATION_NAME_PROPERTY);
|
||||
style.removeProperty(ANIMATION_DURATION_PROPERTY);
|
||||
style.removeProperty(ANIMATION_FILL_MODE_PROPERTY);
|
||||
style.removeProperty("-webkit-backface-visibility");
|
||||
|
||||
if (animation.animationstimingfunctions.length)
|
||||
style.removeProperty(ANIMATION_TIMING_FUNCTION_PROPERTY);
|
||||
|
||||
removeFromParent(animation.styleElement);
|
||||
|
||||
eventTarget.removeEventListener(ANIMATION_END_EVENT_NAME, AnimationEndListener);
|
||||
animation.listener = null;
|
||||
delete (CURRENT_ANIMATIONS[animation.identifier]);
|
||||
}
|
||||
catch (err)
|
||||
{
|
||||
console.warn("CSSAnimation.j - " + err);
|
||||
}
|
||||
};
|
||||
|
||||
this.listener = AnimationEndListener;
|
||||
}
|
||||
|
||||
return this.listener;
|
||||
};
|
||||
|
||||
CSSAnimation.prototype.completionFunctionForAnimationName = function(aName)
|
||||
{
|
||||
var propanims = this.propertyanimations,
|
||||
count = propanims.length;
|
||||
|
||||
while (count--)
|
||||
{
|
||||
var anim = propanims[count];
|
||||
|
||||
if (anim.name === aName)
|
||||
return anim.completionfunction;
|
||||
}
|
||||
|
||||
return null;
|
||||
};
|
||||
|
||||
CSSAnimation.prototype.addAnimationEndEventListener = function()
|
||||
{
|
||||
var listener = this.endEventListener();
|
||||
|
||||
this.target.addEventListener(ANIMATION_END_EVENT_NAME, listener, false);
|
||||
};
|
||||
|
||||
CSSAnimation.prototype.setTargetStyleProperties = function()
|
||||
{
|
||||
var style = this.target.style;
|
||||
|
||||
if (this.animationstimingfunctions.length)
|
||||
style.setProperty(ANIMATION_TIMING_FUNCTION_PROPERTY, this.animationstimingfunctions.join(","));
|
||||
|
||||
style.setProperty(ANIMATION_DURATION_PROPERTY, this.animationsdurations.join(","));
|
||||
|
||||
style.setProperty(ANIMATION_FILL_MODE_PROPERTY, "forwards");
|
||||
|
||||
// http://webdesign.tutsplus.com/tutorials/htmlcss-tutorials/css3-animations-the-hiccups-and-bugs-youll-want-to-avoid/
|
||||
style.setProperty("-webkit-backface-visibility", "hidden");
|
||||
};
|
||||
|
||||
CSSAnimation.prototype.buildDOMElements = function()
|
||||
{
|
||||
this.appendKeyFramesRule();
|
||||
|
||||
this.addAnimationEndEventListener();
|
||||
|
||||
this.setTargetStyleProperties();
|
||||
|
||||
this.didBuildDOMElements = true;
|
||||
};
|
||||
|
||||
CSSAnimation.prototype.start = function()
|
||||
{
|
||||
if (this.propertyanimations.length === 0 || this.islive)
|
||||
return false;
|
||||
|
||||
if (!this.didBuildDOMElements)
|
||||
this.buildDOMElements();
|
||||
|
||||
this.target.style.setProperty(ANIMATION_NAME_PROPERTY, this.animationsnames.join(","));
|
||||
|
||||
this.islive = true;
|
||||
|
||||
return true;
|
||||
};
|
||||
@@ -0,0 +1,92 @@
|
||||
|
||||
@import <Foundation/CPProxy.j>
|
||||
@import "CPAnimationContext.j"
|
||||
|
||||
var _supportsCSSAnimations = null;
|
||||
|
||||
@protocol CPAnimatablePropertyContainer <CPObject>
|
||||
|
||||
+ (id)defaultAnimationForKey:(CPString)key;
|
||||
- (id)animationForKey:(CPString)key;
|
||||
|
||||
- (id)animator;
|
||||
- (CPDictionary)animations;
|
||||
- (void)setAnimations:(CPDictionary)animations;
|
||||
|
||||
@end
|
||||
|
||||
@implementation _CPObjectAnimator : CPProxy
|
||||
{
|
||||
id <CPAnimatablePropertyContainer> _target;
|
||||
}
|
||||
|
||||
+ (void)initialize
|
||||
{
|
||||
if ([self class] !== [_CPObjectAnimator class])
|
||||
return;
|
||||
|
||||
var compat = (CPBrowserCSSProperty("animation") !== nil);
|
||||
CPSetPlatformFeature(CPCSSAnimationFeature, compat);
|
||||
}
|
||||
|
||||
- (id)initWithTarget:(id)aTarget
|
||||
{
|
||||
_target = aTarget;
|
||||
|
||||
return self;
|
||||
}
|
||||
|
||||
- (id)animator
|
||||
{
|
||||
return self;
|
||||
}
|
||||
|
||||
- (BOOL)isEqual:(id)anObject
|
||||
{
|
||||
return [_target isEqual:anObject];
|
||||
}
|
||||
|
||||
- (id)forwardingTargetForSelector:(SEL)aSelector
|
||||
{
|
||||
return _target;
|
||||
}
|
||||
|
||||
- (CPMethodSignature)methodSignatureForSelector:(SEL)aSelector
|
||||
{
|
||||
return [_target methodSignatureForSelector:aSelector];
|
||||
}
|
||||
|
||||
- (void)forwardInvocation:(CPInvocation)anInvocation
|
||||
{
|
||||
var target = [self forwardingTargetForSelector:[anInvocation selector]];
|
||||
[anInvocation invokeWithTarget:target];
|
||||
return;
|
||||
}
|
||||
|
||||
- (void)doesNotRecognizeSelector:(SEL)aSelector
|
||||
{
|
||||
[CPException raise:CPInvalidArgumentException reason:@"Animator does not recognize selector " + CPStringFromSelector(aSelector)];
|
||||
}
|
||||
|
||||
- (CPString)description
|
||||
{
|
||||
return [CPString stringWithFormat:@"%@ Animator Proxy for %@", [self class], _target];
|
||||
}
|
||||
|
||||
- (void)setValue:(id)aTargetValue forKey:(id)aKeyPath
|
||||
{
|
||||
var animation = [_target animationForKey:aKeyPath],
|
||||
context = [CPAnimationContext currentContext];
|
||||
|
||||
if (!animation || ![animation isKindOfClass:[CAAnimation class]] || (![context duration] && ![animation duration]) || !CPFeatureIsCompatible(CPCSSAnimationFeature))
|
||||
[_target setValue:aTargetValue forKey:aKeyPath];
|
||||
else
|
||||
{
|
||||
[context _enqueueActionForObject:_target keyPath:aKeyPath targetValue:aTargetValue animationCompletion:function()
|
||||
{
|
||||
[_target setValue:aTargetValue forKey:aKeyPath];
|
||||
}];
|
||||
}
|
||||
}
|
||||
|
||||
@end
|
||||
@@ -24,6 +24,7 @@
|
||||
@import "CPCompatibility.j"
|
||||
@import "CGGeometry.j"
|
||||
@import "CGPath.j"
|
||||
@import "CGContextText.j"
|
||||
|
||||
@typedef CGContext
|
||||
|
||||
|
||||
@@ -557,14 +557,14 @@ CGContextConcatCTM = function(aContext, anAffineTransform)
|
||||
b = VT.b;
|
||||
c = VT.c;
|
||||
d = VT.d;
|
||||
scale_rotate(a, b, c, d)
|
||||
scale_rotate(a, b, c, d);
|
||||
S.a *= sx;
|
||||
S.d *= sy;
|
||||
a = U.a;
|
||||
b = U.b;
|
||||
c = U.c;
|
||||
d = U.d;
|
||||
rotate_scale(a, b, c, d)
|
||||
rotate_scale(a, b, c, d);
|
||||
sx = S.a * sx;
|
||||
sy = S.d * sy;
|
||||
}
|
||||
|
||||
@@ -0,0 +1,86 @@
|
||||
/*
|
||||
* CGContextText.j
|
||||
* CoreText
|
||||
*
|
||||
* Created by Nicholas Small.
|
||||
* Copyright 2011, 280 North, Inc.
|
||||
*
|
||||
* This library is free software; you can redistribute it and/or
|
||||
* modify it under the terms of the GNU Lesser General Public
|
||||
* License as published by the Free Software Foundation; either
|
||||
* version 2.1 of the License, or (at your option) any later version.
|
||||
*
|
||||
* This library is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
|
||||
* Lesser General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU Lesser General Public
|
||||
* License along with this library; if not, write to the Free Software
|
||||
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
|
||||
*/
|
||||
|
||||
kCGTextFill = 0;
|
||||
kCGTextStroke = 1;
|
||||
kCGTextFillStroke = 2;
|
||||
kCGTextInvisible = 3;
|
||||
|
||||
function CGContextGetTextMatrix(/* CGContext */ aContext)
|
||||
{
|
||||
return aContext._textMatrix;
|
||||
}
|
||||
|
||||
function CGContextSetTextMatrix(/* CGContext */ aContext, /* CGAffineTransform */ aTransform)
|
||||
{
|
||||
aContext._textMatrix = aTransform;
|
||||
}
|
||||
|
||||
function CGContextGetTextPosition(/* CGContext */ aContext)
|
||||
{
|
||||
return aContext._textPosition || _CGPointMakeZero();
|
||||
}
|
||||
|
||||
function CGContextSetTextPosition(/* CGContext */ aContext, /* float */ x, /* float */ y)
|
||||
{
|
||||
aContext._textPosition = CGPointMake(x, y);
|
||||
}
|
||||
|
||||
function CGContextGetFont(/* CGContext */ aContext)
|
||||
{
|
||||
return aContext._CPFont;
|
||||
}
|
||||
|
||||
function CGContextSelectFont(/* CGContext */ aContext, /* CPFont */ aFont)
|
||||
{
|
||||
aContext.font = [aFont cssString];
|
||||
aContext._CPFont = aFont;
|
||||
}
|
||||
|
||||
function CGContextSetTextDrawingMode(/* CGContext */ aContext, /* CGTextDrawingMode */ aMode)
|
||||
{
|
||||
aContext._textDrawingMode = aMode;
|
||||
}
|
||||
|
||||
function CGContextShowText(/* CGContext */ aContext, /* CPString */ aString)
|
||||
{
|
||||
CGContextShowTextAtPoint(aContext, aContext._textPosition.x, aContext._textPosition.y, aString);
|
||||
}
|
||||
|
||||
function CGContextShowTextAtPoint(/* CGContext */ aContext, /* float */ x, /* float */ y, /* CPString */ aString)
|
||||
{
|
||||
aContext.textBaseline = @"middle";
|
||||
aContext.textAlign = @"left";
|
||||
|
||||
var mode = aContext._textDrawingMode;
|
||||
if (!mode && mode !== 0)
|
||||
mode = kCGTextFill;
|
||||
|
||||
var width = aContext.measureText(aString).width;
|
||||
|
||||
if (mode === kCGTextFill || mode === kCGTextFillStroke)
|
||||
aContext.fillText(aString, x, y);
|
||||
if (mode === kCGTextStroke || mode === kCGTextFillStroke)
|
||||
aContext.strokeText(aString, x, y);
|
||||
|
||||
aContext._textPosition = CGPointMake(x + width, y);
|
||||
}
|
||||
@@ -242,7 +242,7 @@ function CGContextDrawPath(aContext, aMode)
|
||||
vml.push("\">");
|
||||
|
||||
if (gState.gradient)
|
||||
vml.push(gState.gradient)
|
||||
vml.push(gState.gradient);
|
||||
|
||||
else if (fill)
|
||||
{
|
||||
|
||||
@@ -132,9 +132,6 @@ function CGPathAddArc(aPath, aTransform, x, y, aRadius, aStartAngle, anEndAngle,
|
||||
}
|
||||
else
|
||||
{
|
||||
var arcStartX = x + aRadius * COS(aStartAngle),
|
||||
arcStartY = y + aRadius * SIN(aStartAngle);
|
||||
|
||||
aPath.start = CGPointMake(arcStartX, arcStartY);
|
||||
}
|
||||
|
||||
@@ -274,7 +271,7 @@ function CGPathAddQuadCurveToPoint(aPath, aTransform, cpx, cpy, x, y)
|
||||
end = CGPointApplyAffineTransform(end, aTransform);
|
||||
}
|
||||
|
||||
aPath.elements[aPath.count++] = { type:kCGPathElementAddQuadCurveToPoint, cpx:cp.x, cpy:cp.y, x:end.x, y:end.y }
|
||||
aPath.elements[aPath.count++] = { type:kCGPathElementAddQuadCurveToPoint, cpx:cp.x, cpy:cp.y, x:end.x, y:end.y };
|
||||
aPath.current = end;
|
||||
}
|
||||
|
||||
|
||||
+1
-1
@@ -37,7 +37,7 @@ appKitTask = framework ("AppKit", function(appKitTask)
|
||||
appKitTask.setCompilerFlags("-O2 " + INCLUDES);
|
||||
}
|
||||
else
|
||||
appKitTask.setCompilerFlags("-DDEBUG -g " + INCLUDES);
|
||||
appKitTask.setCompilerFlags("-DDEBUG -g -S --inline-msg-send " + INCLUDES);
|
||||
});
|
||||
|
||||
$BUILD_CJS_CAPPUCCINO_APPKIT = FILE.join($BUILD_CJS_CAPPUCCINO_FRAMEWORKS, "AppKit");
|
||||
|
||||
@@ -60,6 +60,7 @@ var PrimaryPlatformWindow = NULL;
|
||||
|
||||
BOOL _mouseIsDown;
|
||||
BOOL _mouseDownIsRightClick;
|
||||
int _firstMouseDownButton;
|
||||
CGPoint _lastMouseEventLocation;
|
||||
CPWindow _mouseDownWindow;
|
||||
CPTimeInterval _lastMouseUp;
|
||||
@@ -223,18 +224,10 @@ var PrimaryPlatformWindow = NULL;
|
||||
|
||||
- (void)deminiaturize:(id)sender
|
||||
{
|
||||
#if PLATFORM(DOM)
|
||||
if (_DOMWindow && typeof _DOMWindow["cpDeminiaturize"] === "function")
|
||||
_DOMWindow.cpDeminiaturize();
|
||||
#endif
|
||||
}
|
||||
|
||||
- (void)miniaturize:(id)sender
|
||||
{
|
||||
#if PLATFORM(DOM)
|
||||
if (_DOMWindow && typeof _DOMWindow["cpMiniaturize"] === "function")
|
||||
_DOMWindow.cpMiniaturize();
|
||||
#endif
|
||||
}
|
||||
|
||||
- (void)moveWindow:(CPWindow)aWindow fromLevel:(int)fromLevel toLevel:(int)toLevel
|
||||
@@ -254,31 +247,16 @@ var PrimaryPlatformWindow = NULL;
|
||||
- (void)setLevel:(CPInteger)aLevel
|
||||
{
|
||||
_level = aLevel;
|
||||
|
||||
#if PLATFORM(DOM)
|
||||
if (_DOMWindow && _DOMWindow.cpSetLevel)
|
||||
_DOMWindow.cpSetLevel(aLevel);
|
||||
#endif
|
||||
}
|
||||
|
||||
- (void)setHasShadow:(BOOL)shouldHaveShadow
|
||||
{
|
||||
_hasShadow = shouldHaveShadow;
|
||||
|
||||
#if PLATFORM(DOM)
|
||||
if (_DOMWindow && _DOMWindow.cpSetHasShadow)
|
||||
_DOMWindow.cpSetHasShadow(shouldHaveShadow);
|
||||
#endif
|
||||
}
|
||||
|
||||
- (void)setShadowStyle:(int)aStyle
|
||||
{
|
||||
_shadowStyle = aStyle;
|
||||
|
||||
#if PLATFORM(DOM)
|
||||
if (_DOMWindow && _DOMWindow.cpSetShadowStyle)
|
||||
_shadowStyle.cpSetShadowStyle(aStyle);
|
||||
#endif
|
||||
}
|
||||
|
||||
- (BOOL)supportsFullPlatformWindows
|
||||
|
||||
@@ -384,9 +384,10 @@ Return true if the event may be a copy and paste event, but the target is not an
|
||||
|
||||
if ([value length])
|
||||
{
|
||||
var pasteboard = [CPPasteboard generalPasteboard];
|
||||
var pasteboard = [CPPasteboard generalPasteboard],
|
||||
cappString = [pasteboard stringForType:CPStringPboardType];
|
||||
|
||||
if ([pasteboard _stateUID] != value)
|
||||
if (cappString != value)
|
||||
{
|
||||
[pasteboard declareTypes:[CPStringPboardType] owner:self];
|
||||
[pasteboard setString:value forType:CPStringPboardType];
|
||||
@@ -450,9 +451,10 @@ Return true if the event may be a copy and paste event, but the target is not an
|
||||
|
||||
if ([value length])
|
||||
{
|
||||
var pasteboard = [CPPasteboard generalPasteboard];
|
||||
var pasteboard = [CPPasteboard generalPasteboard],
|
||||
cappString = [pasteboard stringForType:CPStringPboardType];
|
||||
|
||||
if ([pasteboard _stateUID] != value)
|
||||
if (cappString != value)
|
||||
{
|
||||
[pasteboard declareTypes:[CPStringPboardType] owner:self];
|
||||
[pasteboard setString:value forType:CPStringPboardType];
|
||||
@@ -467,7 +469,7 @@ Return true if the event may be a copy and paste event, but the target is not an
|
||||
// By default we'll stop the native handling of the event since we're handling it ourselves. However, we need to
|
||||
// stop it before we send the event so that the event can overrule our choice. CPTextField for instance wants the
|
||||
// default handling when focused (which is to insert into the field).
|
||||
[platformWindow _propagateCurrentDOMEvent:NO]
|
||||
[platformWindow _propagateCurrentDOMEvent:NO];
|
||||
|
||||
[CPApp sendEvent:anEvent];
|
||||
|
||||
@@ -490,7 +492,7 @@ Return true if the event may be a copy and paste event, but the target is not an
|
||||
|
||||
SUPPRESS_CAPPUCCINO_CUT_FOR_EVENT(anEvent);
|
||||
|
||||
[platformWindow _propagateCurrentDOMEvent:NO]
|
||||
[platformWindow _propagateCurrentDOMEvent:NO];
|
||||
|
||||
// Let the app react through copy: and cut: actions.
|
||||
[CPApp sendEvent:anEvent];
|
||||
|
||||
@@ -171,7 +171,9 @@ var DOMFixedWidthSpanElement = nil,
|
||||
span.style.width = ROUND(aWidth) + "px";
|
||||
}
|
||||
|
||||
span.style.font = [(aFont || DefaultFont) cssString];
|
||||
var effectiveFontCSSString = [(aFont || DefaultFont) cssString];
|
||||
if (span.style.font !== effectiveFontCSSString)
|
||||
span.style.font = effectiveFontCSSString;
|
||||
|
||||
if (CPFeatureIsCompatible(CPJavaScriptInnerTextFeature))
|
||||
span.innerText = aString;
|
||||
|
||||
@@ -205,6 +205,9 @@ var resizeTimer = nil;
|
||||
var PreventScroll = true;
|
||||
var blurTimer = nil;
|
||||
|
||||
var touchStartingPointX,
|
||||
touchStartingPointY;
|
||||
|
||||
_CPPlatformWindowWillCloseNotification = @"_CPPlatformWindowWillCloseNotification";
|
||||
|
||||
|
||||
@@ -244,9 +247,6 @@ _CPPlatformWindowWillCloseNotification = @"_CPPlatformWindowWillCloseNotificatio
|
||||
if (!_DOMWindow)
|
||||
return [self contentRect];
|
||||
|
||||
if (_DOMWindow.cpFrame)
|
||||
return _DOMWindow.cpFrame();
|
||||
|
||||
var contentRect = CGRectMakeZero();
|
||||
|
||||
if (window.screenTop)
|
||||
@@ -275,9 +275,6 @@ _CPPlatformWindowWillCloseNotification = @"_CPPlatformWindowWillCloseNotificatio
|
||||
if (!_DOMWindow)
|
||||
return;
|
||||
|
||||
if (typeof _DOMWindow["cpSetFrame"] === "function")
|
||||
return _DOMWindow.cpSetFrame([self contentRect]);
|
||||
|
||||
var origin = [self contentRect].origin,
|
||||
nativeOrigin = [self nativeContentRect].origin;
|
||||
|
||||
@@ -573,22 +570,12 @@ _CPPlatformWindowWillCloseNotification = @"_CPPlatformWindowWillCloseNotificatio
|
||||
|
||||
[PlatformWindows addObject:self];
|
||||
|
||||
// FIXME: cpSetFrame?
|
||||
_DOMWindow.document.write('<!DOCTYPE html><html lang="en"><head><meta charset="utf-8"></head><body style="background-color:transparent; overflow:hidden"></body></html>');
|
||||
_DOMWindow.document.close();
|
||||
|
||||
if (self != [CPPlatformWindow primaryPlatformWindow])
|
||||
_DOMWindow.document.title = _title;
|
||||
|
||||
if (![CPPlatform isBrowser])
|
||||
{
|
||||
_DOMWindow.cpWindowNumber = [self._only windowNumber];
|
||||
_DOMWindow.cpSetFrame(_contentRect);
|
||||
_DOMWindow.cpSetLevel(_level);
|
||||
_DOMWindow.cpSetHasShadow(_hasShadow);
|
||||
_DOMWindow.cpSetShadowStyle(_shadowStyle);
|
||||
}
|
||||
|
||||
[self registerDOMWindow];
|
||||
|
||||
_DOMBodyElement.style.cursor = [[CPCursor currentCursor] _cssString];
|
||||
@@ -666,7 +653,8 @@ _CPPlatformWindowWillCloseNotification = @"_CPPlatformWindowWillCloseNotificatio
|
||||
}
|
||||
else if (type === "dragend")
|
||||
{
|
||||
var dropEffect = aDOMEvent.dataTransfer.dropEffect;
|
||||
var dropEffect = aDOMEvent.dataTransfer.dropEffect,
|
||||
dragOperation;
|
||||
|
||||
if (dropEffect === "move")
|
||||
dragOperation = CPDragOperationMove;
|
||||
@@ -944,16 +932,15 @@ _CPPlatformWindowWillCloseNotification = @"_CPPlatformWindowWillCloseNotificatio
|
||||
if (!theWindow)
|
||||
return;
|
||||
|
||||
var windowNumber = [theWindow windowNumber];
|
||||
|
||||
windowNumber = [theWindow windowNumber];
|
||||
location = [theWindow convertBridgeToBase:location];
|
||||
|
||||
var event = [CPEvent mouseEventWithType:CPScrollWheel location:location modifierFlags:modifierFlags
|
||||
timestamp:timestamp windowNumber:windowNumber context:nil eventNumber:-1 clickCount:1 pressure:0];
|
||||
event._DOMEvent = aDOMEvent;
|
||||
|
||||
// We lag 1 event behind without this timeout.
|
||||
setTimeout(function()
|
||||
// We lag 1 event behind without this approach
|
||||
window.requestAnimationFrame(function()
|
||||
{
|
||||
if (aDOMEvent.deltaMode !== undefined && aDOMEvent.deltaMode !== 0)
|
||||
{
|
||||
@@ -992,10 +979,10 @@ _CPPlatformWindowWillCloseNotification = @"_CPPlatformWindowWillCloseNotificatio
|
||||
_DOMScrollingElement.scrollLeft = 150;
|
||||
_DOMScrollingElement.scrollTop = 150;
|
||||
|
||||
// Is this needed?
|
||||
//[[CPRunLoop currentRunLoop] limitDateForMode:CPDefaultRunLoopMode];
|
||||
// this is needed to prevent flickering during scrolling
|
||||
[[CPRunLoop currentRunLoop] limitDateForMode:CPDefaultRunLoopMode];
|
||||
|
||||
}, 0);
|
||||
});
|
||||
|
||||
// We hide the dom element after a little bit
|
||||
// so that other DOM elements such as inputs
|
||||
@@ -1152,10 +1139,26 @@ _CPPlatformWindowWillCloseNotification = @"_CPPlatformWindowWillCloseNotificatio
|
||||
|
||||
- (void)touchEvent:(DOMEvent)aDOMEvent
|
||||
{
|
||||
var newEvent = {},
|
||||
touch = aDOMEvent.touches.length ? aDOMEvent.touches[0] : aDOMEvent.changedTouches[0];
|
||||
|
||||
newEvent.timestamp = [CPEvent currentTimestamp];
|
||||
newEvent.target = aDOMEvent.target;
|
||||
newEvent.shiftKey = newEvent.ctrlKey = newEvent.altKey = newEvent.metaKey = false;
|
||||
|
||||
newEvent.clientX = touch.clientX;
|
||||
|
||||
/*
|
||||
Normally the document can't scroll in Cappuccino: our body element has top:0 and bottom:0 with absolute positioning. So it should always be exactly the height of the viewport. The below handles a special case. iOS scrolls the document when the virtual keyboard is present and it needs to move a text input upwards visually to avoid covering the input with the keyboard. For most purposes we can ignore this, except here. In theory I think we could always apply this (scrollTop should always be 0 on every other device and situation) but let's be defensive and only apply it for touch events to minimise the risk of surprises.
|
||||
*/
|
||||
newEvent.clientY = _DOMWindow.document.body.scrollTop + touch.clientY;
|
||||
|
||||
newEvent.preventDefault = function() { if (aDOMEvent.preventDefault) aDOMEvent.preventDefault() };
|
||||
newEvent.stopPropagation = function() { if (aDOMEvent.stopPropagation) aDOMEvent.stopPropagation() };
|
||||
|
||||
// single finger event-> simulate a simple mouse-click
|
||||
if (aDOMEvent.touches && (aDOMEvent.touches.length == 1 || (aDOMEvent.touches.length == 0 && aDOMEvent.changedTouches.length == 1)))
|
||||
{
|
||||
var newEvent = {};
|
||||
|
||||
switch (aDOMEvent.type)
|
||||
{
|
||||
case CPDOMEventTouchStart: newEvent.type = CPDOMEventMouseDown;
|
||||
@@ -1168,32 +1171,42 @@ _CPPlatformWindowWillCloseNotification = @"_CPPlatformWindowWillCloseNotificatio
|
||||
break;
|
||||
}
|
||||
|
||||
var touch = aDOMEvent.touches.length ? aDOMEvent.touches[0] : aDOMEvent.changedTouches[0];
|
||||
|
||||
newEvent.clientX = touch.clientX;
|
||||
newEvent.clientY = touch.clientY;
|
||||
|
||||
newEvent.timestamp = [CPEvent currentTimestamp];
|
||||
newEvent.target = aDOMEvent.target;
|
||||
|
||||
newEvent.shiftKey = newEvent.ctrlKey = newEvent.altKey = newEvent.metaKey = false;
|
||||
|
||||
newEvent.preventDefault = function() { if (aDOMEvent.preventDefault) aDOMEvent.preventDefault() };
|
||||
newEvent.stopPropagation = function() { if (aDOMEvent.stopPropagation) aDOMEvent.stopPropagation() };
|
||||
|
||||
[self mouseEvent:newEvent];
|
||||
|
||||
return;
|
||||
}
|
||||
else
|
||||
{
|
||||
// two fingers->simulate scrolling events
|
||||
if (aDOMEvent.touches && aDOMEvent.touches.length == 2)
|
||||
{
|
||||
switch (aDOMEvent.type)
|
||||
{
|
||||
case CPDOMEventTouchStart:
|
||||
touchStartingPointX = touch.pageX;
|
||||
touchStartingPointY = touch.pageY;
|
||||
break;
|
||||
case CPDOMEventTouchMove:
|
||||
newEvent._hasPreciseScrollingDeltas = YES;
|
||||
newEvent.deltaX = touchStartingPointX - touch.pageX;
|
||||
newEvent.deltaY = touchStartingPointY - touch.pageY;
|
||||
newEvent.type = CPDOMEventScrollWheel;
|
||||
|
||||
[self scrollEvent:newEvent];
|
||||
|
||||
touchStartingPointX = touch.pageX;
|
||||
touchStartingPointY = touch.pageY;
|
||||
return;
|
||||
}
|
||||
}
|
||||
// handle other touch cases specifically
|
||||
|
||||
if (aDOMEvent.preventDefault)
|
||||
aDOMEvent.preventDefault();
|
||||
|
||||
if (aDOMEvent.stopPropagation)
|
||||
aDOMEvent.stopPropagation();
|
||||
}
|
||||
// handle touch cases specifically
|
||||
}
|
||||
|
||||
- (void)mouseEvent:(DOMEvent)aDOMEvent
|
||||
@@ -1245,12 +1258,16 @@ _CPPlatformWindowWillCloseNotification = @"_CPPlatformWindowWillCloseNotificatio
|
||||
{
|
||||
if (_mouseIsDown)
|
||||
{
|
||||
if (aDOMEvent.button !== _firstMouseDownButton)
|
||||
return;
|
||||
|
||||
event = _CPEventFromNativeMouseEvent(aDOMEvent, _mouseDownIsRightClick ? CPRightMouseUp : CPLeftMouseUp, location, modifierFlags, timestamp, windowNumber, nil, -1, CPDOMEventGetClickCount(_lastMouseUp, timestamp, location), 0, nil);
|
||||
|
||||
_mouseIsDown = NO;
|
||||
_lastMouseUp = event;
|
||||
_mouseDownWindow = nil;
|
||||
_mouseDownIsRightClick = NO;
|
||||
_firstMouseDownButton = -1;
|
||||
}
|
||||
|
||||
if (_DOMEventMode)
|
||||
@@ -1270,6 +1287,16 @@ _CPPlatformWindowWillCloseNotification = @"_CPPlatformWindowWillCloseNotificatio
|
||||
|
||||
_mouseDownIsRightClick = button == 2 || (CPBrowserIsOperatingSystem(CPMacOperatingSystem) && button == 0 && modifierFlags & CPControlKeyMask);
|
||||
|
||||
// If mouse is already down, that means that a second mouse button is pushed. This could interfere in mouse events treatment. Just ignore it.
|
||||
// BUT we have to track which button will be first released.
|
||||
if (_mouseIsDown)
|
||||
{
|
||||
_mouseDownIsRightClick = !_mouseDownIsRightClick;
|
||||
return;
|
||||
}
|
||||
|
||||
_firstMouseDownButton = button;
|
||||
|
||||
if ((sourceElement.tagName === "INPUT" || sourceElement.tagName === "TEXTAREA") && sourceElement != _DOMFocusElement)
|
||||
{
|
||||
if ([CPPlatform supportsDragAndDrop])
|
||||
@@ -1404,7 +1431,7 @@ _CPPlatformWindowWillCloseNotification = @"_CPPlatformWindowWillCloseNotificatio
|
||||
var insertionIndex = 0;
|
||||
|
||||
if (middle !== undefined)
|
||||
insertionIndex = _windowLevels[middle] > aLevel ? middle : middle + 1
|
||||
insertionIndex = _windowLevels[middle] > aLevel ? middle : middle + 1;
|
||||
|
||||
[_windowLevels insertObject:aLevel atIndex:insertionIndex];
|
||||
layer._DOMElement.style.zIndex = aLevel + 1; // adding one avoids negative zIndices. These have been causing issues in Chrome
|
||||
@@ -1803,7 +1830,7 @@ var _CPEventFromNativeMouseEvent = function(aNativeEvent, anEventType, aPoint, m
|
||||
var CLICK_SPACE_DELTA = 5.0,
|
||||
CLICK_TIME_DELTA = (typeof document != "undefined" && document.addEventListener) ? 0.55 : 1.0;
|
||||
|
||||
var CPDOMEventGetClickCount = function(aComparisonEvent, aTimestamp, aLocation)
|
||||
CPDOMEventGetClickCount = function(aComparisonEvent, aTimestamp, aLocation)
|
||||
{
|
||||
if (!aComparisonEvent)
|
||||
return 1;
|
||||
@@ -1857,11 +1884,10 @@ function CPWindowObjectList()
|
||||
|
||||
function CPWindowList()
|
||||
{
|
||||
var windowObjectList = CPWindowObjectList(),
|
||||
windowList = [];
|
||||
var windowObjectList = CPWindowObjectList();
|
||||
|
||||
for (var i = 0, count = [windowObjectList count]; i < count; i++)
|
||||
windowList.push([windowObjectList[i] windowNumber]);
|
||||
|
||||
return windowList;
|
||||
return [windowObjectList arrayByApplyingBlock:function(windowObject)
|
||||
{
|
||||
return [windowObject windowNumber];
|
||||
}];
|
||||
}
|
||||
|
||||
@@ -389,8 +389,11 @@ var themedButtonValues = nil,
|
||||
var color = [CPColor blackColor],
|
||||
themedColorValues =
|
||||
[
|
||||
[@"alternate-selected-control-color", [[CPColor alloc] _initWithRGBA:[0.22, 0.46, 0.84, 1.0]]],
|
||||
[@"secondary-selected-control-color", [[CPColor alloc] _initWithRGBA:[0.83, 0.83, 0.83, 1.0]]]
|
||||
[@"alternate-selected-control-color", [[CPColor alloc] _initWithRGBA:[0.22, 0.46, 0.84, 1.0]]],
|
||||
[@"secondary-selected-control-color", [[CPColor alloc] _initWithRGBA:[0.83, 0.83, 0.83, 1.0]]],
|
||||
[@"selected-text-background-color", [CPColor colorWithHexString:"99CCFF"]],
|
||||
[@"selected-text-inactive-background-color", [CPColor colorWithHexString:"CCCCCC"]]
|
||||
|
||||
];
|
||||
|
||||
[self registerThemeValues:themedColorValues forObject:color];
|
||||
|
||||
Binary file not shown.
|
Before Width: | Height: | Size: 317 B |
Binary file not shown.
|
Before Width: | Height: | Size: 422 B |
Binary file not shown.
|
Before Width: | Height: | Size: 409 B |
Binary file not shown.
|
Before Width: | Height: | Size: 333 B |
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user