├── .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 | } --------------------------------------------------------------------------------