├── .gitignore
├── LICENSE
├── README.md
├── inc
├── adlist.h
├── page_base.h
├── page_config.h
├── page_manager.h
└── page_manager_private.h
└── src
├── adlist.c
├── page_anim.c
├── page_base.c
├── page_drag.c
├── page_manager.c
├── page_router.c
└── page_state.c
/.gitignore:
--------------------------------------------------------------------------------
1 | # Prerequisites
2 | *.d
3 |
4 | # Object files
5 | *.o
6 | *.ko
7 | *.obj
8 | *.elf
9 |
10 | # Linker output
11 | *.ilk
12 | *.map
13 | *.exp
14 |
15 | # Precompiled Headers
16 | *.gch
17 | *.pch
18 |
19 | # Libraries
20 | *.lib
21 | *.a
22 | *.la
23 | *.lo
24 |
25 | # Shared objects (inc. Windows DLLs)
26 | *.dll
27 | *.so
28 | *.so.*
29 | *.dylib
30 |
31 | # Executables
32 | *.exe
33 | *.out
34 | *.app
35 | *.i*86
36 | *.x86_64
37 | *.hex
38 |
39 | # Debug files
40 | *.dSYM/
41 | *.su
42 | *.idb
43 | *.pdb
44 |
45 | # Kernel Module Compile Results
46 | *.mod*
47 | *.cmd
48 | .tmp_versions/
49 | modules.order
50 | Module.symvers
51 | Mkfile.old
52 | dkms.conf
53 |
--------------------------------------------------------------------------------
/LICENSE:
--------------------------------------------------------------------------------
1 | GNU GENERAL PUBLIC LICENSE
2 | Version 3, 29 June 2007
3 |
4 | Copyright (C) 2007 Free Software Foundation, Inc.
5 | Everyone is permitted to copy and distribute verbatim copies
6 | of this license document, but changing it is not allowed.
7 |
8 | Preamble
9 |
10 | The GNU General Public License is a free, copyleft license for
11 | software and other kinds of works.
12 |
13 | The licenses for most software and other practical works are designed
14 | to take away your freedom to share and change the works. By contrast,
15 | the GNU General Public License is intended to guarantee your freedom to
16 | share and change all versions of a program--to make sure it remains free
17 | software for all its users. We, the Free Software Foundation, use the
18 | GNU General Public License for most of our software; it applies also to
19 | any other work released this way by its authors. You can apply it to
20 | your programs, too.
21 |
22 | When we speak of free software, we are referring to freedom, not
23 | price. Our General Public Licenses are designed to make sure that you
24 | have the freedom to distribute copies of free software (and charge for
25 | them if you wish), that you receive source code or can get it if you
26 | want it, that you can change the software or use pieces of it in new
27 | free programs, and that you know you can do these things.
28 |
29 | To protect your rights, we need to prevent others from denying you
30 | these rights or asking you to surrender the rights. Therefore, you have
31 | certain responsibilities if you distribute copies of the software, or if
32 | you modify it: responsibilities to respect the freedom of others.
33 |
34 | For example, if you distribute copies of such a program, whether
35 | gratis or for a fee, you must pass on to the recipients the same
36 | freedoms that you received. You must make sure that they, too, receive
37 | or can get the source code. And you must show them these terms so they
38 | know their rights.
39 |
40 | Developers that use the GNU GPL protect your rights with two steps:
41 | (1) assert copyright on the software, and (2) offer you this License
42 | giving you legal permission to copy, distribute and/or modify it.
43 |
44 | For the developers' and authors' protection, the GPL clearly explains
45 | that there is no warranty for this free software. For both users' and
46 | authors' sake, the GPL requires that modified versions be marked as
47 | changed, so that their problems will not be attributed erroneously to
48 | authors of previous versions.
49 |
50 | Some devices are designed to deny users access to install or run
51 | modified versions of the software inside them, although the manufacturer
52 | can do so. This is fundamentally incompatible with the aim of
53 | protecting users' freedom to change the software. The systematic
54 | pattern of such abuse occurs in the area of products for individuals to
55 | use, which is precisely where it is most unacceptable. Therefore, we
56 | have designed this version of the GPL to prohibit the practice for those
57 | products. If such problems arise substantially in other domains, we
58 | stand ready to extend this provision to those domains in future versions
59 | of the GPL, as needed to protect the freedom of users.
60 |
61 | Finally, every program is threatened constantly by software patents.
62 | States should not allow patents to restrict development and use of
63 | software on general-purpose computers, but in those that do, we wish to
64 | avoid the special danger that patents applied to a free program could
65 | make it effectively proprietary. To prevent this, the GPL assures that
66 | patents cannot be used to render the program non-free.
67 |
68 | The precise terms and conditions for copying, distribution and
69 | modification follow.
70 |
71 | TERMS AND CONDITIONS
72 |
73 | 0. Definitions.
74 |
75 | "This License" refers to version 3 of the GNU General Public License.
76 |
77 | "Copyright" also means copyright-like laws that apply to other kinds of
78 | works, such as semiconductor masks.
79 |
80 | "The Program" refers to any copyrightable work licensed under this
81 | License. Each licensee is addressed as "you". "Licensees" and
82 | "recipients" may be individuals or organizations.
83 |
84 | To "modify" a work means to copy from or adapt all or part of the work
85 | in a fashion requiring copyright permission, other than the making of an
86 | exact copy. The resulting work is called a "modified version" of the
87 | earlier work or a work "based on" the earlier work.
88 |
89 | A "covered work" means either the unmodified Program or a work based
90 | on the Program.
91 |
92 | To "propagate" a work means to do anything with it that, without
93 | permission, would make you directly or secondarily liable for
94 | infringement under applicable copyright law, except executing it on a
95 | computer or modifying a private copy. Propagation includes copying,
96 | distribution (with or without modification), making available to the
97 | public, and in some countries other activities as well.
98 |
99 | To "convey" a work means any kind of propagation that enables other
100 | parties to make or receive copies. Mere interaction with a user through
101 | a computer network, with no transfer of a copy, is not conveying.
102 |
103 | An interactive user interface displays "Appropriate Legal Notices"
104 | to the extent that it includes a convenient and prominently visible
105 | feature that (1) displays an appropriate copyright notice, and (2)
106 | tells the user that there is no warranty for the work (except to the
107 | extent that warranties are provided), that licensees may convey the
108 | work under this License, and how to view a copy of this License. If
109 | the interface presents a list of user commands or options, such as a
110 | menu, a prominent item in the list meets this criterion.
111 |
112 | 1. Source Code.
113 |
114 | The "source code" for a work means the preferred form of the work
115 | for making modifications to it. "Object code" means any non-source
116 | form of a work.
117 |
118 | A "Standard Interface" means an interface that either is an official
119 | standard defined by a recognized standards body, or, in the case of
120 | interfaces specified for a particular programming language, one that
121 | is widely used among developers working in that language.
122 |
123 | The "System Libraries" of an executable work include anything, other
124 | than the work as a whole, that (a) is included in the normal form of
125 | packaging a Major Component, but which is not part of that Major
126 | Component, and (b) serves only to enable use of the work with that
127 | Major Component, or to implement a Standard Interface for which an
128 | implementation is available to the public in source code form. A
129 | "Major Component", in this context, means a major essential component
130 | (kernel, window system, and so on) of the specific operating system
131 | (if any) on which the executable work runs, or a compiler used to
132 | produce the work, or an object code interpreter used to run it.
133 |
134 | The "Corresponding Source" for a work in object code form means all
135 | the source code needed to generate, install, and (for an executable
136 | work) run the object code and to modify the work, including scripts to
137 | control those activities. However, it does not include the work's
138 | System Libraries, or general-purpose tools or generally available free
139 | programs which are used unmodified in performing those activities but
140 | which are not part of the work. For example, Corresponding Source
141 | includes interface definition files associated with source files for
142 | the work, and the source code for shared libraries and dynamically
143 | linked subprograms that the work is specifically designed to require,
144 | such as by intimate data communication or control flow between those
145 | subprograms and other parts of the work.
146 |
147 | The Corresponding Source need not include anything that users
148 | can regenerate automatically from other parts of the Corresponding
149 | Source.
150 |
151 | The Corresponding Source for a work in source code form is that
152 | same work.
153 |
154 | 2. Basic Permissions.
155 |
156 | All rights granted under this License are granted for the term of
157 | copyright on the Program, and are irrevocable provided the stated
158 | conditions are met. This License explicitly affirms your unlimited
159 | permission to run the unmodified Program. The output from running a
160 | covered work is covered by this License only if the output, given its
161 | content, constitutes a covered work. This License acknowledges your
162 | rights of fair use or other equivalent, as provided by copyright law.
163 |
164 | You may make, run and propagate covered works that you do not
165 | convey, without conditions so long as your license otherwise remains
166 | in force. You may convey covered works to others for the sole purpose
167 | of having them make modifications exclusively for you, or provide you
168 | with facilities for running those works, provided that you comply with
169 | the terms of this License in conveying all material for which you do
170 | not control copyright. Those thus making or running the covered works
171 | for you must do so exclusively on your behalf, under your direction
172 | and control, on terms that prohibit them from making any copies of
173 | your copyrighted material outside their relationship with you.
174 |
175 | Conveying under any other circumstances is permitted solely under
176 | the conditions stated below. Sublicensing is not allowed; section 10
177 | makes it unnecessary.
178 |
179 | 3. Protecting Users' Legal Rights From Anti-Circumvention Law.
180 |
181 | No covered work shall be deemed part of an effective technological
182 | measure under any applicable law fulfilling obligations under article
183 | 11 of the WIPO copyright treaty adopted on 20 December 1996, or
184 | similar laws prohibiting or restricting circumvention of such
185 | measures.
186 |
187 | When you convey a covered work, you waive any legal power to forbid
188 | circumvention of technological measures to the extent such circumvention
189 | is effected by exercising rights under this License with respect to
190 | the covered work, and you disclaim any intention to limit operation or
191 | modification of the work as a means of enforcing, against the work's
192 | users, your or third parties' legal rights to forbid circumvention of
193 | technological measures.
194 |
195 | 4. Conveying Verbatim Copies.
196 |
197 | You may convey verbatim copies of the Program's source code as you
198 | receive it, in any medium, provided that you conspicuously and
199 | appropriately publish on each copy an appropriate copyright notice;
200 | keep intact all notices stating that this License and any
201 | non-permissive terms added in accord with section 7 apply to the code;
202 | keep intact all notices of the absence of any warranty; and give all
203 | recipients a copy of this License along with the Program.
204 |
205 | You may charge any price or no price for each copy that you convey,
206 | and you may offer support or warranty protection for a fee.
207 |
208 | 5. Conveying Modified Source Versions.
209 |
210 | You may convey a work based on the Program, or the modifications to
211 | produce it from the Program, in the form of source code under the
212 | terms of section 4, provided that you also meet all of these conditions:
213 |
214 | a) The work must carry prominent notices stating that you modified
215 | it, and giving a relevant date.
216 |
217 | b) The work must carry prominent notices stating that it is
218 | released under this License and any conditions added under section
219 | 7. This requirement modifies the requirement in section 4 to
220 | "keep intact all notices".
221 |
222 | c) You must license the entire work, as a whole, under this
223 | License to anyone who comes into possession of a copy. This
224 | License will therefore apply, along with any applicable section 7
225 | additional terms, to the whole of the work, and all its parts,
226 | regardless of how they are packaged. This License gives no
227 | permission to license the work in any other way, but it does not
228 | invalidate such permission if you have separately received it.
229 |
230 | d) If the work has interactive user interfaces, each must display
231 | Appropriate Legal Notices; however, if the Program has interactive
232 | interfaces that do not display Appropriate Legal Notices, your
233 | work need not make them do so.
234 |
235 | A compilation of a covered work with other separate and independent
236 | works, which are not by their nature extensions of the covered work,
237 | and which are not combined with it such as to form a larger program,
238 | in or on a volume of a storage or distribution medium, is called an
239 | "aggregate" if the compilation and its resulting copyright are not
240 | used to limit the access or legal rights of the compilation's users
241 | beyond what the individual works permit. Inclusion of a covered work
242 | in an aggregate does not cause this License to apply to the other
243 | parts of the aggregate.
244 |
245 | 6. Conveying Non-Source Forms.
246 |
247 | You may convey a covered work in object code form under the terms
248 | of sections 4 and 5, provided that you also convey the
249 | machine-readable Corresponding Source under the terms of this License,
250 | in one of these ways:
251 |
252 | a) Convey the object code in, or embodied in, a physical product
253 | (including a physical distribution medium), accompanied by the
254 | Corresponding Source fixed on a durable physical medium
255 | customarily used for software interchange.
256 |
257 | b) Convey the object code in, or embodied in, a physical product
258 | (including a physical distribution medium), accompanied by a
259 | written offer, valid for at least three years and valid for as
260 | long as you offer spare parts or customer support for that product
261 | model, to give anyone who possesses the object code either (1) a
262 | copy of the Corresponding Source for all the software in the
263 | product that is covered by this License, on a durable physical
264 | medium customarily used for software interchange, for a price no
265 | more than your reasonable cost of physically performing this
266 | conveying of source, or (2) access to copy the
267 | Corresponding Source from a network server at no charge.
268 |
269 | c) Convey individual copies of the object code with a copy of the
270 | written offer to provide the Corresponding Source. This
271 | alternative is allowed only occasionally and noncommercially, and
272 | only if you received the object code with such an offer, in accord
273 | with subsection 6b.
274 |
275 | d) Convey the object code by offering access from a designated
276 | place (gratis or for a charge), and offer equivalent access to the
277 | Corresponding Source in the same way through the same place at no
278 | further charge. You need not require recipients to copy the
279 | Corresponding Source along with the object code. If the place to
280 | copy the object code is a network server, the Corresponding Source
281 | may be on a different server (operated by you or a third party)
282 | that supports equivalent copying facilities, provided you maintain
283 | clear directions next to the object code saying where to find the
284 | Corresponding Source. Regardless of what server hosts the
285 | Corresponding Source, you remain obligated to ensure that it is
286 | available for as long as needed to satisfy these requirements.
287 |
288 | e) Convey the object code using peer-to-peer transmission, provided
289 | you inform other peers where the object code and Corresponding
290 | Source of the work are being offered to the general public at no
291 | charge under subsection 6d.
292 |
293 | A separable portion of the object code, whose source code is excluded
294 | from the Corresponding Source as a System Library, need not be
295 | included in conveying the object code work.
296 |
297 | A "User Product" is either (1) a "consumer product", which means any
298 | tangible personal property which is normally used for personal, family,
299 | or household purposes, or (2) anything designed or sold for incorporation
300 | into a dwelling. In determining whether a product is a consumer product,
301 | doubtful cases shall be resolved in favor of coverage. For a particular
302 | product received by a particular user, "normally used" refers to a
303 | typical or common use of that class of product, regardless of the status
304 | of the particular user or of the way in which the particular user
305 | actually uses, or expects or is expected to use, the product. A product
306 | is a consumer product regardless of whether the product has substantial
307 | commercial, industrial or non-consumer uses, unless such uses represent
308 | the only significant mode of use of the product.
309 |
310 | "Installation Information" for a User Product means any methods,
311 | procedures, authorization keys, or other information required to install
312 | and execute modified versions of a covered work in that User Product from
313 | a modified version of its Corresponding Source. The information must
314 | suffice to ensure that the continued functioning of the modified object
315 | code is in no case prevented or interfered with solely because
316 | modification has been made.
317 |
318 | If you convey an object code work under this section in, or with, or
319 | specifically for use in, a User Product, and the conveying occurs as
320 | part of a transaction in which the right of possession and use of the
321 | User Product is transferred to the recipient in perpetuity or for a
322 | fixed term (regardless of how the transaction is characterized), the
323 | Corresponding Source conveyed under this section must be accompanied
324 | by the Installation Information. But this requirement does not apply
325 | if neither you nor any third party retains the ability to install
326 | modified object code on the User Product (for example, the work has
327 | been installed in ROM).
328 |
329 | The requirement to provide Installation Information does not include a
330 | requirement to continue to provide support service, warranty, or updates
331 | for a work that has been modified or installed by the recipient, or for
332 | the User Product in which it has been modified or installed. Access to a
333 | network may be denied when the modification itself materially and
334 | adversely affects the operation of the network or violates the rules and
335 | protocols for communication across the network.
336 |
337 | Corresponding Source conveyed, and Installation Information provided,
338 | in accord with this section must be in a format that is publicly
339 | documented (and with an implementation available to the public in
340 | source code form), and must require no special password or key for
341 | unpacking, reading or copying.
342 |
343 | 7. Additional Terms.
344 |
345 | "Additional permissions" are terms that supplement the terms of this
346 | License by making exceptions from one or more of its conditions.
347 | Additional permissions that are applicable to the entire Program shall
348 | be treated as though they were included in this License, to the extent
349 | that they are valid under applicable law. If additional permissions
350 | apply only to part of the Program, that part may be used separately
351 | under those permissions, but the entire Program remains governed by
352 | this License without regard to the additional permissions.
353 |
354 | When you convey a copy of a covered work, you may at your option
355 | remove any additional permissions from that copy, or from any part of
356 | it. (Additional permissions may be written to require their own
357 | removal in certain cases when you modify the work.) You may place
358 | additional permissions on material, added by you to a covered work,
359 | for which you have or can give appropriate copyright permission.
360 |
361 | Notwithstanding any other provision of this License, for material you
362 | add to a covered work, you may (if authorized by the copyright holders of
363 | that material) supplement the terms of this License with terms:
364 |
365 | a) Disclaiming warranty or limiting liability differently from the
366 | terms of sections 15 and 16 of this License; or
367 |
368 | b) Requiring preservation of specified reasonable legal notices or
369 | author attributions in that material or in the Appropriate Legal
370 | Notices displayed by works containing it; or
371 |
372 | c) Prohibiting misrepresentation of the origin of that material, or
373 | requiring that modified versions of such material be marked in
374 | reasonable ways as different from the original version; or
375 |
376 | d) Limiting the use for publicity purposes of names of licensors or
377 | authors of the material; or
378 |
379 | e) Declining to grant rights under trademark law for use of some
380 | trade names, trademarks, or service marks; or
381 |
382 | f) Requiring indemnification of licensors and authors of that
383 | material by anyone who conveys the material (or modified versions of
384 | it) with contractual assumptions of liability to the recipient, for
385 | any liability that these contractual assumptions directly impose on
386 | those licensors and authors.
387 |
388 | All other non-permissive additional terms are considered "further
389 | restrictions" within the meaning of section 10. If the Program as you
390 | received it, or any part of it, contains a notice stating that it is
391 | governed by this License along with a term that is a further
392 | restriction, you may remove that term. If a license document contains
393 | a further restriction but permits relicensing or conveying under this
394 | License, you may add to a covered work material governed by the terms
395 | of that license document, provided that the further restriction does
396 | not survive such relicensing or conveying.
397 |
398 | If you add terms to a covered work in accord with this section, you
399 | must place, in the relevant source files, a statement of the
400 | additional terms that apply to those files, or a notice indicating
401 | where to find the applicable terms.
402 |
403 | Additional terms, permissive or non-permissive, may be stated in the
404 | form of a separately written license, or stated as exceptions;
405 | the above requirements apply either way.
406 |
407 | 8. Termination.
408 |
409 | You may not propagate or modify a covered work except as expressly
410 | provided under this License. Any attempt otherwise to propagate or
411 | modify it is void, and will automatically terminate your rights under
412 | this License (including any patent licenses granted under the third
413 | paragraph of section 11).
414 |
415 | However, if you cease all violation of this License, then your
416 | license from a particular copyright holder is reinstated (a)
417 | provisionally, unless and until the copyright holder explicitly and
418 | finally terminates your license, and (b) permanently, if the copyright
419 | holder fails to notify you of the violation by some reasonable means
420 | prior to 60 days after the cessation.
421 |
422 | Moreover, your license from a particular copyright holder is
423 | reinstated permanently if the copyright holder notifies you of the
424 | violation by some reasonable means, this is the first time you have
425 | received notice of violation of this License (for any work) from that
426 | copyright holder, and you cure the violation prior to 30 days after
427 | your receipt of the notice.
428 |
429 | Termination of your rights under this section does not terminate the
430 | licenses of parties who have received copies or rights from you under
431 | this License. If your rights have been terminated and not permanently
432 | reinstated, you do not qualify to receive new licenses for the same
433 | material under section 10.
434 |
435 | 9. Acceptance Not Required for Having Copies.
436 |
437 | You are not required to accept this License in order to receive or
438 | run a copy of the Program. Ancillary propagation of a covered work
439 | occurring solely as a consequence of using peer-to-peer transmission
440 | to receive a copy likewise does not require acceptance. However,
441 | nothing other than this License grants you permission to propagate or
442 | modify any covered work. These actions infringe copyright if you do
443 | not accept this License. Therefore, by modifying or propagating a
444 | covered work, you indicate your acceptance of this License to do so.
445 |
446 | 10. Automatic Licensing of Downstream Recipients.
447 |
448 | Each time you convey a covered work, the recipient automatically
449 | receives a license from the original licensors, to run, modify and
450 | propagate that work, subject to this License. You are not responsible
451 | for enforcing compliance by third parties with this License.
452 |
453 | An "entity transaction" is a transaction transferring control of an
454 | organization, or substantially all assets of one, or subdividing an
455 | organization, or merging organizations. If propagation of a covered
456 | work results from an entity transaction, each party to that
457 | transaction who receives a copy of the work also receives whatever
458 | licenses to the work the party's predecessor in interest had or could
459 | give under the previous paragraph, plus a right to possession of the
460 | Corresponding Source of the work from the predecessor in interest, if
461 | the predecessor has it or can get it with reasonable efforts.
462 |
463 | You may not impose any further restrictions on the exercise of the
464 | rights granted or affirmed under this License. For example, you may
465 | not impose a license fee, royalty, or other charge for exercise of
466 | rights granted under this License, and you may not initiate litigation
467 | (including a cross-claim or counterclaim in a lawsuit) alleging that
468 | any patent claim is infringed by making, using, selling, offering for
469 | sale, or importing the Program or any portion of it.
470 |
471 | 11. Patents.
472 |
473 | A "contributor" is a copyright holder who authorizes use under this
474 | License of the Program or a work on which the Program is based. The
475 | work thus licensed is called the contributor's "contributor version".
476 |
477 | A contributor's "essential patent claims" are all patent claims
478 | owned or controlled by the contributor, whether already acquired or
479 | hereafter acquired, that would be infringed by some manner, permitted
480 | by this License, of making, using, or selling its contributor version,
481 | but do not include claims that would be infringed only as a
482 | consequence of further modification of the contributor version. For
483 | purposes of this definition, "control" includes the right to grant
484 | patent sublicenses in a manner consistent with the requirements of
485 | this License.
486 |
487 | Each contributor grants you a non-exclusive, worldwide, royalty-free
488 | patent license under the contributor's essential patent claims, to
489 | make, use, sell, offer for sale, import and otherwise run, modify and
490 | propagate the contents of its contributor version.
491 |
492 | In the following three paragraphs, a "patent license" is any express
493 | agreement or commitment, however denominated, not to enforce a patent
494 | (such as an express permission to practice a patent or covenant not to
495 | sue for patent infringement). To "grant" such a patent license to a
496 | party means to make such an agreement or commitment not to enforce a
497 | patent against the party.
498 |
499 | If you convey a covered work, knowingly relying on a patent license,
500 | and the Corresponding Source of the work is not available for anyone
501 | to copy, free of charge and under the terms of this License, through a
502 | publicly available network server or other readily accessible means,
503 | then you must either (1) cause the Corresponding Source to be so
504 | available, or (2) arrange to deprive yourself of the benefit of the
505 | patent license for this particular work, or (3) arrange, in a manner
506 | consistent with the requirements of this License, to extend the patent
507 | license to downstream recipients. "Knowingly relying" means you have
508 | actual knowledge that, but for the patent license, your conveying the
509 | covered work in a country, or your recipient's use of the covered work
510 | in a country, would infringe one or more identifiable patents in that
511 | country that you have reason to believe are valid.
512 |
513 | If, pursuant to or in connection with a single transaction or
514 | arrangement, you convey, or propagate by procuring conveyance of, a
515 | covered work, and grant a patent license to some of the parties
516 | receiving the covered work authorizing them to use, propagate, modify
517 | or convey a specific copy of the covered work, then the patent license
518 | you grant is automatically extended to all recipients of the covered
519 | work and works based on it.
520 |
521 | A patent license is "discriminatory" if it does not include within
522 | the scope of its coverage, prohibits the exercise of, or is
523 | conditioned on the non-exercise of one or more of the rights that are
524 | specifically granted under this License. You may not convey a covered
525 | work if you are a party to an arrangement with a third party that is
526 | in the business of distributing software, under which you make payment
527 | to the third party based on the extent of your activity of conveying
528 | the work, and under which the third party grants, to any of the
529 | parties who would receive the covered work from you, a discriminatory
530 | patent license (a) in connection with copies of the covered work
531 | conveyed by you (or copies made from those copies), or (b) primarily
532 | for and in connection with specific products or compilations that
533 | contain the covered work, unless you entered into that arrangement,
534 | or that patent license was granted, prior to 28 March 2007.
535 |
536 | Nothing in this License shall be construed as excluding or limiting
537 | any implied license or other defenses to infringement that may
538 | otherwise be available to you under applicable patent law.
539 |
540 | 12. No Surrender of Others' Freedom.
541 |
542 | If conditions are imposed on you (whether by court order, agreement or
543 | otherwise) that contradict the conditions of this License, they do not
544 | excuse you from the conditions of this License. If you cannot convey a
545 | covered work so as to satisfy simultaneously your obligations under this
546 | License and any other pertinent obligations, then as a consequence you may
547 | not convey it at all. For example, if you agree to terms that obligate you
548 | to collect a royalty for further conveying from those to whom you convey
549 | the Program, the only way you could satisfy both those terms and this
550 | License would be to refrain entirely from conveying the Program.
551 |
552 | 13. Use with the GNU Affero General Public License.
553 |
554 | Notwithstanding any other provision of this License, you have
555 | permission to link or combine any covered work with a work licensed
556 | under version 3 of the GNU Affero General Public License into a single
557 | combined work, and to convey the resulting work. The terms of this
558 | License will continue to apply to the part which is the covered work,
559 | but the special requirements of the GNU Affero General Public License,
560 | section 13, concerning interaction through a network will apply to the
561 | combination as such.
562 |
563 | 14. Revised Versions of this License.
564 |
565 | The Free Software Foundation may publish revised and/or new versions of
566 | the GNU General Public License from time to time. Such new versions will
567 | be similar in spirit to the present version, but may differ in detail to
568 | address new problems or concerns.
569 |
570 | Each version is given a distinguishing version number. If the
571 | Program specifies that a certain numbered version of the GNU General
572 | Public License "or any later version" applies to it, you have the
573 | option of following the terms and conditions either of that numbered
574 | version or of any later version published by the Free Software
575 | Foundation. If the Program does not specify a version number of the
576 | GNU General Public License, you may choose any version ever published
577 | by the Free Software Foundation.
578 |
579 | If the Program specifies that a proxy can decide which future
580 | versions of the GNU General Public License can be used, that proxy's
581 | public statement of acceptance of a version permanently authorizes you
582 | to choose that version for the Program.
583 |
584 | Later license versions may give you additional or different
585 | permissions. However, no additional obligations are imposed on any
586 | author or copyright holder as a result of your choosing to follow a
587 | later version.
588 |
589 | 15. Disclaimer of Warranty.
590 |
591 | THERE IS NO WARRANTY FOR THE PROGRAM, TO THE EXTENT PERMITTED BY
592 | APPLICABLE LAW. EXCEPT WHEN OTHERWISE STATED IN WRITING THE COPYRIGHT
593 | HOLDERS AND/OR OTHER PARTIES PROVIDE THE PROGRAM "AS IS" WITHOUT WARRANTY
594 | OF ANY KIND, EITHER EXPRESSED OR IMPLIED, INCLUDING, BUT NOT LIMITED TO,
595 | THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
596 | PURPOSE. THE ENTIRE RISK AS TO THE QUALITY AND PERFORMANCE OF THE PROGRAM
597 | IS WITH YOU. SHOULD THE PROGRAM PROVE DEFECTIVE, YOU ASSUME THE COST OF
598 | ALL NECESSARY SERVICING, REPAIR OR CORRECTION.
599 |
600 | 16. Limitation of Liability.
601 |
602 | IN NO EVENT UNLESS REQUIRED BY APPLICABLE LAW OR AGREED TO IN WRITING
603 | WILL ANY COPYRIGHT HOLDER, OR ANY OTHER PARTY WHO MODIFIES AND/OR CONVEYS
604 | THE PROGRAM AS PERMITTED ABOVE, BE LIABLE TO YOU FOR DAMAGES, INCLUDING ANY
605 | GENERAL, SPECIAL, INCIDENTAL OR CONSEQUENTIAL DAMAGES ARISING OUT OF THE
606 | USE OR INABILITY TO USE THE PROGRAM (INCLUDING BUT NOT LIMITED TO LOSS OF
607 | DATA OR DATA BEING RENDERED INACCURATE OR LOSSES SUSTAINED BY YOU OR THIRD
608 | PARTIES OR A FAILURE OF THE PROGRAM TO OPERATE WITH ANY OTHER PROGRAMS),
609 | EVEN IF SUCH HOLDER OR OTHER PARTY HAS BEEN ADVISED OF THE POSSIBILITY OF
610 | SUCH DAMAGES.
611 |
612 | 17. Interpretation of Sections 15 and 16.
613 |
614 | If the disclaimer of warranty and limitation of liability provided
615 | above cannot be given local legal effect according to their terms,
616 | reviewing courts shall apply local law that most closely approximates
617 | an absolute waiver of all civil liability in connection with the
618 | Program, unless a warranty or assumption of liability accompanies a
619 | copy of the Program in return for a fee.
620 |
621 | END OF TERMS AND CONDITIONS
622 |
623 | How to Apply These Terms to Your New Programs
624 |
625 | If you develop a new program, and you want it to be of the greatest
626 | possible use to the public, the best way to achieve this is to make it
627 | free software which everyone can redistribute and change under these terms.
628 |
629 | To do so, attach the following notices to the program. It is safest
630 | to attach them to the start of each source file to most effectively
631 | state the exclusion of warranty; and each file should have at least
632 | the "copyright" line and a pointer to where the full notice is found.
633 |
634 |
635 | Copyright (C)
636 |
637 | This program is free software: you can redistribute it and/or modify
638 | it under the terms of the GNU General Public License as published by
639 | the Free Software Foundation, either version 3 of the License, or
640 | (at your option) any later version.
641 |
642 | This program is distributed in the hope that it will be useful,
643 | but WITHOUT ANY WARRANTY; without even the implied warranty of
644 | MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
645 | GNU General Public License for more details.
646 |
647 | You should have received a copy of the GNU General Public License
648 | along with this program. If not, see .
649 |
650 | Also add information on how to contact you by electronic and paper mail.
651 |
652 | If the program does terminal interaction, make it output a short
653 | notice like this when it starts in an interactive mode:
654 |
655 | Copyright (C)
656 | This program comes with ABSOLUTELY NO WARRANTY; for details type `show w'.
657 | This is free software, and you are welcome to redistribute it
658 | under certain conditions; type `show c' for details.
659 |
660 | The hypothetical commands `show w' and `show c' should show the appropriate
661 | parts of the General Public License. Of course, your program's commands
662 | might be different; for a GUI interface, you would use an "about box".
663 |
664 | You should also get your employer (if you work as a programmer) or school,
665 | if any, to sign a "copyright disclaimer" for the program, if necessary.
666 | For more information on this, and how to apply and follow the GNU GPL, see
667 | .
668 |
669 | The GNU General Public License does not permit incorporating your program
670 | into proprietary programs. If your program is a subroutine library, you
671 | may consider it more useful to permit linking proprietary applications with
672 | the library. If this is what you want to do, use the GNU Lesser General
673 | Public License instead of this License. But first, please read
674 | .
675 |
--------------------------------------------------------------------------------
/README.md:
--------------------------------------------------------------------------------
1 | # page_manager
2 |
3 | base on X-TRACK [PageManager](https://github.com/FASTSHIFT/X-TRACK/tree/main/Software/X-Track/USER/App/Utils/PageManager)
4 | Thanks to [FASTSHIFT](https://github.com/FASTSHIFT) for authorization
5 |
6 | ## introduce
7 | page management framework base on lvgl
8 |
9 | ## how to use
10 | 1. First you have to have a compileable lvgl project
11 | 2. Put the framework as a component in the project
12 | 3. Create and use page managers
13 | ```C
14 | page_manager_t *manager = page_manager_create(); // create page manager
15 | pm_install(manager, "demo", demo_create("demo")); // install your page
16 | pm_set_global_load_anim_type(manager, LOAD_ANIM_OVER_TOP, 500, lv_anim_path_overshoot); // setting global anim type
17 | pm_push(manager, "demo", NULL); // push to show installed pages
18 | pm_pop(manager); // pop up the page that has already been shown
19 | ```
20 |
21 |
--------------------------------------------------------------------------------
/inc/adlist.h:
--------------------------------------------------------------------------------
1 | #ifndef __ADLIST_H__
2 | #define __ADLIST_H__
3 |
4 | /* Node, List, and Iterator are the only data structures used currently. */
5 |
6 | typedef struct listNode
7 | {
8 | struct listNode *prev;
9 | struct listNode *next;
10 | void *value;
11 | } listNode;
12 |
13 | typedef struct listIter
14 | {
15 | listNode *next;
16 | int direction;
17 | } listIter;
18 |
19 | typedef struct list
20 | {
21 | listNode *head;
22 | listNode *tail;
23 | void *(*dup)(void *ptr);
24 | void (*free)(void *ptr);
25 | int (*match)(void *ptr, void *key);
26 | unsigned long len;
27 | } list;
28 |
29 | /* Functions implemented as macros */
30 | #define listLength(l) ((l)->len)
31 | #define listFirst(l) ((l)->head)
32 | #define listLast(l) ((l)->tail)
33 | #define listPrevNode(n) ((n)->prev)
34 | #define listNextNode(n) ((n)->next)
35 | #define listNodeValue(n) ((n)->value)
36 |
37 | #define listSetDupMethod(l, m) ((l)->dup = (m))
38 | #define listSetFreeMethod(l, m) ((l)->free = (m))
39 | #define listSetMatchMethod(l, m) ((l)->match = (m))
40 |
41 | #define listGetDupMethod(l) ((l)->dup)
42 | #define listGetFreeMethod(l) ((l)->free)
43 | #define listGetMatchMethod(l) ((l)->match)
44 |
45 | /* Prototypes */
46 | list *listCreate(void);
47 | void listRelease(list *list);
48 | void listEmpty(list *list);
49 | list *listAddNodeHead(list *list, void *value);
50 | list *listAddNodeTail(list *list, void *value);
51 | list *listInsertNode(list *list, listNode *old_node, void *value, int after);
52 | void listDelNode(list *list, listNode *node);
53 | listIter *listGetIterator(list *list, int direction);
54 | listNode *listNext(listIter *iter);
55 | void listReleaseIterator(listIter *iter);
56 | list *listDup(list *orig);
57 | listNode *listSearchKey(list *list, void *key);
58 | listNode *listIndex(list *list, long index);
59 | void listRewind(list *list, listIter *li);
60 | void listRewindTail(list *list, listIter *li);
61 | void listRotateTailToHead(list *list);
62 | void listRotateHeadToTail(list *list);
63 | void listJoin(list *l, list *o);
64 |
65 | /* Directions for iterators */
66 | #define AL_START_HEAD 0
67 | #define AL_START_TAIL 1
68 |
69 | #endif /* __ADLIST_H__ */
--------------------------------------------------------------------------------
/inc/page_base.h:
--------------------------------------------------------------------------------
1 | #pragma once
2 |
3 | #include "page_config.h"
4 |
5 | #ifdef __cplusplus
6 | extern "C"
7 | {
8 | #endif
9 |
10 | typedef struct page_base_t page_base_t;
11 | typedef struct page_manager_t page_manager_t;
12 |
13 | typedef enum
14 | {
15 | PAGE_STATE_IDLE,
16 | PAGE_STATE_LOAD,
17 | PAGE_STATE_WILL_APPEAR,
18 | PAGE_STATE_DID_APPEAR,
19 | PAGE_STATE_ACTIVITY,
20 | PAGE_STATE_WILL_DISAPPEAR,
21 | PAGE_STATE_DID_DISAPPEAR,
22 | PAGE_STATE_UNLOAD,
23 | _PAGE_STATE_LAST
24 | } page_state_t;
25 |
26 | // 数据块
27 | typedef struct
28 | {
29 | void *ptr;
30 | uint32_t size;
31 | } page_stash_t;
32 |
33 | // 页面切换动画属性
34 | typedef struct
35 | {
36 | uint8_t type;
37 | uint16_t time;
38 | lv_anim_path_cb_t path;
39 | } page_anim_attr_t;
40 |
41 | typedef struct
42 | {
43 | /**
44 | * @brief 同步用户自定义属性配置
45 | * @note 在install阶段被调用,用于打开一些页面设置,此时root页面未创建
46 | */
47 | void (*on_custom_attr_config)(page_base_t *self);
48 |
49 | /**
50 | * @brief 页面加载
51 | * @note 在push/pop后创建完root页面后调用,is_cache是true时跳过
52 | */
53 | void (*on_view_load)(page_base_t *self);
54 |
55 | /**
56 | * @brief 页面加载完成
57 | * @note 比on_view_load多初始化了拖拽功能(根据动画参数自动开启)后调用,is_cache是true时跳过
58 | */
59 | void (*on_view_did_load)(page_base_t *self);
60 |
61 | /**
62 | * @brief 页面即将显示
63 | * @note 比on_view_load多了更新is_cache相关的内容
64 | */
65 | void (*on_view_will_appear)(page_base_t *self);
66 |
67 | /**
68 | * @brief 页面显示
69 | * @note 动画结束后调用
70 | */
71 | void (*on_view_did_appear)(page_base_t *self);
72 |
73 | /**
74 | * @brief 页面即将消失
75 | * @note 页面被切换时第一时间调用
76 | */
77 | void (*on_view_will_disappear)(page_base_t *self);
78 |
79 | /**
80 | * @brief 页面消失完成
81 | * @note 页面消失时调用
82 | */
83 | void (*on_view_did_disappear)(page_base_t *self);
84 |
85 | /**
86 | * @brief 页面卸载完成
87 | * @note 页面被卸载的时候会被调用,is_cache是true时跳过
88 | */
89 | void (*on_view_did_unload)(page_base_t *self);
90 | } page_vtable_t;
91 |
92 | typedef struct page_base_t
93 | {
94 | page_vtable_t* base;
95 | lv_obj_t *root;
96 | lv_event_cb_t root_event_cb; // 根对象回调
97 | page_manager_t *manager;
98 | const char *name;
99 | void *user_data;
100 | struct
101 | {
102 | bool req_enable_cache; // 页面缓存启用标志位
103 | bool req_disable_auto_cache; // 页面自动缓存管理启用标志位
104 | bool is_disable_auto_cache; // 页面自动缓存标志位
105 | bool is_cached; // 页面缓存标志位
106 | page_stash_t stash; // push时传入参数
107 | page_state_t state; // 页面状态
108 | /* 动画状态 */
109 | struct
110 | {
111 | bool is_enter; // 进入还是退出动画
112 | bool is_busy; // 动画是否正在播放
113 | page_anim_attr_t attr; // lvgl动画属性
114 | } anim;
115 | } priv;
116 | } page_base_t;
117 |
118 | /**
119 | * @brief 设置自动缓存
120 | *
121 | * @param self 页面对象
122 | * @param en 是都开启自动缓存
123 | */
124 | void page_set_custom_auto_cache_enable(page_base_t *self, bool en);
125 |
126 | /**
127 | * @brief 手动设置缓存是否开启
128 | *
129 | * @param self 页面对象
130 | * @param en 开启或者关闭缓存
131 | */
132 | void page_set_custom_cache_enable(page_base_t *self, bool en);
133 |
134 | /**
135 | * @brief 设置用户加载动画的参数
136 | *
137 | * @param self 页面对象
138 | * @param anim_type 动画类型
139 | * @param time 动画持续时间
140 | * @param path 动画路径
141 | */
142 | void page_set_custom_load_anim_type(page_base_t *self, uint8_t anim_type, uint16_t time, lv_anim_path_cb_t path);
143 |
144 | /**
145 | * @brief 设置用户根对象事件回调函数
146 | *
147 | * @param self 页面对象
148 | * @param root_event_cb 根对象回调函数
149 | */
150 | void page_set_custom_root_event_cb(page_base_t *self, lv_event_cb_t root_event_cb);
151 |
152 | /**
153 | * @brief 获取缓存区里的数据,这里的缓存区是页面push的时候存放自己的数据
154 | *
155 | * @param self 页面对象
156 | * @param ptr 缓存区指针
157 | * @param size 数据长度
158 | * @return true 成功获取数据
159 | * @return false 获取数据失败
160 | */
161 | bool page_get_stash(page_base_t *self, void *ptr, uint32_t size);
162 |
163 | #ifdef __cplusplus
164 | } /* extern "C" */
165 | #endif
--------------------------------------------------------------------------------
/inc/page_config.h:
--------------------------------------------------------------------------------
1 | #pragma once
2 |
3 | #include
4 | #include
5 | #include
6 | #include
7 |
8 | #ifdef LV_LVGL_H_INCLUDE_SIMPLE
9 | #include "lvgl.h"
10 | #else
11 | #include "lvgl/lvgl.h"
12 | #endif
13 |
14 | #define PAGE_MANAGER_USE_GC 0
15 | #define PAGE_MANAGER_USE_LOG 1
16 |
17 | #if PAGE_MANAGER_USE_GC
18 | #define PM_MALLOC(x) lv_mem_alloc(x);
19 | #define FREE(x) lv_men_free(x)
20 | #else
21 | #define PM_MALLOC(x) malloc(x);
22 | #define PM_FREE(x) free(x)
23 | #endif
24 |
25 | #if PAGE_MANAGER_USE_LOG
26 | #define _PM_LOG(format, ...) printf("[PM]" format "\r\n", ##__VA_ARGS__)
27 | #define PM_LOG_INFO(format, ...) _PM_LOG("[Info] " format, ##__VA_ARGS__)
28 | #define PM_LOG_WARN(format, ...) _PM_LOG("[Warn] " format, ##__VA_ARGS__)
29 | #define PM_LOG_ERROR(format, ...) _PM_LOG("[Error] " format, ##__VA_ARGS__)
30 | #else
31 | #define PM_LOG_INFO(...)
32 | #define PM_LOG_WARN(...)
33 | #define PM_LOG_ERROR(...)
34 | #endif
35 |
--------------------------------------------------------------------------------
/inc/page_manager.h:
--------------------------------------------------------------------------------
1 | #pragma once
2 |
3 | #include "adlist.h"
4 | #include "page_base.h"
5 |
6 | #ifdef __cplusplus
7 | extern "C"
8 | {
9 | #endif
10 |
11 | /* Page switching animation type */
12 | typedef enum
13 | {
14 | /* Default (global) animation type */
15 | LOAD_ANIM_GLOBAL = 0,
16 |
17 | /* New page overwrites old page */
18 | LOAD_ANIM_OVER_LEFT,
19 | LOAD_ANIM_OVER_RIGHT,
20 | LOAD_ANIM_OVER_TOP,
21 | LOAD_ANIM_OVER_BOTTOM,
22 |
23 | /* New page pushes old page */
24 | LOAD_ANIM_MOVE_LEFT,
25 | LOAD_ANIM_MOVE_RIGHT,
26 | LOAD_ANIM_MOVE_TOP,
27 | LOAD_ANIM_MOVE_BOTTOM,
28 |
29 | /* The new interface fades in, the old page fades out */
30 | LOAD_ANIM_FADE_ON,
31 |
32 | /* No animation */
33 | LOAD_ANIM_NONE,
34 |
35 | _LOAD_ANIM_LAST = LOAD_ANIM_NONE
36 | } page_load_anim_t;
37 |
38 | /* Page dragging direction */
39 | typedef enum
40 | {
41 | ROOT_DRAG_DIR_NONE,
42 | ROOT_DRAG_DIR_HOR,
43 | ROOT_DRAG_DIR_VER,
44 | } page_root_drag_dir_t;
45 |
46 | /* Animated setter */
47 | typedef void (*lv_anim_setter_t)(void *, int16_t);
48 |
49 | /* Animated getter */
50 | typedef int32_t (*lv_anim_getter_t)(void *);
51 |
52 | /* Animation switching record */
53 | typedef struct
54 | {
55 | /* As the entered party */
56 | struct
57 | {
58 | int32_t start;
59 | int32_t end;
60 | } enter;
61 |
62 | /* As the exited party */
63 | struct
64 | {
65 | int32_t start;
66 | int32_t end;
67 | } exit;
68 | } page_anim_value_t;
69 |
70 | /* 页面加载动画属性 */
71 | typedef struct
72 | {
73 | lv_anim_setter_t setter;
74 | lv_anim_getter_t getter;
75 | page_root_drag_dir_t drag_dir;
76 | page_anim_value_t push;
77 | page_anim_value_t pop;
78 | } page_load_anim_attr_t;
79 |
80 | typedef struct page_manager_t
81 | {
82 | list *page_pool; // 页面池,用于注册页面
83 | list *page_stack; // 页面堆栈,用于收集页面进入方式并依次退出
84 | page_base_t *page_prev; // 上一个页面节点
85 | page_base_t *page_current; // 当前页面节点
86 | struct
87 | {
88 | bool is_switch_req; // 是否切换请求
89 | bool is_busy; // 忙碌标志位
90 | bool is_pushing; // 是否处于压栈状态
91 | page_anim_attr_t current; // 当前动画属性
92 | page_anim_attr_t global; // 全局动画属性
93 | } anim_state;
94 | } page_manager_t;
95 |
96 | /**
97 | * @brief 创建页面管理器对象
98 | *
99 | * @return page_manager_t* 页面管理器对象
100 | */
101 | page_manager_t *page_manager_create(void);
102 |
103 | /**
104 | * @brief 删除页面管理器对象
105 | *
106 | * @param self 页面管理器对象
107 | */
108 | void page_manager_delete(page_manager_t *self);
109 |
110 | /**
111 | * @brief 安装页面到页面管理器中
112 | *
113 | * @param self 页面管理器对象
114 | * @param page_param 页面调度函数
115 | */
116 | void pm_install(page_manager_t *self, const char *name, page_vtable_t* page_param);
117 |
118 | /**
119 | * @brief 页面管理器中卸载页面
120 | *
121 | * @param self 页面管理器对象
122 | * @param name 页面名称
123 | */
124 | void pm_uninstall(page_manager_t *self, const char *name);
125 |
126 | /**
127 | * @brief 页面管理器中加载页面展示
128 | *
129 | * @param self 页面管理器对象
130 | * @param name 页面名称
131 | * @param stash 缓存区,没有数据就填NULL
132 | */
133 | void pm_push(page_manager_t *self, const char *name, const page_stash_t *stash);
134 |
135 | /**
136 | * @brief 回退到上一个页面
137 | *
138 | * @param self 页面管理器对象
139 | */
140 | void pm_pop(page_manager_t *self);
141 |
142 | /**
143 | * @brief 返回主界面
144 | *
145 | * @param self 页面管理器对象
146 | * @return true 返回成功
147 | * @return false 页面正在切换
148 | */
149 | bool pm_back_home(page_manager_t *self);
150 |
151 | /**
152 | * @brief 设置页面管理器全局默认动画参数
153 | *
154 | * @param self 页面管理器对象
155 | * @param anim 动画类型
156 | * @param time 动画持续时间
157 | * @param path 动画路径
158 | */
159 | void pm_set_global_load_anim_type(page_manager_t *self, page_load_anim_t anim, uint16_t time, lv_anim_path_cb_t path);
160 |
161 | #ifdef __cplusplus
162 | } /* extern "C" */
163 | #endif
--------------------------------------------------------------------------------
/inc/page_manager_private.h:
--------------------------------------------------------------------------------
1 | #pragma once
2 |
3 | #include "page_manager.h"
4 |
5 | /* page_base */
6 | page_base_t *page_base_create(void);
7 | void page_base_delete(void *self);
8 | page_base_t *find_page_pool(page_manager_t *self, const char *name);
9 | page_base_t *find_page_stack(page_manager_t *self, const char *name);
10 | page_base_t *get_stack_top(page_manager_t *self);
11 | page_base_t *get_stack_top_after(page_manager_t *self);
12 | void set_satck_clear(page_manager_t *self, bool keep_bottom);
13 | const char *get_page_prev_name(page_manager_t *self);
14 |
15 | /* page_anim */
16 | page_load_anim_t page_get_current_load_anim_type(page_manager_t *self);
17 | bool page_get_current_load_anim_attr(page_manager_t *self, page_load_anim_attr_t *attr);
18 |
19 | /* page_drag */
20 | void page_root_drag_event(lv_obj_t *obj, lv_event_t event);
21 | void root_enable_drag(lv_obj_t *root);
22 | void root_get_drag_predict(lv_coord_t *x, lv_coord_t *y);
23 |
24 | /* page_router */
25 | bool fource_unload(page_base_t *base);
26 | void switch_anim_create(page_manager_t *self, page_base_t *base);
27 | void anim_default_init(page_manager_t *self, lv_anim_t *a);
28 |
29 | /* page_state */
30 | void page_state_update(page_manager_t *self, page_base_t *base);
31 | page_state_t state_unload_execute(page_base_t *base);
--------------------------------------------------------------------------------
/src/adlist.c:
--------------------------------------------------------------------------------
1 | #include
2 | #include "adlist.h"
3 |
4 | /* Create a new list. The created list can be freed with
5 | * listRelease(), but private value of every node need to be freed
6 | * by the user before to call listRelease(), or by setting a free method using
7 | * listSetFreeMethod.
8 | *
9 | * On error, NULL is returned. Otherwise the pointer to the new list. */
10 | list *listCreate(void)
11 | {
12 | struct list *list;
13 |
14 | if ((list = malloc(sizeof(*list))) == NULL)
15 | return NULL;
16 | list->head = list->tail = NULL;
17 | list->len = 0;
18 | list->dup = NULL;
19 | list->free = NULL;
20 | list->match = NULL;
21 | return list;
22 | }
23 |
24 | /* Remove all the elements from the list without destroying the list itself. */
25 | void listEmpty(list *list)
26 | {
27 | unsigned long len;
28 | listNode *current, *next;
29 |
30 | current = list->head;
31 | len = list->len;
32 | while (len--)
33 | {
34 | next = current->next;
35 | if (list->free)
36 | list->free(current->value);
37 | free(current);
38 | current = next;
39 | }
40 | list->head = list->tail = NULL;
41 | list->len = 0;
42 | }
43 |
44 | /* Free the whole list.
45 | *
46 | * This function can't fail. */
47 | void listRelease(list *list)
48 | {
49 | listEmpty(list);
50 | free(list);
51 | }
52 |
53 | /* Add a new node to the list, to head, containing the specified 'value'
54 | * pointer as value.
55 | *
56 | * On error, NULL is returned and no operation is performed (i.e. the
57 | * list remains unaltered).
58 | * On success the 'list' pointer you pass to the function is returned. */
59 | list *listAddNodeHead(list *list, void *value)
60 | {
61 | listNode *node;
62 |
63 | if ((node = malloc(sizeof(*node))) == NULL)
64 | return NULL;
65 | node->value = value;
66 | if (list->len == 0)
67 | {
68 | list->head = list->tail = node;
69 | node->prev = node->next = NULL;
70 | }
71 | else
72 | {
73 | node->prev = NULL;
74 | node->next = list->head;
75 | list->head->prev = node;
76 | list->head = node;
77 | }
78 | list->len++;
79 | return list;
80 | }
81 |
82 | /* Add a new node to the list, to tail, containing the specified 'value'
83 | * pointer as value.
84 | *
85 | * On error, NULL is returned and no operation is performed (i.e. the
86 | * list remains unaltered).
87 | * On success the 'list' pointer you pass to the function is returned. */
88 | list *listAddNodeTail(list *list, void *value)
89 | {
90 | listNode *node;
91 |
92 | if ((node = malloc(sizeof(*node))) == NULL)
93 | return NULL;
94 | node->value = value;
95 | if (list->len == 0)
96 | {
97 | list->head = list->tail = node;
98 | node->prev = node->next = NULL;
99 | }
100 | else
101 | {
102 | node->prev = list->tail;
103 | node->next = NULL;
104 | list->tail->next = node;
105 | list->tail = node;
106 | }
107 | list->len++;
108 | return list;
109 | }
110 |
111 | list *listInsertNode(list *list, listNode *old_node, void *value, int after)
112 | {
113 | listNode *node;
114 |
115 | if ((node = malloc(sizeof(*node))) == NULL)
116 | return NULL;
117 | node->value = value;
118 | if (after)
119 | {
120 | node->prev = old_node;
121 | node->next = old_node->next;
122 | if (list->tail == old_node)
123 | {
124 | list->tail = node;
125 | }
126 | }
127 | else
128 | {
129 | node->next = old_node;
130 | node->prev = old_node->prev;
131 | if (list->head == old_node)
132 | {
133 | list->head = node;
134 | }
135 | }
136 | if (node->prev != NULL)
137 | {
138 | node->prev->next = node;
139 | }
140 | if (node->next != NULL)
141 | {
142 | node->next->prev = node;
143 | }
144 | list->len++;
145 | return list;
146 | }
147 |
148 | /* Remove the specified node from the specified list.
149 | * It's up to the caller to free the private value of the node.
150 | *
151 | * This function can't fail. */
152 | void listDelNode(list *list, listNode *node)
153 | {
154 | if (node->prev)
155 | node->prev->next = node->next;
156 | else
157 | list->head = node->next;
158 | if (node->next)
159 | node->next->prev = node->prev;
160 | else
161 | list->tail = node->prev;
162 | if (list->free)
163 | list->free(node->value);
164 | free(node);
165 | list->len--;
166 | }
167 |
168 | /* Returns a list iterator 'iter'. After the initialization every
169 | * call to listNext() will return the next element of the list.
170 | *
171 | * This function can't fail. */
172 | listIter *listGetIterator(list *list, int direction)
173 | {
174 | listIter *iter;
175 |
176 | if ((iter = malloc(sizeof(*iter))) == NULL)
177 | return NULL;
178 | if (direction == AL_START_HEAD)
179 | iter->next = list->head;
180 | else
181 | iter->next = list->tail;
182 | iter->direction = direction;
183 | return iter;
184 | }
185 |
186 | /* Release the iterator memory */
187 | void listReleaseIterator(listIter *iter)
188 | {
189 | free(iter);
190 | }
191 |
192 | /* Create an iterator in the list private iterator structure */
193 | void listRewind(list *list, listIter *li)
194 | {
195 | li->next = list->head;
196 | li->direction = AL_START_HEAD;
197 | }
198 |
199 | void listRewindTail(list *list, listIter *li)
200 | {
201 | li->next = list->tail;
202 | li->direction = AL_START_TAIL;
203 | }
204 |
205 | /* Return the next element of an iterator.
206 | * It's valid to remove the currently returned element using
207 | * listDelNode(), but not to remove other elements.
208 | *
209 | * The function returns a pointer to the next element of the list,
210 | * or NULL if there are no more elements, so the classical usage
211 | * pattern is:
212 | *
213 | * iter = listGetIterator(list,);
214 | * while ((node = listNext(iter)) != NULL) {
215 | * doSomethingWith(listNodeValue(node));
216 | * }
217 | *
218 | * */
219 | listNode *listNext(listIter *iter)
220 | {
221 | listNode *current = iter->next;
222 |
223 | if (current != NULL)
224 | {
225 | if (iter->direction == AL_START_HEAD)
226 | iter->next = current->next;
227 | else
228 | iter->next = current->prev;
229 | }
230 | return current;
231 | }
232 |
233 | /* Duplicate the whole list. On out of memory NULL is returned.
234 | * On success a copy of the original list is returned.
235 | *
236 | * The 'Dup' method set with listSetDupMethod() function is used
237 | * to copy the node value. Otherwise the same pointer value of
238 | * the original node is used as value of the copied node.
239 | *
240 | * The original list both on success or error is never modified. */
241 | list *listDup(list *orig)
242 | {
243 | list *copy;
244 | listIter iter;
245 | listNode *node;
246 |
247 | if ((copy = listCreate()) == NULL)
248 | return NULL;
249 | copy->dup = orig->dup;
250 | copy->free = orig->free;
251 | copy->match = orig->match;
252 | listRewind(orig, &iter);
253 | while ((node = listNext(&iter)) != NULL)
254 | {
255 | void *value;
256 |
257 | if (copy->dup)
258 | {
259 | value = copy->dup(node->value);
260 | if (value == NULL)
261 | {
262 | listRelease(copy);
263 | return NULL;
264 | }
265 | }
266 | else
267 | {
268 | value = node->value;
269 | }
270 |
271 | if (listAddNodeTail(copy, value) == NULL)
272 | {
273 | /* Free value if dup succeed but listAddNodeTail failed. */
274 | if (copy->free)
275 | copy->free(value);
276 |
277 | listRelease(copy);
278 | return NULL;
279 | }
280 | }
281 | return copy;
282 | }
283 |
284 | /* Search the list for a node matching a given key.
285 | * The match is performed using the 'match' method
286 | * set with listSetMatchMethod(). If no 'match' method
287 | * is set, the 'value' pointer of every node is directly
288 | * compared with the 'key' pointer.
289 | *
290 | * On success the first matching node pointer is returned
291 | * (search starts from head). If no matching node exists
292 | * NULL is returned. */
293 | listNode *listSearchKey(list *list, void *key)
294 | {
295 | listIter iter;
296 | listNode *node;
297 |
298 | listRewind(list, &iter);
299 | while ((node = listNext(&iter)) != NULL)
300 | {
301 | if (list->match)
302 | {
303 | if (list->match(node->value, key))
304 | {
305 | return node;
306 | }
307 | }
308 | else
309 | {
310 | if (key == node->value)
311 | {
312 | return node;
313 | }
314 | }
315 | }
316 | return NULL;
317 | }
318 |
319 | /* Return the element at the specified zero-based index
320 | * where 0 is the head, 1 is the element next to head
321 | * and so on. Negative integers are used in order to count
322 | * from the tail, -1 is the last element, -2 the penultimate
323 | * and so on. If the index is out of range NULL is returned. */
324 | listNode *listIndex(list *list, long index)
325 | {
326 | listNode *n;
327 |
328 | if (index < 0)
329 | {
330 | index = (-index) - 1;
331 | n = list->tail;
332 | while (index-- && n)
333 | n = n->prev;
334 | }
335 | else
336 | {
337 | n = list->head;
338 | while (index-- && n)
339 | n = n->next;
340 | }
341 | return n;
342 | }
343 |
344 | /* Rotate the list removing the tail node and inserting it to the head. */
345 | void listRotateTailToHead(list *list)
346 | {
347 | if (listLength(list) <= 1)
348 | return;
349 |
350 | /* Detach current tail */
351 | listNode *tail = list->tail;
352 | list->tail = tail->prev;
353 | list->tail->next = NULL;
354 | /* Move it as head */
355 | list->head->prev = tail;
356 | tail->prev = NULL;
357 | tail->next = list->head;
358 | list->head = tail;
359 | }
360 |
361 | /* Rotate the list removing the head node and inserting it to the tail. */
362 | void listRotateHeadToTail(list *list)
363 | {
364 | if (listLength(list) <= 1)
365 | return;
366 |
367 | listNode *head = list->head;
368 | /* Detach current head */
369 | list->head = head->next;
370 | list->head->prev = NULL;
371 | /* Move it as tail */
372 | list->tail->next = head;
373 | head->next = NULL;
374 | head->prev = list->tail;
375 | list->tail = head;
376 | }
377 |
378 | /* Add all the elements of the list 'o' at the end of the
379 | * list 'l'. The list 'other' remains empty but otherwise valid. */
380 | void listJoin(list *l, list *o)
381 | {
382 | if (o->len == 0)
383 | return;
384 |
385 | o->head->prev = l->tail;
386 |
387 | if (l->tail)
388 | l->tail->next = o->head;
389 | else
390 | l->head = o->head;
391 |
392 | l->tail = o->tail;
393 | l->len += o->len;
394 |
395 | /* Setup other as an empty list. */
396 | o->head = o->tail = NULL;
397 | o->len = 0;
398 | }
--------------------------------------------------------------------------------
/src/page_anim.c:
--------------------------------------------------------------------------------
1 | #include "page_manager_private.h"
2 |
3 | static bool _get_load_anim_attr(uint8_t anim, page_load_anim_attr_t *attr);
4 | static void _lv_anim_setter_x(void *obj, int16_t v);
5 | static int32_t _lv_anim_getter_x(void *obj);
6 | static void _lv_anim_setter_y(void *obj, int16_t v);
7 | static int32_t _lv_anim_getter_y(void *obj);
8 | static void _lv_anim_setter_opa(void *obj, int16_t v);
9 | static int32_t _lv_anim_getter_opa(void *obj);
10 |
11 | /**
12 | * @brief 获取加载动画的参数
13 | *
14 | * @param anim 动画路径
15 | * @param attr [out]动画属性
16 | * @return true 获取成功
17 | * @return false 获取失败
18 | */
19 | static bool _get_load_anim_attr(uint8_t anim, page_load_anim_attr_t *attr)
20 | {
21 | lv_coord_t hor = LV_HOR_RES;
22 | lv_coord_t ver = LV_VER_RES;
23 |
24 | switch (anim)
25 | {
26 | case LOAD_ANIM_OVER_LEFT:
27 | attr->drag_dir = ROOT_DRAG_DIR_HOR;
28 |
29 | attr->push.enter.start = hor;
30 | attr->push.enter.end = 0;
31 | attr->push.exit.start = 0;
32 | attr->push.exit.end = 0;
33 |
34 | attr->pop.enter.start = 0;
35 | attr->pop.enter.end = 0;
36 | attr->pop.exit.start = 0;
37 | attr->pop.exit.end = hor;
38 | break;
39 |
40 | case LOAD_ANIM_OVER_RIGHT:
41 | attr->drag_dir = ROOT_DRAG_DIR_HOR;
42 |
43 | attr->push.enter.start = -hor;
44 | attr->push.enter.end = 0;
45 | attr->push.exit.start = 0;
46 | attr->push.exit.end = 0;
47 |
48 | attr->pop.enter.start = 0;
49 | attr->pop.enter.end = 0;
50 | attr->pop.exit.start = 0;
51 | attr->pop.exit.end = -hor;
52 | break;
53 |
54 | case LOAD_ANIM_OVER_TOP:
55 | attr->drag_dir = ROOT_DRAG_DIR_VER;
56 |
57 | attr->push.enter.start = ver;
58 | attr->push.enter.end = 0;
59 | attr->push.exit.start = 0;
60 | attr->push.exit.end = 0;
61 |
62 | attr->pop.enter.start = 0;
63 | attr->pop.enter.end = 0;
64 | attr->pop.exit.start = 0;
65 | attr->pop.exit.end = ver;
66 | break;
67 |
68 | case LOAD_ANIM_OVER_BOTTOM:
69 | attr->drag_dir = ROOT_DRAG_DIR_VER;
70 |
71 | attr->push.enter.start = -ver;
72 | attr->push.enter.end = 0;
73 | attr->push.exit.start = 0;
74 | attr->push.exit.end = 0;
75 |
76 | attr->pop.enter.start = 0;
77 | attr->pop.enter.end = 0;
78 | attr->pop.exit.start = 0;
79 | attr->pop.exit.end = -ver;
80 | break;
81 |
82 | case LOAD_ANIM_MOVE_LEFT:
83 | attr->drag_dir = ROOT_DRAG_DIR_HOR;
84 |
85 | attr->push.enter.start = hor;
86 | attr->push.enter.end = 0;
87 | attr->push.exit.start = 0;
88 | attr->push.exit.end = -hor;
89 |
90 | attr->pop.enter.start = -hor;
91 | attr->pop.enter.end = 0;
92 | attr->pop.exit.start = 0;
93 | attr->pop.exit.end = hor;
94 | break;
95 |
96 | case LOAD_ANIM_MOVE_RIGHT:
97 | attr->drag_dir = ROOT_DRAG_DIR_HOR;
98 |
99 | attr->push.enter.start = -hor;
100 | attr->push.enter.end = 0;
101 | attr->push.exit.start = 0;
102 | attr->push.exit.end = hor;
103 |
104 | attr->pop.enter.start = hor;
105 | attr->pop.enter.end = 0;
106 | attr->pop.exit.start = 0;
107 | attr->pop.exit.end = -hor;
108 | break;
109 |
110 | case LOAD_ANIM_MOVE_TOP:
111 | attr->drag_dir = ROOT_DRAG_DIR_VER;
112 |
113 | attr->push.enter.start = ver;
114 | attr->push.enter.end = 0;
115 | attr->push.exit.start = 0;
116 | attr->push.exit.end = -ver;
117 |
118 | attr->pop.enter.start = -ver;
119 | attr->pop.enter.end = 0;
120 | attr->pop.exit.start = 0;
121 | attr->pop.exit.end = ver;
122 | break;
123 |
124 | case LOAD_ANIM_MOVE_BOTTOM:
125 | attr->drag_dir = ROOT_DRAG_DIR_VER;
126 |
127 | attr->push.enter.start = -ver;
128 | attr->push.enter.end = 0;
129 | attr->push.exit.start = 0;
130 | attr->push.exit.end = ver;
131 |
132 | attr->pop.enter.start = ver;
133 | attr->pop.enter.end = 0;
134 | attr->pop.exit.start = 0;
135 | attr->pop.exit.end = -ver;
136 | break;
137 |
138 | case LOAD_ANIM_FADE_ON:
139 | attr->drag_dir = ROOT_DRAG_DIR_NONE;
140 |
141 | attr->push.enter.start = LV_OPA_TRANSP;
142 | attr->push.enter.end = LV_OPA_COVER;
143 | attr->push.exit.start = LV_OPA_COVER;
144 | attr->push.exit.end = LV_OPA_COVER;
145 |
146 | attr->pop.enter.start = LV_OPA_COVER;
147 | attr->pop.enter.end = LV_OPA_COVER;
148 | attr->pop.exit.start = LV_OPA_COVER;
149 | attr->pop.exit.end = LV_OPA_TRANSP;
150 | break;
151 |
152 | case LOAD_ANIM_NONE:
153 | memset(attr, 0, sizeof(page_load_anim_attr_t));
154 | return true;
155 |
156 | default:
157 | PM_LOG_ERROR("Load anim type error: %d", anim);
158 | return false;
159 | }
160 |
161 | /* 确定动画的setter和getter*/
162 | if (attr->drag_dir == ROOT_DRAG_DIR_HOR)
163 | {
164 | attr->setter = _lv_anim_setter_x;
165 | attr->getter = _lv_anim_getter_x;
166 | }
167 | else if (attr->drag_dir == ROOT_DRAG_DIR_VER)
168 | {
169 | attr->setter = _lv_anim_setter_y;
170 | attr->getter = _lv_anim_getter_y;
171 | }
172 | else
173 | {
174 | attr->setter = _lv_anim_setter_opa;
175 | attr->getter = _lv_anim_getter_opa;
176 | }
177 |
178 | return true;
179 | }
180 |
181 | /**
182 | * @brief 设置x轴的数值
183 | *
184 | * @param obj lvgl屏幕对象
185 | * @param v 滑动数值
186 | */
187 | static void _lv_anim_setter_x(void *obj, int16_t v)
188 | {
189 | lv_obj_set_x((lv_obj_t *)obj, v);
190 | };
191 |
192 | /**
193 | * @brief 获取x轴的数值
194 | *
195 | * @param obj lvgl屏幕对象
196 | * @param v 滑动数值
197 | */
198 | static int32_t _lv_anim_getter_x(void *obj)
199 | {
200 | return (int32_t)lv_obj_get_x((lv_obj_t *)obj);
201 | };
202 |
203 | /**
204 | * @brief 设置y轴的数值
205 | *
206 | * @param obj lvgl屏幕对象
207 | * @param v 滑动数值
208 | */
209 | static void _lv_anim_setter_y(void *obj, int16_t v)
210 | {
211 | lv_obj_set_y((lv_obj_t *)obj, v);
212 | };
213 |
214 | /**
215 | * @brief 获取y轴的数值
216 | *
217 | * @param obj lvgl屏幕对象
218 | * @param v 滑动数值
219 | */
220 | static int32_t _lv_anim_getter_y(void *obj)
221 | {
222 | return (int32_t)lv_obj_get_y((lv_obj_t *)obj);
223 | };
224 |
225 | /**
226 | * @brief 设置透明度的数值
227 | *
228 | * @param obj lvgl屏幕对象
229 | * @param v 滑动数值
230 | */
231 | static void _lv_anim_setter_opa(void *obj, int16_t v)
232 | {
233 | lv_obj_set_style_local_bg_opa((lv_obj_t *)obj, LV_OBJ_PART_MAIN, LV_STATE_DEFAULT, (lv_opa_t)v);
234 | }
235 |
236 | /**
237 | * @brief 获取透明度的数值
238 | *
239 | * @param obj lvgl屏幕对象
240 | * @param v 滑动数值
241 | */
242 | static int32_t _lv_anim_getter_opa(void *obj)
243 | {
244 | return (int32_t)lv_obj_get_style_bg_opa((lv_obj_t *)obj, LV_OBJ_PART_MAIN);
245 | }
246 |
247 | /**
248 | * @brief 获取当前页面的用户动画参数
249 | *
250 | * @param self 页面管理器对象
251 | * @return page_load_anim_t 用户动画参数
252 | */
253 | page_load_anim_t page_get_current_load_anim_type(page_manager_t *self)
254 | {
255 | return (page_load_anim_t)self->anim_state.current.type;
256 | }
257 |
258 | /**
259 | * @brief 获取当前页面动画参数
260 | *
261 | * @param self 页面管理器对象
262 | * @param attr [out]页面动画参数
263 | * @return true 获取成功
264 | * @return false 获取失败
265 | */
266 | bool page_get_current_load_anim_attr(page_manager_t *self, page_load_anim_attr_t *attr)
267 | {
268 | return _get_load_anim_attr(page_get_current_load_anim_type(self), attr);
269 | }
270 |
--------------------------------------------------------------------------------
/src/page_base.c:
--------------------------------------------------------------------------------
1 | #include "page_base.h"
2 |
3 | /**
4 | * @brief 创建基础页面
5 | *
6 | * @return page_base_t* 返回页面对象
7 | */
8 | page_base_t *page_base_create(void)
9 | {
10 | page_base_t *page_base = (page_base_t *)PM_MALLOC(sizeof(page_base_t));
11 | if (page_base == NULL)
12 | {
13 | PM_LOG_ERROR("page_base alloc error\n");
14 | return NULL;
15 | }
16 | return page_base;
17 | }
18 |
19 | /**
20 | * @brief 删除页面对象
21 | *
22 | * @param self 页面对象
23 | */
24 | void page_base_delete(void *self)
25 | {
26 | if (self == NULL)
27 | {
28 | PM_LOG_ERROR("page_base is NULL\n");
29 | return;
30 | }
31 | PM_FREE(self);
32 | }
33 |
34 | /**
35 | * @brief 设置自动缓存
36 | *
37 | * @param self 页面对象
38 | * @param en 是都开启自动缓存
39 | */
40 | void page_set_custom_auto_cache_enable(page_base_t *self, bool en)
41 | {
42 | self->priv.req_disable_auto_cache = !en;
43 | }
44 |
45 | /**
46 | * @brief 手动设置缓存是否开启
47 | *
48 | * @param self 页面对象
49 | * @param en 开启或者关闭缓存
50 | */
51 | void page_set_custom_cache_enable(page_base_t *self, bool en)
52 | {
53 | page_set_custom_auto_cache_enable(self, false);
54 | self->priv.req_enable_cache = en;
55 | }
56 |
57 | /**
58 | * @brief 设置用户加载动画的参数
59 | *
60 | * @param self 页面对象
61 | * @param anim_type 动画类型
62 | * @param time 动画持续时间
63 | * @param path 动画路径
64 | */
65 | void page_set_custom_load_anim_type(page_base_t *self, uint8_t anim_type, uint16_t time, lv_anim_path_cb_t path)
66 | {
67 | self->priv.anim.attr.type = anim_type;
68 | self->priv.anim.attr.time = time;
69 | self->priv.anim.attr.path = path;
70 | }
71 |
72 | /**
73 | * @brief 设置用户根对象事件回调函数
74 | *
75 | * @param self 页面对象
76 | * @param root_event_cb 根对象回调函数
77 | */
78 | void page_set_custom_root_event_cb(page_base_t *self, lv_event_cb_t root_event_cb)
79 | {
80 | self->root_event_cb = root_event_cb;
81 | }
82 |
83 | /**
84 | * @brief 获取缓存区里的数据
85 | *
86 | * @param self 页面对象
87 | * @param ptr 缓存区指针
88 | * @param size 数据长度
89 | * @return true 成功获取数据
90 | * @return false 获取数据失败
91 | */
92 | bool page_get_stash(page_base_t *self, void *ptr, uint32_t size)
93 | {
94 | bool retval = false;
95 | if (self->priv.stash.ptr != NULL && self->priv.stash.size == size)
96 | {
97 | memcpy(ptr, self->priv.stash.ptr, self->priv.stash.size);
98 | retval = true;
99 | }
100 | return retval;
101 | }
--------------------------------------------------------------------------------
/src/page_drag.c:
--------------------------------------------------------------------------------
1 | #include "page_manager_private.h"
2 | #include
3 |
4 | #define MAX(x, y) ((x) > (y) ? (x) : (y))
5 | #define MIN(x, y) ((x) > (y) ? (y) : (x))
6 | #define CONSTRAIN(amt, low, high) ((amt) < (low) ? (low) : ((amt) > (high) ? (high) : (amt)))
7 |
8 | /* The distance threshold to trigger the drag */
9 | #define PM_INDEV_DEF_DRAG_THROW 20
10 |
11 | static void _on_root_async_leavel(void *data);
12 | static void _on_root_anim_finish(lv_anim_t *a);
13 |
14 | /**
15 | * @brief 页面拖动事件回调
16 | *
17 | * @param obj lvgl对象
18 | * @param event 事件类型
19 | */
20 | void page_root_drag_event(lv_obj_t * obj, lv_event_t event)
21 | {
22 | page_base_t *base = (page_base_t *)lv_obj_get_user_data(obj);
23 |
24 | if (base == NULL)
25 | {
26 | PM_LOG_ERROR("Page base is NULL");
27 | return;
28 | }
29 |
30 | page_manager_t *manager = base->manager;
31 | page_load_anim_attr_t anim_attr;
32 |
33 | if (!page_get_current_load_anim_attr(manager, &anim_attr))
34 | {
35 | PM_LOG_ERROR("Can't get current anim attr");
36 | return;
37 | }
38 | if (base->root_event_cb != NULL)
39 | {
40 | base->root_event_cb(obj, event);
41 | }
42 |
43 | switch (event)
44 | {
45 | case LV_EVENT_PRESSED:
46 | {
47 | if (manager->anim_state.is_switch_req)
48 | return;
49 | if (!manager->anim_state.is_busy)
50 | return;
51 |
52 | PM_LOG_INFO("Root anim interrupted");
53 | lv_anim_del(obj, anim_attr.setter);
54 | manager->anim_state.is_busy = false;
55 | }
56 | break;
57 | case LV_EVENT_PRESSING:
58 | {
59 | lv_coord_t cur = anim_attr.getter(obj);
60 |
61 | lv_coord_t max = MAX(anim_attr.pop.exit.start, anim_attr.pop.exit.end);
62 | lv_coord_t min = MIN(anim_attr.pop.exit.start, anim_attr.pop.exit.end);
63 |
64 | lv_point_t offset;
65 | lv_indev_get_vect(lv_indev_get_act(), &offset);
66 |
67 | if (anim_attr.drag_dir == ROOT_DRAG_DIR_HOR)
68 | {
69 | cur += offset.x;
70 | }
71 | else if (anim_attr.drag_dir == ROOT_DRAG_DIR_VER)
72 | {
73 | cur += offset.y;
74 | }
75 |
76 | anim_attr.setter(obj, CONSTRAIN(cur, min, max));
77 | }
78 | break;
79 | case LV_EVENT_RELEASED:
80 | {
81 | if (manager->anim_state.is_switch_req)
82 | {
83 | return;
84 | }
85 |
86 | lv_coord_t offset_sum = anim_attr.push.enter.end - anim_attr.push.enter.start;
87 |
88 | lv_coord_t x_predict = 0;
89 | lv_coord_t y_predict = 0;
90 | root_get_drag_predict(&x_predict, &y_predict);
91 |
92 | lv_coord_t start = anim_attr.getter(obj);
93 | lv_coord_t end = start;
94 |
95 | if (anim_attr.drag_dir == ROOT_DRAG_DIR_HOR)
96 | {
97 | end += x_predict;
98 | PM_LOG_INFO("Root drag x_predict = %d", end);
99 | }
100 | else if (anim_attr.drag_dir == ROOT_DRAG_DIR_VER)
101 | {
102 | end += y_predict;
103 | PM_LOG_INFO("Root drag y_predict = %d", end);
104 | }
105 |
106 | if (abs(end) > abs((int)offset_sum) / 2)
107 | {
108 | lv_async_call(_on_root_async_leavel, base);
109 | }
110 | else if (end != anim_attr.push.enter.end)
111 | {
112 | manager->anim_state.is_busy = true;
113 |
114 | lv_anim_t a;
115 | anim_default_init(manager, &a);
116 | a.user_data = manager;
117 | lv_anim_set_var(&a, obj);
118 | lv_anim_set_values(&a, start, anim_attr.push.enter.end);
119 | lv_anim_set_exec_cb(&a, anim_attr.setter);
120 | lv_anim_set_ready_cb(&a, _on_root_anim_finish);
121 | lv_anim_start(&a);
122 | PM_LOG_INFO("Root anim start");
123 | }
124 | }
125 | break;
126 |
127 | default:
128 | break;
129 | }
130 | }
131 |
132 | /**
133 | * @brief 拖动动画结束事件回调
134 | *
135 | * @param a 动画对象
136 | */
137 | static void _on_root_anim_finish(lv_anim_t *a)
138 | {
139 | page_manager_t *manager = (page_manager_t *)a->user_data;
140 | PM_LOG_INFO("Root anim finish");
141 | manager->anim_state.is_busy = false;
142 | }
143 |
144 | /**
145 | * @brief 开启root的拖拽功能
146 | *
147 | * @param root 页面根对象
148 | */
149 | void root_enable_drag(lv_obj_t *root)
150 | {
151 | lv_obj_set_event_cb(root, page_root_drag_event);
152 | PM_LOG_INFO("Root drag enabled");
153 | }
154 |
155 | /**
156 | * @brief 拖动结束时的异步回调
157 | *
158 | * @param data 页面基本对象
159 | */
160 | static void _on_root_async_leavel(void *data)
161 | {
162 | page_base_t *base = (page_base_t *)data;
163 | PM_LOG_INFO("Page(%s) send event: LV_EVENT_LEAVE, need to handle...", base->name);
164 | lv_event_send(base->root, LV_EVENT_LEAVE, NULL);
165 | }
166 |
167 | /**
168 | * @brief 获取拖曳惯性预测停止点
169 | *
170 | * @param x
171 | * @param y
172 | */
173 | void root_get_drag_predict(lv_coord_t* x, lv_coord_t* y)
174 | {
175 | lv_indev_t* indev = lv_indev_get_act();
176 | lv_point_t vect;
177 | lv_indev_get_vect(indev, &vect);
178 |
179 | lv_coord_t y_predict = 0;
180 | lv_coord_t x_predict = 0;
181 |
182 | while (vect.y != 0)
183 | {
184 | y_predict += vect.y;
185 | vect.y = vect.y * (100 - PM_INDEV_DEF_DRAG_THROW) / 100;
186 | }
187 |
188 | while (vect.x != 0)
189 | {
190 | x_predict += vect.x;
191 | vect.x = vect.x * (100 - PM_INDEV_DEF_DRAG_THROW) / 100;
192 | }
193 |
194 | *x = x_predict;
195 | *y = y_predict;
196 | }
197 |
--------------------------------------------------------------------------------
/src/page_manager.c:
--------------------------------------------------------------------------------
1 | #include "page_manager_private.h"
2 |
3 | #define PM_EMPTY_PAGE_NAME "EMPTY_PAGE"
4 |
5 | /**
6 | * @brief 创建页面管理器对象
7 | *
8 | * @return page_manager_t* 页面管理器对象
9 | */
10 | page_manager_t *page_manager_create(void)
11 | {
12 | page_manager_t *page_manager = (page_manager_t *)PM_MALLOC(sizeof(page_manager_t));
13 | if (page_manager == NULL)
14 | {
15 | PM_LOG_ERROR("page_manager alloc error\n");
16 | return NULL;
17 | }
18 | PM_LOG_INFO("page_manager alloc sucess\n");
19 | memset(page_manager, 0, sizeof(page_manager_t));
20 | page_manager->page_pool = listCreate();
21 | listSetFreeMethod(page_manager->page_pool, page_base_delete);
22 | page_manager->page_stack = listCreate();
23 | return page_manager;
24 | }
25 |
26 | /**
27 | * @brief 删除页面管理器对象
28 | *
29 | * @param self 页面管理器对象
30 | */
31 | void page_manager_delete(page_manager_t *self)
32 | {
33 | if (self == NULL)
34 | {
35 | PM_LOG_ERROR("page_manager is NULL\n");
36 | return;
37 | }
38 | listRelease(self->page_pool);
39 | listRelease(self->page_stack);
40 | self->page_current = NULL;
41 | self->page_prev = NULL;
42 | PM_FREE(self);
43 | PM_LOG_INFO("page_manager free sucess\n");
44 | }
45 |
46 | /**
47 | * @brief 通过名字在页面池中到到页面对象
48 | *
49 | * @param self 页面管理器对象
50 | * @param name 页面名称
51 | * @return page_base_t* 页面对象
52 | */
53 | page_base_t *find_page_pool(page_manager_t *self, const char *name)
54 | {
55 | listIter *iter = listGetIterator(self->page_pool, AL_START_HEAD);
56 | for (listNode *node = listNext(iter); node != NULL; node = listNext(iter))
57 | {
58 | if (strcmp(((page_base_t *)node->value)->name, name) == 0)
59 | {
60 | PM_LOG_INFO("find object %s addr[%p]\n", name, node);
61 | listReleaseIterator(iter);
62 | return (page_base_t *)node->value;
63 | }
64 | }
65 | listReleaseIterator(iter);
66 | return NULL;
67 | }
68 |
69 | /**
70 | * @brief 从栈中找到页面
71 | *
72 | * @param self 页面管理器对象
73 | * @param name 页面名称
74 | * @return page_base_t*
75 | */
76 | page_base_t *find_page_stack(page_manager_t *self, const char *name)
77 | {
78 | listIter *iter = listGetIterator(self->page_stack, AL_START_HEAD);
79 | for (listNode *node = listNext(iter); node != NULL; node = listNext(iter))
80 | {
81 | if (strcmp(((page_base_t *)node->value)->name, name) == 0)
82 | {
83 | PM_LOG_INFO("find object %s addr[%p]\n", name, node);
84 | listReleaseIterator(iter);
85 | return (page_base_t *)node->value;
86 | }
87 | }
88 | listReleaseIterator(iter);
89 | return NULL;
90 | }
91 |
92 | /**
93 | * @brief 向页面管理器中注册页面
94 | *
95 | * @param self 页面管理器对象
96 | * @param base 页面对象
97 | */
98 | static void pm_register(page_manager_t *self, page_base_t *base)
99 | {
100 | if (find_page_pool(self, base->name) != NULL)
101 | {
102 | PM_LOG_ERROR("Page(%s) was multi registered", base->name);
103 | return;
104 | }
105 |
106 | base->manager = self;
107 | listAddNodeTail(self->page_pool, base);
108 | PM_LOG_INFO("page(%s) manager register", base->name);
109 | }
110 |
111 | /**
112 | * @brief 向页面管理器中注销页面
113 | *
114 | * @param self 页面管理器对象
115 | * @param base 页面对象
116 | */
117 | static void pm_unregister(page_manager_t *self, const char *name)
118 | {
119 | PM_LOG_INFO("Page(%s) unregister...", name);
120 |
121 | page_base_t *base = find_page_stack(self, name);
122 | if (base != NULL)
123 | {
124 | PM_LOG_ERROR("Page(%s) was in recycle_pool", name);
125 | return;
126 | }
127 |
128 | if (find_page_pool(self, name) != NULL)
129 | {
130 | PM_LOG_ERROR("Page(%s) was multi registered", name);
131 | }
132 |
133 | listDelNode(self->page_pool, listSearchKey(self->page_pool, base));
134 | PM_LOG_INFO("Unregister OK");
135 | }
136 |
137 | /**
138 | * @brief 安装页面到页面管理器中
139 | *
140 | * @param self 页面管理器对象
141 | * @param page_param 页面调度函数
142 | */
143 | void pm_install(page_manager_t *self, const char *name, page_vtable_t* page_param)
144 | {
145 | page_base_t *page_base = page_base_create();
146 |
147 | page_base->base = page_param;
148 | page_base->name = name;
149 | page_base->manager = NULL;
150 | page_base->root = NULL;
151 | page_base->root_event_cb = NULL;
152 | page_base->user_data = NULL;
153 | memset(&page_base->priv, 0, sizeof(page_base->priv));
154 |
155 | page_base->base->on_custom_attr_config(page_base);
156 |
157 | pm_register(self, page_base);
158 | }
159 |
160 | /**
161 | * @brief 页面管理器中卸载页面
162 | *
163 | * @param self 页面管理器对象
164 | * @param name 页面名称
165 | */
166 | void pm_uninstall(page_manager_t *self, const char *name)
167 | {
168 | PM_LOG_INFO("Page(%s) uninstall...", name);
169 | page_base_t *base = find_page_pool(self, name);
170 | if (base == NULL)
171 | {
172 | PM_LOG_ERROR("Page(%s) was not found", name);
173 | return;
174 | }
175 |
176 | pm_unregister(self, name);
177 |
178 | if (base->priv.is_cached)
179 | {
180 | PM_LOG_WARN("Page(%s) has cached, unloading...", name);
181 | base->priv.state = PAGE_STATE_UNLOAD;
182 | page_state_update(self, base);
183 | }
184 | else
185 | {
186 | PM_LOG_INFO("Page(%s) has not cache", name);
187 | }
188 |
189 | PM_LOG_INFO("Uninstall OK");
190 | }
191 |
192 | /**
193 | * @brief 获取上一个页面的名字
194 | *
195 | * @param self 页面管理器对象
196 | * @return const char* 页面名称
197 | */
198 | const char *get_page_prev_name(page_manager_t *self)
199 | {
200 | if (self->page_prev != NULL)
201 | {
202 | return self->page_prev->name;
203 | }
204 | return PM_EMPTY_PAGE_NAME;
205 | }
206 |
207 | /**
208 | * @brief 获取栈顶页面
209 | *
210 | * @param self 页面管理器对象
211 | * @return page_base_t* 页面对象
212 | */
213 | page_base_t *get_stack_top(page_manager_t *self)
214 | {
215 | listNode *base_node = listIndex(self->page_stack, 0);
216 | if (base_node == NULL)
217 | {
218 | PM_LOG_ERROR("page_manage is empty");
219 | return NULL;
220 | }
221 | return (page_base_t *)base_node->value;
222 | }
223 |
224 | /**
225 | * @brief 获取栈顶页面后面的页面
226 | *
227 | * @param self 页面管理器对象
228 | * @return page_base_t* 页面对象
229 | */
230 | page_base_t *get_stack_top_after(page_manager_t *self)
231 | {
232 | listNode *base_node = listIndex(self->page_stack, 1);
233 | if (base_node == NULL)
234 | {
235 | PM_LOG_ERROR("page_manage is empty or only heve one page");
236 | return NULL;
237 | }
238 | return (page_base_t *)base_node->value;
239 | }
240 |
241 | /**
242 | * @brief 清除页面
243 | *
244 | * @param self 页面管理器对象
245 | * @param keep_bottom 是否保留栈底页面
246 | */
247 | void set_satck_clear(page_manager_t *self, bool keep_bottom)
248 | {
249 | while (1)
250 | {
251 | page_base_t *top = get_stack_top(self);
252 | if (top == NULL)
253 | {
254 | PM_LOG_INFO("Page stack is empty, breaking...");
255 | break;
256 | }
257 |
258 | page_base_t *top_after = get_stack_top_after(self);
259 |
260 | if (top_after == NULL)
261 | {
262 | if (keep_bottom)
263 | {
264 | self->page_prev = top;
265 | PM_LOG_INFO("Keep page stack bottom(%s), breaking...", top->name);
266 | break;
267 | }
268 | else
269 | {
270 | self->page_prev = NULL;
271 | }
272 | }
273 |
274 | fource_unload(top);
275 | listDelNode(self->page_stack, listSearchKey(self->page_stack, top));
276 | }
277 | PM_LOG_INFO("Stack clear done");
278 | }
--------------------------------------------------------------------------------
/src/page_router.c:
--------------------------------------------------------------------------------
1 | #include "page_manager_private.h"
2 |
3 | static bool _switch_anim_state_check(page_manager_t *self);
4 | static void _switch_anim_type_update(page_manager_t *self, page_base_t *base);
5 | static void _page_switch(page_manager_t *self, page_base_t *new_node, bool is_push_act, const page_stash_t *stash);
6 |
7 | /**
8 | * @brief 推送已安装的页面显示
9 | *
10 | * @param self 页面管理器
11 | * @param name 页面名
12 | * @param stash push时用户的自定义参数
13 | */
14 | void pm_push(page_manager_t *self, const char *name, const page_stash_t *stash)
15 | {
16 | // 检查是否正在执行切换页面的动画
17 | if (!_switch_anim_state_check(self))
18 | {
19 | PM_LOG_WARN("Page stack anim, cat't pop");
20 | return;
21 | }
22 |
23 | // 检测是否处于栈区
24 | if (find_page_stack(self, name) != NULL)
25 | {
26 | PM_LOG_ERROR("Page(%s) was multi push", name);
27 | return;
28 | }
29 |
30 | // 检测页面是否在页面池中被注册
31 | page_base_t *base = find_page_pool(self, name);
32 | if (base == NULL)
33 | {
34 | PM_LOG_ERROR("Page(%s) was not install", name);
35 | return;
36 | }
37 |
38 | /* 同步自动缓存配置*/
39 | base->priv.is_disable_auto_cache = base->priv.req_disable_auto_cache;
40 |
41 | /* 页面压栈 */
42 | listAddNodeHead(self->page_stack, base);
43 |
44 | /* 切换页面 */
45 | _page_switch(self, base, true, stash);
46 | }
47 |
48 | /**
49 | * @brief 回退到上一个页面
50 | *
51 | * @param self 页面管理器对象
52 | */
53 | void pm_pop(page_manager_t *self)
54 | {
55 | // 检查是否正在执行切换页面的动画
56 | if (!_switch_anim_state_check(self))
57 | {
58 | PM_LOG_WARN("Page stack anim, cat't pop");
59 | return;
60 | }
61 |
62 | /* 获取栈顶页面 */
63 | page_base_t *top = get_stack_top(self);
64 |
65 | if (top == NULL)
66 | {
67 | PM_LOG_WARN("Page stack is empty, cat't pop");
68 | return;
69 | }
70 |
71 | if (!top->priv.is_disable_auto_cache)
72 | {
73 | PM_LOG_INFO("Page(%s) has auto cache, cache disabled", top->name);
74 | top->priv.is_cached = false;
75 | }
76 |
77 | PM_LOG_INFO("Page(%s) pop << [Screen]", top->name);
78 |
79 | // 页面出栈
80 | listDelNode(self->page_stack, listSearchKey(self->page_stack, top));
81 |
82 | top = get_stack_top(self);
83 |
84 | if (top != NULL)
85 | {
86 | /* 切换页面 */
87 | _page_switch(self, top, false, NULL);
88 | }
89 | else
90 | {
91 | PM_LOG_WARN("Page stack is empty, cat't pop");
92 | }
93 | }
94 |
95 | /**
96 | * @brief 切换页面
97 | *
98 | * @param self 页面管理器对象
99 | * @param new_node 新的页面
100 | * @param is_push_act 动画状态
101 | * @param stash 缓存区
102 | */
103 | static void _page_switch(page_manager_t *self, page_base_t *new_node, bool is_push_act, const page_stash_t *stash)
104 | {
105 | if (new_node == NULL)
106 | {
107 | PM_LOG_ERROR("newNode is nullptr");
108 | return;
109 | }
110 |
111 | if (self->anim_state.is_switch_req) // 确定没有页面在切换
112 | {
113 | PM_LOG_WARN("Page switch busy, reqire(%s) is ignore", new_node->name);
114 | return;
115 | }
116 |
117 | self->anim_state.is_switch_req = true; // 请求切换页面
118 |
119 | if (stash != NULL) // 如果有缓存区
120 | {
121 | PM_LOG_INFO("stash is detect, %s >> stash(%p) >> %s", get_page_prev_name(self), stash, new_node->name);
122 |
123 | void *buffer = NULL;
124 |
125 | //如果缓存区是空则申请内存
126 | if (new_node->priv.stash.ptr == NULL)
127 | {
128 | buffer = PM_MALLOC(stash->size);
129 | if (buffer == NULL)
130 | {
131 | PM_LOG_ERROR("stash malloc failed");
132 | }
133 | else
134 | {
135 | PM_LOG_INFO("stash(%p) malloc[%d]", buffer, stash->size);
136 | }
137 | }
138 | // 如果缓存区大小和现在大小一致。则获取内存地址(为下文营造非空判断)
139 | else if (new_node->priv.stash.size == stash->size)
140 | {
141 | buffer = new_node->priv.stash.ptr;
142 | PM_LOG_INFO("stash(%p) is exist", buffer);
143 | }
144 |
145 | // 将当前缓存的地址内容复制到缓存
146 | if (buffer != NULL)
147 | {
148 | memcpy(buffer, stash->ptr, stash->size);
149 | PM_LOG_INFO("stash memcpy[%d] %p >> %p", stash->size, stash->ptr, buffer);
150 | new_node->priv.stash.ptr = buffer;
151 | new_node->priv.stash.size = stash->size;
152 | }
153 | }
154 |
155 | // 当前页面更新
156 | self->page_current = new_node;
157 |
158 | // 如果页面有被缓存则跳过PAGE_STATE_LOAD
159 | if (self->page_current->priv.is_cached)
160 | {
161 | PM_LOG_INFO("Page(%s) has cached, appear driectly", self->page_current->name);
162 | self->page_current->priv.state = PAGE_STATE_WILL_APPEAR;
163 | }
164 | else
165 | {
166 | self->page_current->priv.state = PAGE_STATE_LOAD;
167 | }
168 |
169 | // 如果上一个页面存在则将is_enter标志位置0
170 | if (self->page_prev != NULL)
171 | {
172 | self->page_prev->priv.anim.is_enter = false;
173 | }
174 |
175 | // 把当前的设置为进入
176 | self->page_current->priv.anim.is_enter = true;
177 | // 设置动画为推送状态
178 | self->anim_state.is_pushing = is_push_act;
179 |
180 | // 如果动画标志位为进入
181 | if (self->anim_state.is_pushing)
182 | {
183 | // 根据当前页面更新动画配置
184 | _switch_anim_type_update(self, self->page_current);
185 | }
186 |
187 | // 更新页面
188 | page_state_update(self, self->page_prev);
189 | page_state_update(self, self->page_current);
190 |
191 | // 改变页面前后关系
192 | if (self->anim_state.is_pushing)
193 | {
194 | PM_LOG_INFO("Page PUSH is detect, move Page(%s) to foreground", self->page_current->name);
195 | if (self->page_prev)
196 | lv_obj_move_foreground(self->page_prev->root);
197 | lv_obj_move_foreground(self->page_current->root);
198 | }
199 | else
200 | {
201 | PM_LOG_INFO("Page POP is detect, move Page(%s) to foreground", get_page_prev_name(self));
202 | lv_obj_move_foreground(self->page_current->root);
203 | if (self->page_prev)
204 | lv_obj_move_foreground(self->page_prev->root);
205 | }
206 | }
207 |
208 | /**
209 | * @brief 强制卸载当前页面
210 | *
211 | * @param base 页面管理器
212 | * @return true
213 | * @return false
214 | */
215 | bool fource_unload(page_base_t *base)
216 | {
217 | if (base == NULL)
218 | {
219 | PM_LOG_ERROR("Page is nullptr, Unload failed");
220 | return false;
221 | }
222 |
223 | PM_LOG_INFO("Page(%s) Fource unloading...", base->name);
224 |
225 | if (base->priv.state == PAGE_STATE_ACTIVITY)
226 | {
227 | PM_LOG_INFO("Page state is ACTIVITY, Disappearing...");
228 | base->base->on_view_will_disappear(base);
229 | base->base->on_view_did_disappear(base);
230 | }
231 |
232 | base->priv.state = state_unload_execute(base);
233 |
234 | return true;
235 | }
236 |
237 | /**
238 | * @brief 返回主界面
239 | *
240 | * @param self 页面管理器对象
241 | * @return true 返回成功
242 | * @return false 页面正在切换
243 | */
244 | bool pm_back_home(page_manager_t *self)
245 | {
246 | // 检查是否正在执行切换页面的动画
247 | if (!_switch_anim_state_check(self))
248 | {
249 | return false;
250 | }
251 | set_satck_clear(self, true);
252 | self->page_prev = NULL;
253 | page_base_t *home = get_stack_top(self);
254 | _page_switch(self, home, false, NULL);
255 | return true;
256 | }
257 |
258 | /**
259 | * @brief 检测界面是否在切换
260 | *
261 | * @param self 页面管理器对象
262 | * @return true 页面正在切换
263 | * @return false 页面不在切换
264 | */
265 | static bool _switch_anim_state_check(page_manager_t *self)
266 | {
267 | if (self->anim_state.is_switch_req || self->anim_state.is_busy)
268 | {
269 | PM_LOG_WARN(
270 | "Page switch busy[self->anim_state.IsSwitchReq = %d,"
271 | "self->anim_state.IsBusy = %d],"
272 | "request ignored",
273 | self->anim_state.is_switch_req,
274 | self->anim_state.is_busy);
275 | return false;
276 | }
277 | return true;
278 | }
279 |
280 | /**
281 | * @brief 检测切换动画是否完成
282 | *
283 | * @param self 页面管理器对象
284 | * @return true 动画已完成
285 | * @return false 动画未完成
286 | */
287 | static bool _switch_req_check(page_manager_t *self)
288 | {
289 | bool ret = false;
290 | bool last_node_busy = self->page_prev && self->page_prev->priv.anim.is_busy;
291 |
292 | if (!self->page_current->priv.anim.is_busy && !last_node_busy)
293 | {
294 | PM_LOG_INFO("----Page switch was all finished----");
295 | self->anim_state.is_switch_req = false;
296 | ret = true;
297 | self->page_prev = self->page_current;
298 | }
299 | else
300 | {
301 | if (self->page_current->priv.anim.is_busy)
302 | {
303 | PM_LOG_WARN("Page PageCurrent(%s) is busy", self->page_current->name);
304 | }
305 | else
306 | {
307 | PM_LOG_WARN("Page PagePrev(%s) is busy", get_page_prev_name(self));
308 | }
309 | }
310 | return ret;
311 | }
312 |
313 | /**
314 | * @brief 动画完成后更新页面状态
315 | *
316 | * @param a lvgl动画对象
317 | */
318 | static void _switch_anim_finsh(lv_anim_t *a)
319 | {
320 | page_base_t *base = (page_base_t *)a->user_data;
321 | page_manager_t *manager = base->manager;
322 |
323 | PM_LOG_INFO("Page(%s) Anim finish", base->name);
324 |
325 | page_state_update(manager, base);
326 | base->priv.anim.is_busy = false;
327 | bool is_finished = _switch_req_check(manager);
328 |
329 | if (!manager->anim_state.is_pushing && is_finished)
330 | {
331 | _switch_anim_type_update(manager, manager->page_current);
332 | }
333 | }
334 |
335 | /**
336 | * @brief 创建切换动画
337 | *
338 | * @param self 页面管理器对象
339 | * @param base 页面对象
340 | */
341 | void switch_anim_create(page_manager_t *self, page_base_t *base)
342 | {
343 | page_load_anim_attr_t anim_attr;
344 | // 获取当前页面参数
345 | if (!page_get_current_load_anim_attr(self, &anim_attr))
346 | {
347 | return;
348 | }
349 | PM_LOG_INFO("page anim create");
350 | lv_anim_t a;
351 | anim_default_init(self, &a);
352 |
353 | a.user_data = base;
354 | lv_anim_set_var(&a, base->root);
355 | lv_anim_set_ready_cb(&a, _switch_anim_finsh);
356 | lv_anim_set_exec_cb(&a, anim_attr.setter);
357 |
358 | int32_t start = 0;
359 |
360 | if (anim_attr.getter)
361 | {
362 | start = anim_attr.getter(base->root);
363 | }
364 |
365 | // 根据标志位更新动画
366 | if (self->anim_state.is_pushing)
367 | {
368 | if (base->priv.anim.is_enter)
369 | {
370 | lv_anim_set_values(
371 | &a,
372 | anim_attr.push.enter.start,
373 | anim_attr.push.enter.end);
374 | }
375 | else /* Exit */
376 | {
377 | lv_anim_set_values(
378 | &a,
379 | start,
380 | anim_attr.push.exit.end);
381 | }
382 | }
383 | else /* Pop */
384 | {
385 | if (base->priv.anim.is_enter)
386 | {
387 | lv_anim_set_values(
388 | &a,
389 | anim_attr.pop.enter.start,
390 | anim_attr.pop.enter.end);
391 | }
392 | else /* Exit */
393 | {
394 | lv_anim_set_values(
395 | &a,
396 | start,
397 | anim_attr.pop.exit.end);
398 | }
399 | }
400 |
401 | lv_anim_start(&a);
402 | base->priv.anim.is_busy = true;
403 | }
404 |
405 | /**
406 | * @brief 设置页面管理器全局默认动画参数
407 | *
408 | * @param self 页面管理器对象
409 | * @param anim 动画类型
410 | * @param time 动画持续时间
411 | * @param path 动画路径
412 | */
413 | void pm_set_global_load_anim_type(page_manager_t *self, page_load_anim_t anim, uint16_t time, lv_anim_path_cb_t path)
414 | {
415 | if (anim > _LOAD_ANIM_LAST)
416 | {
417 | anim = LOAD_ANIM_NONE;
418 | }
419 |
420 | self->anim_state.global.type = anim;
421 | self->anim_state.global.time = time;
422 | self->anim_state.global.path = path;
423 |
424 | PM_LOG_INFO("Set global load anim type = %d", anim);
425 | }
426 |
427 | /**
428 | * @brief
429 | *
430 | * @param self
431 | * @param base
432 | */
433 | void _switch_anim_type_update(page_manager_t *self, page_base_t *base)
434 | {
435 | if (base->priv.anim.attr.type == LOAD_ANIM_GLOBAL)
436 | {
437 | PM_LOG_INFO(
438 | "Page(%s) Anim.Type was not set, use self->anim_state.Global.Type = %d",
439 | base->name,
440 | self->anim_state.global.type);
441 | self->anim_state.current = self->anim_state.global;
442 | }
443 | else
444 | {
445 | if (base->priv.anim.attr.type > _LOAD_ANIM_LAST)
446 | {
447 | PM_LOG_ERROR("Page(%s)", base->name);
448 | PM_LOG_ERROR("ERROR custom Anim.Type = %d", base->priv.anim.attr.type);
449 | PM_LOG_ERROR("use self->anim_state.Global.Type = %d", self->anim_state.global.type);
450 | base->priv.anim.attr = self->anim_state.global;
451 | }
452 | else
453 | {
454 | PM_LOG_INFO(
455 | "Page(%s) custom Anim.Type set = %d",
456 | base->name,
457 | base->priv.anim.attr.type);
458 | }
459 | self->anim_state.current = base->priv.anim.attr;
460 | }
461 | }
462 |
463 | /**
464 | * @brief 默认动画初始化
465 | *
466 | * @param self 页面管理器对象
467 | * @param a lvgl动画对象
468 | */
469 | void anim_default_init(page_manager_t *self, lv_anim_t *a)
470 | {
471 | lv_anim_init(a);
472 |
473 | uint32_t time = (page_get_current_load_anim_type(self) == LOAD_ANIM_NONE) ? 0 : self->anim_state.current.time;
474 | lv_anim_set_time(a, time);
475 |
476 | lv_anim_path_t path;
477 | lv_anim_path_init(&path);
478 | lv_anim_path_set_cb(&path, self->anim_state.current.path);
479 | PM_LOG_INFO("(%s) current path is (%p)", self->page_current->name, self->anim_state.current.path);
480 |
481 | lv_anim_set_path(a, &path);
482 | }
--------------------------------------------------------------------------------
/src/page_state.c:
--------------------------------------------------------------------------------
1 | #include "page_manager_private.h"
2 |
3 | static page_state_t _state_load_execute(page_manager_t *self, page_base_t *base);
4 | static page_state_t _state_will_appear_execute(page_manager_t *self, page_base_t *base);
5 | static page_state_t _state_did_appear_execute(page_base_t *base);
6 | static page_state_t _state_will_disappear_execute(page_manager_t *self, page_base_t *base);
7 | static page_state_t _state_did_disappear_execute(page_manager_t *self, page_base_t *base);
8 | static bool _get_is_over_anim(uint8_t anim);
9 |
10 | /**
11 | * @brief 页面更新
12 | *
13 | * @param self 页面管理器对象
14 | * @param base 页面对象
15 | */
16 | void page_state_update(page_manager_t *self, page_base_t *base)
17 | {
18 | if (base == NULL)
19 | return;
20 |
21 | switch (base->priv.state)
22 | {
23 | // 页面被卸载后进入空闲状态
24 | case PAGE_STATE_IDLE:
25 | PM_LOG_INFO("Page(%s) state idle", base->name);
26 | break;
27 | // 页面没有被缓存时,第一次加载进入这里
28 | // 该状态下执行on_view_load函数,创建root对象
29 | // 立即切换到PAGE_STATE_WILL_APPEAR
30 | case PAGE_STATE_LOAD:
31 | base->priv.state = _state_load_execute(self, base);
32 | page_state_update(self, base);
33 | break;
34 |
35 | // 加载状态过后或者页面有被缓存会进入到这里
36 | // 该状态下在动画执行之前会执行on_view_will_appear函数并且初始化切换动画
37 | // 动画结束后切换到PAGE_STATE_DID_APPEAR
38 | case PAGE_STATE_WILL_APPEAR:
39 | base->priv.state = _state_will_appear_execute(self, base);
40 | break;
41 |
42 | // 由页面切换动画结束后进入这里
43 | // 该状态下会执行on_view_did_appear
44 | // 该状态执行完毕后会长期停留在PAGE_STATE_ACTIVITY状态
45 | case PAGE_STATE_DID_APPEAR:
46 | base->priv.state = _state_did_appear_execute(base);
47 | PM_LOG_INFO("Page(%s) state active", base->name);
48 | break;
49 |
50 | // 页面会在切换的时候,被切换的页面会进入这里
51 | // 该状态会立即转到PAGE_STATE_WILL_DISAPPEAR
52 | case PAGE_STATE_ACTIVITY:
53 | PM_LOG_INFO("Page(%s) state active break", base->name);
54 | base->priv.state = PAGE_STATE_WILL_DISAPPEAR;
55 | page_state_update(self, base);
56 | break;
57 |
58 | // 被切换的页面会进入到这里
59 | // 该状态会在动画开始前执行on_view_will_disappear,并且加载关机动画
60 | // 动画结束后进入PAGE_STATE_DID_DISAPPEAR
61 | case PAGE_STATE_WILL_DISAPPEAR:
62 | base->priv.state = _state_will_disappear_execute(self, base);
63 | break;
64 |
65 | // 结束动画播放完毕后进入这里
66 | // 该状态会执行on_view_did_disappear
67 | // 如果开启缓存,状态会转换成PAGE_STATE_WILL_APPEAR,如果没开启缓存则会进入PAGE_STATE_UNLOAD
68 | case PAGE_STATE_DID_DISAPPEAR:
69 | base->priv.state = _state_did_disappear_execute(self, base);
70 | if (base->priv.state == PAGE_STATE_UNLOAD)
71 | {
72 | page_state_update(self, base);
73 | }
74 | break;
75 |
76 | // 注销页面或者是关闭页面缓存的时候会进入该状态
77 | // 该状态下会回收相关的页面对象,并且执行on_view_did_unload
78 | // 该状态结束后进入PAGE_STATE_IDLE
79 | case PAGE_STATE_UNLOAD:
80 | base->priv.state = state_unload_execute(base);
81 | break;
82 |
83 | default:
84 | PM_LOG_ERROR("Page(%s) state[%d] was NOT FOUND!", base->name, base->priv.state);
85 | break;
86 | }
87 | }
88 |
89 | /**
90 | * @brief 将lvgl根对象初始化,并且调用on_view_load()
91 | *
92 | * @param self 页面管理器对象
93 | * @param base 页面对象
94 | * @return page_state_t 下一个页面状态
95 | */
96 | static page_state_t _state_load_execute(page_manager_t *self, page_base_t *base)
97 | {
98 | PM_LOG_INFO("Page(%s) state load", base->name);
99 |
100 | if (base->root != NULL)
101 | {
102 | PM_LOG_ERROR("Page(%s) root must be NULL", base->name);
103 | }
104 |
105 | // 创建根对象
106 | lv_obj_t *root_obj = lv_obj_create(lv_scr_act(), NULL);
107 | lv_obj_set_size(root_obj, LV_HOR_RES, LV_VER_RES);
108 | root_obj->user_data = base;
109 | base->root = root_obj;
110 | base->base->on_view_load(base);
111 |
112 | if (base->root_event_cb != NULL)
113 | {
114 | lv_obj_set_event_cb(root_obj, base->root_event_cb);
115 | }
116 |
117 | if (_get_is_over_anim(page_get_current_load_anim_type(self)))
118 | {
119 | page_base_t *bottom_page = get_stack_top_after(self);
120 |
121 | if (bottom_page != NULL && bottom_page->priv.is_cached)
122 | {
123 | page_load_anim_attr_t anim_attr;
124 | if (page_get_current_load_anim_attr(self, &anim_attr))
125 | {
126 | if (anim_attr.drag_dir != ROOT_DRAG_DIR_NONE)
127 | {
128 | root_enable_drag(base->root);
129 | }
130 | }
131 | }
132 | }
133 |
134 | base->base->on_view_did_load(base);
135 |
136 | if (base->priv.is_disable_auto_cache)
137 | {
138 | PM_LOG_INFO("Page(%s) disable auto cache, ReqEnableCache = %d", base->name, base->priv.req_enable_cache);
139 | base->priv.is_cached = base->priv.req_enable_cache;
140 | }
141 | else
142 | {
143 | PM_LOG_INFO("Page(%s) AUTO cached", base->name);
144 | base->priv.is_cached = true;
145 | }
146 |
147 | return PAGE_STATE_WILL_APPEAR;
148 | }
149 |
150 | static page_state_t _state_will_appear_execute(page_manager_t *self, page_base_t *base)
151 | {
152 | PM_LOG_INFO("Page(%s) state will appear", base->name);
153 | base->base->on_view_will_appear(base);
154 | switch_anim_create(self, base);
155 | return PAGE_STATE_DID_APPEAR;
156 | }
157 |
158 | static page_state_t _state_did_appear_execute(page_base_t *base)
159 | {
160 | PM_LOG_INFO("Page(%s) state did appear", base->name);
161 | base->base->on_view_did_appear(base);
162 | return PAGE_STATE_ACTIVITY;
163 | }
164 |
165 | static page_state_t _state_will_disappear_execute(page_manager_t *self, page_base_t *base)
166 | {
167 | PM_LOG_INFO("Page(%s) state will disappear", base->name);
168 | base->base->on_view_will_disappear(base);
169 | switch_anim_create(self, base);
170 | return PAGE_STATE_DID_DISAPPEAR;
171 | }
172 |
173 | static page_state_t _state_did_disappear_execute(page_manager_t *self, page_base_t *base)
174 | {
175 | PM_LOG_INFO("Page(%s) state did disappear", base->name);
176 | if (page_get_current_load_anim_type(self) == LOAD_ANIM_FADE_ON)
177 | {
178 | PM_LOG_INFO("AnimState.TypeCurrent == LOAD_ANIM_FADE_ON, Page(%s) hidden", base->name);
179 | }
180 | base->base->on_view_did_disappear(base);
181 | if (base->priv.is_cached)
182 | {
183 | PM_LOG_INFO("Page(%s) has cached", base->name);
184 | return PAGE_STATE_WILL_APPEAR;
185 | }
186 | else
187 | {
188 | return PAGE_STATE_UNLOAD;
189 | }
190 | }
191 |
192 | page_state_t state_unload_execute(page_base_t* base)
193 | {
194 | PM_LOG_INFO("Page(%s) state unload", base->name);
195 | if (base->root == NULL)
196 | {
197 | PM_LOG_WARN("Page is loaded!");
198 | goto Exit;
199 | }
200 |
201 | if (base->priv.stash.ptr != NULL && base->priv.stash.size != 0)
202 | {
203 | PM_LOG_INFO("Page(%s) free stash(0x%p)[%d]", base->name, base->priv.stash.ptr, base->priv.stash.size);
204 | PM_FREE(base->priv.stash.ptr);
205 | base->priv.stash.ptr = NULL;
206 | base->priv.stash.size = 0;
207 | }
208 | lv_obj_del_async(base->root);
209 | base->root = NULL;
210 | base->priv.is_cached = false;
211 | base->base->on_view_did_unload(base);
212 |
213 | Exit:
214 | return PAGE_STATE_IDLE;
215 | }
216 |
217 | static bool _get_is_over_anim(uint8_t anim)
218 | {
219 | return (anim >= LOAD_ANIM_OVER_LEFT && anim <= LOAD_ANIM_OVER_BOTTOM);
220 | }
--------------------------------------------------------------------------------